code

메타클래스의 (구체적인) 사용 사례는 무엇입니까?

starcafe 2023. 6. 12. 21:37
반응형

메타클래스의 (구체적인) 사용 사례는 무엇입니까?

메타클래스를 즐겨 사용하는 친구가 있는데, 정기적으로 메타클래스를 해결책으로 제공합니다.

저는 당신이 메타클래스를 사용할 필요가 거의 없다고 생각합니다.왜죠? 왜냐하면 저는 여러분이 수업에 그런 것을 하고 있다면, 여러분은 아마도 어떤 사물에 그렇게 해야 한다고 생각하기 때문입니다.그리고 작은 재설계/리팩터가 준비되어 있습니다.

메타 수업을 사용할 수 있다는 것은 많은 곳에서 많은 사람들이 수업을 일종의 2류 목적어로 사용하게 만들었습니다. 이것은 제게 재앙적인 것처럼 보입니다.프로그래밍이 메타 프로그래밍으로 대체됩니까?교실 장식가들의 추가는 불행하게도 그것을 훨씬 더 수용할 수 있게 만들었습니다.

그러니 제발, 저는 파이썬에서 메타클래스에 대한 당신의 유효한 (구체적인) 사용 사례를 알고 싶습니다.혹은 왜 계급을 변형시키는 것이 물체를 변형시키는 것보다 더 나은지에 대해 깨닫는 것.

시작하겠습니다.

타사 라이브러리를 사용할 때 클래스를 특정 방식으로 변형할 수 있으면 유용할 수 있습니다.

(이것이 제가 생각할 수 있는 유일한 경우이고, 구체적이지 않습니다)

저는 최근에 같은 질문을 받았고, 몇 가지 답을 생각해냈습니다.언급된 몇 가지 사용 사례를 자세히 설명하고 몇 가지 새로운 사용 사례를 추가하고 싶었기 때문에 이 스레드를 다시 사용해도 괜찮길 바랍니다.

제가 본 대부분의 메타클래스는 다음 두 가지 중 하나를 수행합니다.

  1. 등록(데이터 구조에 클래스 추가):

    models = {}
    
    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            models[name] = cls = type.__new__(meta, name, bases, attrs)
            return cls
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    이 서브클래스를 할 마다 ㅠㅠㅠㅠㅠㅠㅠModel은 당의수에있습다니어에 되어 있습니다.models사전:

    >>> class A(Model):
    ...     pass
    ...
    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...>,
     'B': <__main__.B class at 0x...>}
    

    클래스 장식자를 사용하여 다음 작업을 수행할 수도 있습니다.

    models = {}
    
    def model(cls):
        models[cls.__name__] = cls
        return cls
    
    @model
    class A(object):
        pass
    

    또는 명시적 등록 기능이 있는 경우:

    models = {}
    
    def register_model(cls):
        models[cls.__name__] = cls
    
    class A(object):
        pass
    
    register_model(A)
    

    사실, 이것은 거의 비슷합니다. 여러분은 교실 장식가들을 불리하게 언급하지만, 그것은 실제로 교실에서 기능 호출을 위한 통사적 설탕에 지나지 않습니다. 그래서 마법은 없습니다.

    어쨌든, 이 경우 메타클래스의 장점은 모든 하위 클래스에 대해 작동하기 때문에 상속인 반면, 다른 솔루션은 명시적으로 장식되거나 등록된 하위 클래스에만 작동합니다.

    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...> # No B :(
    
  2. 리팩터링(클래스 속성 수정 또는 새 속성 추가):

    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            fields = {}
            for key, value in attrs.items():
                if isinstance(value, Field):
                    value.name = '%s.%s' % (name, key)
                    fields[key] = value
            for base in bases:
                if hasattr(base, '_fields'):
                    fields.update(base._fields)
            attrs['_fields'] = fields
            return type.__new__(meta, name, bases, attrs)
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    이 서브클래스를 할 마다 ㅠㅠㅠㅠㅠㅠㅠModel그리고 일부를 정의합니다.Field(더 을 위해)되고 (" 들 로 으 주 고 되 입 다 음 과 같 그 이 화 룹 됩 니 다 름 어 경 보 우 의 지 다 오 메_fields사전(모든 클래스 속성과 모든 기본 클래스의 속성을 매번 살펴볼 필요 없이 쉬운 반복을 위해):

    >>> class A(Model):
    ...     foo = Integer()
    ...
    >>> class B(A):
    ...     bar = String()
    ...
    >>> B._fields
    {'foo': Integer('A.foo'), 'bar': String('B.bar')}
    

    클래스 장식자를 사용하여 상속 없이 이 작업을 수행할 수 있습니다.

    def model(cls):
        fields = {}
        for key, value in vars(cls).items():
            if isinstance(value, Field):
                value.name = '%s.%s' % (cls.__name__, key)
                fields[key] = value
        for base in cls.__bases__:
            if hasattr(base, '_fields'):
                fields.update(base._fields)
        cls._fields = fields
        return cls
    
    @model
    class A(object):
        foo = Integer()
    
    class B(A):
        bar = String()
    
    # B.bar has no name :(
    # B._fields is {'foo': Integer('A.foo')} :(
    

    또는 명시적으로:

    class A(object):
        foo = Integer('A.foo')
        _fields = {'foo': foo} # Don't forget all the base classes' fields, too!
    

    읽기 쉽고 유지 관리 가능한 비메타 프로그래밍에 대한 귀하의 옹호와는 반대로, 이는 훨씬 더 번거롭고 중복되며 오류가 발생하기 쉽습니다.

    class B(A):
        bar = String()
    
    # vs.
    
    class B(A):
        bar = String('bar')
        _fields = {'B.bar': bar, 'A.foo': A.foo}
    

가장 일반적이고 구체적인 사용 사례를 고려했을 때 메타 클래스를 사용해야 하는 유일한 경우는 클래스 이름이나 기본 클래스 목록을 수정하려는 경우입니다. 일단 정의되면 이러한 매개 변수가 클래스에 구워지고 장식자나 함수가 이 매개 변수를 구울 수 없기 때문입니다.

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        return type.__new__(meta, 'foo', (int,), attrs)

class Baseclass(object):
    __metaclass__ = Metaclass

class A(Baseclass):
    pass

class B(A):
    pass

print A.__name__ # foo
print B.__name__ # foo
print issubclass(B, A)   # False
print issubclass(B, int) # True

이는 유사한 이름을 가진 클래스나 불완전한 상속 트리가 정의될 때마다 경고를 보내는 프레임워크에서 유용할 수 있지만, 이러한 값을 실제로 변경해야 하는 이유 외에는 생각할 수 없습니다.아마도 데이비드 비즐리는 할 수 있을 것입니다.

, 파썬서 3는메, 이▁the▁have도 있습니다.__prepare__메소드는을 다매른본핑평수방이 할 수 .dict따라서 정렬된 속성, 오버로드된 속성 및 기타 사악한 쿨한 것을 지원합니다.

import collections

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return collections.OrderedDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(list(attrs))
        # Do more stuff...

class A(metaclass=Metaclass):
    x = 1
    y = 2

# prints ['x', 'y'] rather than ['y', 'x']

 

class ListDict(dict):
    def __setitem__(self, key, value):
        self.setdefault(key, []).append(value)

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return ListDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(attrs['foo'])
        # Do more stuff...

class A(metaclass=Metaclass):

    def foo(self):
        pass

    def foo(self, x):
        pass

# prints [<function foo at 0x...>, <function foo at 0x...>] rather than <function foo at 0x...>

순서가 지정된 특성은 생성 카운터를 사용하여 달성할 수 있으며 오버로드는 기본 인수를 사용하여 시뮬레이션할 수 있습니다.

import itertools

class Attribute(object):
    _counter = itertools.count()
    def __init__(self):
        self._count = Attribute._counter.next()

class A(object):
    x = Attribute()
    y = Attribute()

A._order = sorted([(k, v) for k, v in vars(A).items() if isinstance(v, Attribute)],
                  key = lambda (k, v): v._count)

 

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=None):
        if x is None:
            return self._foo0()
        else:
            return self._foo1(x)

훨씬 더 못생겼을 뿐만 아니라 덜 유연합니다. 정수나 문자열과 같은 정렬된 문자 속성을 원한다면 어떨까요?면 어쩌지None는 의유값 다니입에 대한 유효한 입니다.x?

첫 번째 문제를 해결하기 위한 창의적인 방법은 다음과 같습니다.

import sys

class Builder(object):
    def __call__(self, cls):
        cls._order = self.frame.f_code.co_names
        return cls

def ordered():
    builder = Builder()
    def trace(frame, event, arg):
        builder.frame = frame
        sys.settrace(None)
    sys.settrace(trace)
    return builder

@ordered()
class A(object):
    x = 1
    y = 'foo'

print A._order # ['x', 'y']

두 번째 문제를 해결하기 위한 창의적인 방법은 다음과 같습니다.

_undefined = object()

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=_undefined):
        if x is _undefined:
            return self._foo0()
        else:
            return self._foo1(x)

하지만 이것은 단순한 메타클래스(특히 첫 번째 클래스, 정말로 뇌를 녹이는 것)보다 훨씬 더 부두교적입니다.제 요점은, 여러분은 메타클래스를 낯설고 반직관적인 것으로 생각하지만, 프로그래밍 언어에서 진화의 다음 단계로 볼 수도 있다는 것입니다. 여러분은 단지 여러분의 사고방식을 조정해야만 합니다.결국, 당신은 아마도 C에서 함수 포인터로 구조를 정의하고 그것을 함수의 첫 번째 인수로 전달하는 것을 포함하여 모든 것을 할 수 있을 것입니다.C++를 처음 보는 사람은 "이게 무슨 마법이야?"라고 말할 수 있습니다.컴파일러가 암묵적으로 전달되는 이유는 무엇입니까?this규칙적이고 정적인 기능이 아닌 방법으로? 대해노 장골말것좋다습니이하는황고게하이신적장에▁then▁isming다▁object,." 하지만 객체 지향 프로그래밍은 일단 훨씬 더 합니다. 하지만 객체 지향 프로그래밍은 일단 그것을 얻으면 훨씬 더 강력합니다. 리고이것마도찬니다입지가그. 준측면 아마내 생각에 준 측면 지향 프로그래밍.일단 메타클래스를 이해하게 되면, 그것들은 실제로 매우 간단하기 때문에, 편리할 때 그것들을 사용하는 것은 어떨까요?

그리고 마지막으로, 메타 수업은 rad이고, 프로그래밍은 재미있어야 합니다.표준 프로그래밍 구조와 디자인 패턴을 항상 사용하는 것은 지루하고 영감을 주지 않으며 상상력을 방해합니다.조금만 살아요!메타메타 수업이 있어요, 당신을 위해서요.

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls 
        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

class China(type):
    __metaclass__ = MetaMetaclass

class Taiwan(type):
    __metaclass__ = MetaMetaclass

class A(object):
    __metaclass__ = China

class B(object):
    __metaclass__ = Taiwan

print A._label # Made in China
print B._label # Made in Taiwan

편집

이것은 꽤 오래된 질문이지만, 여전히 투표율이 상승하고 있기 때문에 좀 더 포괄적인 답변에 대한 링크를 추가하려고 생각했습니다.메타클래스와 메타클래스의 용도에 대해 자세히 알고 싶다면, 방금 여기에 메타클래스에 대한 기사를 실었습니다.

메타클래스의 목적은 클래스/객체 구분을 메타클래스/클래스로 대체하는 것이 아니라 클래스 정의(및 인스턴스)의 동작을 어떤 식으로든 변경하는 것입니다.효과적으로 클래스 문의 동작을 기본값보다 특정 도메인에 더 유용한 방식으로 변경하는 것입니다.제가 사용한 용도는 다음과 같습니다.

  • 일반적으로 핸들러를 등록하는 하위 클래스를 추적합니다.이것은 플러그인 스타일 설정을 사용할 때 유용하며, 몇 가지 클래스 속성을 하위 분류하고 설정하여 특정 항목에 대한 핸들러를 등록할 때만 유용합니다.예를 들어, 각 클래스가 해당 유형에 적합한 방법(재생/태그 가져오기 등)을 구현하는 다양한 음악 형식에 대한 핸들러를 작성한다고 가정합니다.새 유형의 핸들러를 추가하면 다음과 같이 됩니다.

    class Mp3File(MusicFile):
        extensions = ['.mp3']  # Register this type as a handler for mp3 files
        ...
        # Implementation of mp3 methods go here
    

    그런 다음 메타 클래스는 다음 사전을 유지합니다.{'.mp3' : MP3File, ... }등, 공장 기능을 통해 핸들러를 요청할 때 적절한 유형의 객체를 구성합니다.

  • 변화하는 행동.특정 속성에 특별한 의미를 부여하여 속성이 있을 때 변경된 동작을 수행할 수 있습니다.를 들어,이 " 예들어가, 방찾을수있다습니법을"인 방법을 찾을 수 있습니다._get_foo그리고._set_foo투명하게 속성으로 변환합니다.실제 사례로, 여기 제가 쓴 요리법이 있습니다. C와 같은 구조 정의를 더 많이 제공하기 위해서요.메타 클래스는 선언된 항목을 구조 형식 문자열로 변환하고 상속을 처리하며 이를 처리할 수 있는 클래스를 생성하는 데 사용됩니다.

    다른 실제 예제에서는 sqalchemy의 ORM 또는 sqlobject와 같은 다양한 ORM을 살펴봅니다.여기서도 SQL 열 정의를 특정 의미로 해석하는 것이 목적입니다.

저는 Matplotlib의 프런트 엔드로 비인터랙티브 플롯팅을 처리하는 클래스가 있습니다.그러나 때로는 대화형 그림을 그리고 싶을 때가 있습니다.몇 가지 기능만 사용하면 수치 카운트를 늘리거나 수동으로 통화 그리기 등을 수행할 수 있었지만 모든 플로팅 호출 전후에 이러한 작업을 수행해야 했습니다.따라서 대화형 플로팅 래퍼와 화면 밖의 플로팅 래퍼를 모두 만들기 위해 메타 클래스를 통해 적절한 방법으로 래핑하는 것이 다음과 같은 작업을 수행하는 것보다 더 효율적이라는 것을 알게 되었습니다.

class PlottingInteractive:
    add_slice = wrap_pylab_newplot(add_slice)

메는드등 API 따않의속클반성복다니합을래스고라이지가의 입니다.__init__클래스 속성을 다시 설정하기 전에 보다 효율적이고 최신 상태를 유지할 수 있습니다.

class _Interactify(type):
    def __init__(cls, name, bases, d):
        super(_Interactify, cls).__init__(name, bases, d)
        for base in bases:
            for attrname in dir(base):
                if attrname in d: continue # If overridden, don't reset
                attr = getattr(cls, attrname)
                if type(attr) == types.MethodType:
                    if attrname.startswith("add_"):
                        setattr(cls, attrname, wrap_pylab_newplot(attr))
                    elif attrname.startswith("set_"):
                        setattr(cls, attrname, wrap_pylab_show(attr))

물론, 더 나은 방법이 있을 수도 있지만, 저는 이것이 효과적이라는 것을 알았습니다.물론, 이것은 또한 할 수 있습니다.__new__또는__init__하지만 이것이 제가 가장 직접적으로 찾은 해결책이었습니다.

팀 피터의 고전적인 인용구로 시작해 보겠습니다.

메타클래스는 99%의 사용자가 걱정해야 할 것보다 더 깊은 마법입니다.만약 여러분이 그것들이 필요한지 궁금하다면, 여러분은 그렇지 않습니다. (그들이 실제로 필요로 하는 사람들은 그들이 그것들을 필요로 한다는 것을 확실히 알고 있고, 왜 그런지에 대한 설명은 필요하지 않습니다.)팀 피터스 (c.l.p post 2002-12-22)

그렇긴 하지만, 저는 (주기적으로) 메타 클래스의 진정한 사용법을 우연히 발견했습니다.생각나는 것은 당신의 모든 모델이 모델로부터 물려받는 장고에 있습니다.모델. 모델.결국 모델은 장고의 ORM 장점으로 DB 모델을 포장하는 몇 가지 심각한 마술을 합니다.그 마법은 메타 수업을 통해 일어납니다.모든 형식의 예외 클래스, 관리자 클래스 등을 만듭니다.

이야기의 시작 부분은 django/db/db/db/base.py , 클래스 ModelBase()를 참조하십시오.

메타 클래스 사용의 합리적인 패턴은 동일한 클래스가 인스턴스화될 때마다 반복적으로 수행되는 것이 아니라 클래스가 정의될 때 한 번 수행되는 것입니다.

가 같은 행동을 때, 여클래동특동수작공반경복우유는하을을 반복합니다.__metaclass__=X특수 목적 코드를 반복하거나 애드혹 공유 슈퍼 클래스를 도입하는 것보다 훨씬 낫습니다.

할 수 있는 확장이 , 만특별수하있나예없연도더라있장수이는하할상지한고업이만,▁and,▁but없도더라.__new__그리고.__init__는 특수 와 normal을 혼합하는 것보다 글로벌 하는 더 입니다.def그리고.class클래스 정의 본문의 문입니다.

메타 클래스는 Python에서 도메인 특정 언어를 구성하는 데 유용할 수 있습니다.구체적인 예로는 SQLObject의 데이터베이스 스키마 선언 구문인 Django가 있습니다.

이안 비킹의 보수적인 메타 수업의 기본 예:

제가 사용한 메타 클래스는 주로 일종의 선언적인 프로그래밍 스타일을 지원하는 것이었습니다.예를 들어, 유효성 검사 스키마를 생각해 보십시오.

class Registration(schema.Schema):
    first_name = validators.String(notEmpty=True)
    last_name = validators.String(notEmpty=True)
    mi = validators.MaxLength(1)
    class Numbers(foreach.ForEach):
        class Number(schema.Schema):
            type = validators.OneOf(['home', 'work'])
            phone_number = validators.PhoneNumber()

기타 기술:Python에서 DSL을 구축하기 위한 구성 요소(pdf).

편집(알리 기준):컬렉션과 인스턴스를 사용하여 이 작업을 수행하는 예는 제가 선호하는 것입니다.중요한 사실은 당신에게 더 많은 힘을 주고 메타클래스를 사용할 이유를 없애는 인스턴스입니다.또한 이 예제는 클래스와 인스턴스를 혼합하여 사용한다는 점에 주목할 필요가 있습니다. 이는 메타 클래스로 모든 작업을 수행할 수 없다는 것을 나타냅니다.그리고 정말로 불균일한 방법을 만들어냅니다.

number_validator = [
    v.OneOf('type', ['home', 'work']),
    v.PhoneNumber('phone_number'),
]

validators = [
    v.String('first_name', notEmpty=True),
    v.String('last_name', notEmpty=True),
    v.MaxLength('mi', 1),
    v.ForEach([number_validator,])
]

완벽하지는 않지만, 이미 마법은 거의 없고, 메타 클래스가 필요 없고, 균일성이 향상되었습니다.

저는 어제도 같은 생각을 하고 있었는데 완전히 동의합니다.제 생각에 코드베이스를 더 선언적으로 만들기 위한 시도로 인해 발생하는 코드의 복잡성은 일반적으로 코드베이스를 유지하기 더 어렵고 읽기 더 어렵고 덜 비단결적으로 만듭니다.또한 일반적으로 copy.copy()ing(상속을 유지하고 클래스에서 인스턴스로 복사하기 위해)이 많이 필요하며, python grain에 반하는 상황(항상 메타 클래스에서 위쪽으로 보기)을 보려면 여러 곳을 살펴봐야 한다는 것을 의미합니다.저는 그러한 선언적 스타일이 가치가 있는지 없는지와 분명히 없는지를 알아보기 위해 멘코드와 SQLalchemy 코드를 선택해 왔습니다.이러한 스타일은 설명자(예: 속성 및 메서드)와 불변 데이터에 맡겨야 합니다.루비는 그러한 선언적 스타일을 더 잘 지원하고 핵심 파이썬 언어가 그 길을 가지 않아서 기쁩니다.

디버깅에 사용되는 것을 볼 수 있습니다. 더 풍부한 정보를 얻기 위해 모든 기본 클래스에 메타 클래스를 추가합니다.저는 또한 그것들이 (매우) 큰 프로젝트에서만 일부 상용어구 코드를 제거하기 위해 사용된다고 봅니다(그러나 명확성을 잃음).를 들어 sqlalchemy는 클래스 정의의 속성 값을 기반으로 모든 하위 클래스에 특정 사용자 지정 메서드를 추가하기 위해 다른 곳에서 사용합니다.

class test(baseclass_with_metaclass):
    method_maker_value = "hello"

는 "hello"(문자열 끝에 "hello"를 추가한 메서드)를 기반으로 하는 특수 속성을 가진 해당 클래스의 메서드를 생성하는 메타 클래스를 가질 수 있습니다.method_maker_value만 정의하면 되는 대신 모든 하위 클래스에서 메서드를 작성할 필요가 없도록 하는 것이 유지관리성에 도움이 될 수 있습니다.

그러나 이것에 대한 필요성은 매우 드물고 약간의 타이핑만 줄일 뿐이므로 충분히 큰 코드베이스가 없는 한 고려할 가치가 없습니다.

메타 클래스의 유일한 합법적인 사용 사례는 다른 nosy 개발자가 사용자의 코드를 건드리지 못하도록 하는 것입니다.귀찮은 개발자가 메타클래스를 마스터하고 당신의 클래스를 탐색하기 시작하면 다른 레벨이나 두 레벨을 추가하여 해당 클래스를 차단합니다.그래도 작동하지 않으면 사용하기 시작합니다.type.__new__또는 재귀 메타 클래스를 사용하는 일부 체계.

(볼에 혀를 썼지만, 저는 이런 종류의 난독화가 이루어진 것을 보았습니다.장고는 완벽한 예입니다)

Python에서 메타클래스를 사용한 것은 Flickr API를 위한 래퍼를 작성했을 때뿐입니다.

제 목표는 flickr의 api 사이트를 스크랩하고 Python 객체를 사용하여 API에 액세스할 수 있도록 전체 클래스 계층을 동적으로 생성하는 것이었습니다.

# Both the photo type and the flickr.photos.search API method 
# are generated at "run-time"
for photo in flickr.photos.search(text=balloons):
    print photo.description

이 예에서는 웹사이트에서 전체 Python Flickr API를 생성했기 때문에 런타임에 클래스 정의를 잘 알지 못합니다.동적으로 유형을 생성할 수 있다는 것은 매우 유용했습니다.

메타 수업이 프로그래밍을 대체하지 않습니다!그것들은 단지 몇 가지 작업을 자동화하거나 더 우아하게 만들 수 있는 속임수일 뿐입니다.이에 대한 좋은 예는 Pygments 구문 강조 라이브러리입니다.라는 수업이 있습니다.RegexLexer사용자가 렉싱 규칙 집합을 클래스의 정규식으로 정의할 수 있습니다.메타 클래스는 정의를 유용한 파서로 변환하는 데 사용됩니다.

그것들은 소금과 같습니다. 너무 많이 사용하기 쉽습니다.

일부 GUI 라이브러리는 여러 스레드가 상호 작용하려고 할 때 문제가 발생합니다. tkinter이벤트 및 대기열의 문제를 명시적으로 처리할 수는 있지만 문제를 완전히 무시하는 방식으로 라이브러리를 사용하는 것이 훨씬 더 간단할 수 있습니다.보세요. 메타클래스의 마법입니다.

전체 라이브러리를 멀티스레드 애플리케이션에서 예상한 대로 올바르게 작동하도록 원활하게 동적으로 다시 작성할 수 있는 것은 상황에 따라 매우 유용할 수 있습니다.Safetkinter 모듈은 스레드 상자 모듈에서 제공하는 메타 클래스의 도움을 받아 이벤트 및 대기열이 필요하지 않습니다.

▁aspect의 한 면.threadbox어떤 클래스를 복제하든 상관하지 않는다는 것입니다.필요한 경우 메타 클래스에서 모든 기본 클래스를 터치할 수 있는 방법의 예를 제공합니다.메타 클래스와 함께 제공되는 또 다른 이점은 상속 클래스에서도 실행된다는 것입니다.직접 작성하는 프로그램은 왜 안 될까요?

메타 클래스는 수정할 클래스의 상속 또는 집계를 사용하여 원하는 작업을 수행하는 클래스를 항상 구성할 수 있으므로 절대로 사용할 필요가 없습니다.

그렇긴 하지만, 기존 클래스를 수정할 수 있는 것은 Smalltalk 및 Ruby에서 매우 유용할 수 있지만, Python은 직접적으로 수정하는 것을 좋아하지 않습니다.

Python의 메타클래스에 대한 훌륭한 DeveloperWorks 기사가 있습니다.위키백과 기사도 꽤 좋습니다.

메타클래스를 사용한 방법은 클래스에 몇 가지 속성을 제공하는 것이었습니다.예를 들어,

class NameClass(type):
    def __init__(cls, *args, **kwargs):
       type.__init__(cls, *args, **kwargs)
       cls.name = cls.__name__

메타클래스가 NameClass를 가리키도록 설정되는 모든 클래스에 이름 특성을 배치합니다.

이건 사소한 용도지만...메타클래스가 유용하다는 것을 알게 된 한 가지는 서브클래스가 만들어질 때마다 함수를 호출하는 것입니다.저는 이것을 메타 클래스로 코드화하여 다음을 찾습니다.__initsubclass__하는 모든 가 " 속성: 하위클가만들때질해메정마다호모니출됩는의상사여다든클음을가하용스래위하"로 됩니다.__initsubclass__(cls, subcls)이를 통해 모든 하위 클래스를 일부 글로벌 레지스트리에 등록하고, 하위 클래스가 정의될 때마다 하위 클래스에 대해 불변 검사를 실행하고, 지연 바인딩 작업을 수행하는 등의 상위 클래스를 만들 수 있습니다.수동으로 함수를 호출하거나 이러한 개별 작업을 수행하는 사용자 지정 메타클래스를 생성할 필요가 없습니다.

명심하세요, 저는 서서히 이 행동의 암묵적인 마법성이 다소 바람직하지 않다는 것을 깨닫게 되었습니다. 왜냐하면 클래스 정의를 맥락에서 벗어나 본다면 예상치 못한 일이기 때문입니다.그래서 나는 그 솔루션을 초기화하는 것 외에 심각한 것에 사용하는 것에서 벗어났습니다.__super각 클래스 및 인스턴스에 대한 특성입니다.

최근 http://census.ire.org/data/bulkdata.html 의 미국 인구 조사 데이터로 채워진 데이터베이스 테이블을 중심으로 SQL 화학 모델을 선언적으로 정의하는 데 도움이 되는 메타 클래스를 사용해야 했습니다.

IRE는 인구 조사 데이터 테이블에 대한 데이터베이스 셸을 제공합니다. 이 테이블은 p012015, p012016, p012017 등의 인구 조사국 명명 규칙에 따라 정수 열을 생성합니다.

는 a) a) 를하여 이 할 수 .model_instance.p012017할 필요가 없기 구문, b) 가내하고이적 c) 하위 분류했습니다.DeclarativeMeta.

from sqlalchemy.ext.declarative.api import DeclarativeMeta

class CensusTableMeta(DeclarativeMeta):
    def __init__(cls, classname, bases, dict_):
        table = 'p012'
        for i in range(1, 49):
            fname = "%s%03d" % (table, i)
            dict_[fname] = Column(Integer)
            setattr(cls, fname, dict_[fname])

        super(CensusTableMeta, cls).__init__(classname, bases, dict_)

그런 다음 이 메타클래스를 모델 정의에 사용하고 모델의 자동 열거 필드에 액세스할 수 있습니다.

CensusTableBase = declarative_base(metaclass=CensusTableMeta)

class P12Tract(CensusTableBase):
    __tablename__ = 'ire_p12'

    geoid = Column(String(12), primary_key=True)

    @property
    def male_under_5(self):
        return self.p012003

    ...

여기에 설명된 합법적인 용도가 있는 것 같습니다. 즉, 메타 클래스로 Python Docstrings 다시 쓰기입니다.

Pydantic은 런타임에 유형 힌트를 적용하고 데이터가 잘못되었을 때 사용자에게 친숙한 오류를 제공하는 데이터 유효성 검사 및 설정 관리용 라이브러리입니다.기본 모델 및 번호 범위 검증에 메타 클래스를 사용합니다.

작업 중에 클래스에 의해 정의된 여러 단계가 있는 프로세스가 있는 코드를 발견했습니다.이러한 단계의 순서는 클래스가 정의될 때 목록에 단계를 추가하는 메타 클래스에 의해 제어되었습니다.이것은 버려졌고 그것들을 목록에 추가하여 순서를 정했습니다.

저는 사용하기 쉽게 하기 위해 바이너리 파서를 위해 그것들을 한 번 사용해야 했습니다.와이어에 있는 필드의 속성으로 메시지 클래스를 정의합니다.그들은 그것으로부터 최종 와이어 형식을 구성하기 위해 선언된 방식으로 주문되어야 했습니다.정렬된 네임스페이스 딕트를 사용하는 경우 메타클래스를 사용하여 이 작업을 수행할 수 있습니다.실제로 메타클래스의 예는 다음과 같습니다.

https://docs.python.org/3/reference/datamodel.html#metaclass-example

하지만 일반적으로:메타클래스의 복잡성이 정말로 필요하다면 매우 신중하게 평가하십시오.

@Dan Gittik의 대답은 멋집니다.

마지막 예시들은 많은 것들을 명확하게 할 수 있었습니다. 저는 그것을 파이썬 3으로 바꾸고 몇 가지 설명을 해주었습니다.

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls

        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

#China is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class China(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#Taiwan is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class Taiwan(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#A is a normal class and it's __new__ method would be changed by China(metaclass)
class A(metaclass=China):
    __metaclass__ = China

#B is a normal class and it's __new__ method would be changed by Taiwan(metaclass)
class B(metaclass=Taiwan):
    __metaclass__ = Taiwan


print(A._label)  # Made in China
print(B._label)  # Made in Taiwan

  • 모든 것은 객체입니다, 그래서 클래스는 객체입니다.
  • 클래스 개체가 메타클래스에 의해 생성되었습니다.
  • 형식에서 상속된 모든 클래스가 메타 클래스입니다.
  • 메타클래스가 클래스 생성을 제어할 수 있음
  • 메타 클래스는 메타 클래스 생성도 제어할 수 있습니다(그래서 영원히 반복될 수 있음).
  • 이건 메타프로그래밍...실행 시 유형 시스템을 제어할 수 있습니다.
  • 다시 말하지만, 모든 것은 객체입니다. 이것은 균일한 시스템입니다. 유형을 입력하고 유형을 입력하십시오. 인스턴스를 생성합니다.

또 다른 사용 사례는 클래스 수준 특성을 수정하고 해당 특성이 사용 중인 개체에만 영향을 미치는지 확인하려는 경우입니다.실제로 이것은 메타 클래스와 클래스 인스턴스화의 단계를 "통합"하여 고유한 클래스 인스턴스만 처리하도록 유도합니다.

(가독성다형성에 대한 우려로) 클래스 수준 속성을 기반으로 (자주 변경되는) 계산에서 반환되는 값을 동적으로 정의하고 싶을, 즉 메타 클래스 인스턴스화 후 및 클래스 인스턴스화 전에만 수행할 수 있습니다.

오래된 질문인 것은 알지만 생성자에게 전달된 매개 변수를 기반으로 클래스의 단일 인스턴스만 생성하려는 경우 매우 유용한 사용 사례가 있습니다.

인스턴스 싱글톤 Z-Wave 네트워크에서 장치의 싱글톤 인스턴스를 만드는 데 이 코드를 사용합니다.동일한 값이 생성자에게 전달된 경우 인스턴스를 아무리 많이 만들어도 정확히 동일한 값을 가진 인스턴스가 존재하면 인스턴스가 반환됩니다.

import inspect


class SingletonMeta(type):
    # only here to make IDE happy
    _instances = {}

    def __init__(cls, name, bases, dct):
        super(SingletonMeta, cls).__init__(name, bases, dct)
        cls._instances = {}

    def __call__(cls, *args, **kwargs):
        sig = inspect.signature(cls.__init__)
        keywords = {}

        for i, param in enumerate(list(sig.parameters.values())[1:]):
            if len(args) > i:
                keywords[param.name] = args[i]
            elif param.name not in kwargs and param.default != param.empty:
                keywords[param.name] = param.default
            elif param.name in kwargs:
                keywords[param.name] = kwargs[param.name]
        key = []
        for k in sorted(list(keywords.keys())):
            key.append(keywords[k])
        key = tuple(key)

        if key not in cls._instances:
            cls._instances[key] = (
                super(SingletonMeta, cls).__call__(*args, **kwargs)
            )

        return cls._instances[key]


class Test1(metaclass=SingletonMeta):

    def __init__(self, param1, param2='test'):
        pass


class Test2(metaclass=SingletonMeta):

    def __init__(self, param3='test1', param4='test2'):
        pass


test1 = Test1('test1')
test2 = Test1('test1', 'test2')
test3 = Test1('test1', 'test')

test4 = Test2()
test5 = Test2(param4='test1')
test6 = Test2('test2', 'test1')
test7 = Test2('test1')

print('test1 == test2:', test1 == test2)
print('test2 == test3:', test2 == test3)
print('test1 == test3:', test1 == test3)
print('test4 == test2:', test4 == test2)
print('test7 == test3:', test7 == test3)
print('test6 == test4:', test6 == test4)
print('test7 == test4:', test7 == test4)
print('test5 == test6:', test5 == test6)

print('number of Test1 instances:', len(Test1._instances))
print('number of Test2 instances:', len(Test2._instances))

산출량

test1 == test2: False
test2 == test3: False
test1 == test3: True
test4 == test2: False
test7 == test3: False
test6 == test4: False
test7 == test4: True
test5 == test6: False
number of Test1 instances: 2
number of Test2 instances: 3

이제 누군가 메타 클래스를 사용하지 않고도 수행할 수 있다고 말할 수 있으며 __init__ 메서드를 장식하면 수행할 수 있다는 것을 알고 있습니다.나는 그것을 하는 다른 방법을 모릅니다.아래 코드는 단일 인스턴스가 아닌 동일한 데이터를 모두 포함하는 유사한 인스턴스를 반환합니다. 새 인스턴스가 생성됩니다.동일한 데이터로 새 인스턴스를 만들기 때문에 인스턴스의 동일성을 확인하기 위해 추가 단계를 수행해야 합니다.결국 메타 클래스를 사용하는 것보다 메모리를 더 많이 소비하고 메타 클래스를 사용하면 동일성을 확인하기 위한 추가 단계를 수행할 필요가 없습니다.

class Singleton(object):
    _instances = {}

    def __init__(self, param1, param2='test'):
        key = (param1, param2)
        if key in self._instances:
            self.__dict__.update(self._instances[key].__dict__)
        else:
            self.param1 = param1
            self.param2 = param2
            self._instances[key] = self


test1 = Singleton('test1', 'test2')
test2 = Singleton('test')
test3 = Singleton('test', 'test')

print('test1 == test2:', test1 == test2)
print('test2 == test3:', test2 == test3)
print('test1 == test3:', test1 == test3)

print('test1 params', test1.param1, test1.param2)
print('test2 params', test2.param1, test2.param2)
print('test3 params', test3.param1, test3.param2)

print('number of Singleton instances:', len(Singleton._instances))

산출량

test1 == test2: False
test2 == test3: False
test1 == test3: False
test1 params test1 test2
test2 params test test
test3 params test test
number of Singleton instances: 2

메타 클래스 접근 방식은 새 인스턴스를 제거하거나 추가해야 할 경우에도 사용하기에 매우 좋습니다.

    import inspect


class SingletonMeta(type):
    # only here to make IDE happy
    _instances = {}

    def __init__(cls, name, bases, dct):
        super(SingletonMeta, cls).__init__(name, bases, dct)
        cls._instances = {}

    def __call__(cls, *args, **kwargs):
        sig = inspect.signature(cls.__init__)
        keywords = {}

        for i, param in enumerate(list(sig.parameters.values())[1:]):
            if len(args) > i:
                keywords[param.name] = args[i]
            elif param.name not in kwargs and param.default != param.empty:
                keywords[param.name] = param.default
            elif param.name in kwargs:
                keywords[param.name] = kwargs[param.name]
        key = []
        for k in sorted(list(keywords.keys())):
            key.append(keywords[k])
        key = tuple(key)

        if key not in cls._instances:
            cls._instances[key] = (
                super(SingletonMeta, cls).__call__(*args, **kwargs)
            )

        return cls._instances[key]


class Test(metaclass=SingletonMeta):

    def __init__(self, param1, param2='test'):
        pass


instances = []

instances.append(Test('test1', 'test2'))
instances.append(Test('test1', 'test'))

print('number of instances:', len(instances))

instance = Test('test2', 'test3')
if instance not in instances:
    instances.append(instance)

instance = Test('test1', 'test2')
if instance not in instances:
    instances.append(instance)

print('number of instances:', len(instances))

산출량

number of instances: 2
number of instances: 3

다음은 인스턴스가 더 이상 사용되지 않은 후에 생성된 인스턴스를 제거하는 방법입니다.

    import inspect
import weakref


class SingletonMeta(type):
    # only here to make IDE happy
    _instances = {}

    def __init__(cls, name, bases, dct):
        super(SingletonMeta, cls).__init__(name, bases, dct)

        def remove_instance(c, ref):
            for k, v in list(c._instances.items())[:]:
                if v == ref:
                    del cls._instances[k]
                    break
                    
        cls.remove_instance = classmethod(remove_instance)
        cls._instances = {}

    def __call__(cls, *args, **kwargs):
        sig = inspect.signature(cls.__init__)
        keywords = {}

        for i, param in enumerate(list(sig.parameters.values())[1:]):
            if len(args) > i:
                keywords[param.name] = args[i]
            elif param.name not in kwargs and param.default != param.empty:
                keywords[param.name] = param.default
            elif param.name in kwargs:
                keywords[param.name] = kwargs[param.name]
        key = []
        for k in sorted(list(keywords.keys())):
            key.append(keywords[k])
        key = tuple(key)

        if key not in cls._instances:
            instance = super(SingletonMeta, cls).__call__(*args, **kwargs)

            cls._instances[key] = weakref.ref(
                instance,
                instance.remove_instance
            )

        return cls._instances[key]()


class Test1(metaclass=SingletonMeta):

    def __init__(self, param1, param2='test'):
        pass


class Test2(metaclass=SingletonMeta):

    def __init__(self, param3='test1', param4='test2'):
        pass


test1 = Test1('test1')
test2 = Test1('test1', 'test2')
test3 = Test1('test1', 'test')

test4 = Test2()
test5 = Test2(param4='test1')
test6 = Test2('test2', 'test1')
test7 = Test2('test1')

print('test1 == test2:', test1 == test2)
print('test2 == test3:', test2 == test3)
print('test1 == test3:', test1 == test3)
print('test4 == test2:', test4 == test2)
print('test7 == test3:', test7 == test3)
print('test6 == test4:', test6 == test4)
print('test7 == test4:', test7 == test4)
print('test5 == test6:', test5 == test6)

print('number of Test1 instances:', len(Test1._instances))
print('number of Test2 instances:', len(Test2._instances))


print()
del test1
del test5
del test6

print('number of Test1 instances:', len(Test1._instances))
print('number of Test2 instances:', len(Test2._instances))

산출량

test1 == test2: False
test2 == test3: False
test1 == test3: True
test4 == test2: False
test7 == test3: False
test6 == test4: False
test7 == test4: True
test5 == test6: False
number of Test1 instances: 2
number of Test2 instances: 3

number of Test1 instances: 2
number of Test2 instances: 1

출력을 보면 테스트 1 인스턴스의 수가 변경되지 않았음을 알 수 있습니다.test1과 test3가 동일한 인스턴스이고 test1만 삭제했기 때문에 코드에 test1 인스턴스에 대한 참조가 남아 있기 때문에 test1 인스턴스가 제거되지 않습니다.

인스턴스가 제공된 매개 변수만 사용하여 작업이 수행되는 작업을 수행하는 경우 메타 클래스를 사용하여 완전히 다른 컴퓨터에서 또는 동일한 컴퓨터에서 다른 프로세스에서 원격으로 인스턴스를 생성할 수 있습니다.매개변수는 소켓 또는 명명된 파이프를 통해 간단히 전달될 수 있으며 클래스의 복제본은 수신측에 작성될 수 있습니다.

언급URL : https://stackoverflow.com/questions/392160/what-are-some-concrete-use-cases-for-metaclasses

반응형