[python] 파이썬에서 메타 클래스 란 무엇입니까?

파이썬에서 메타 클래스 란 무엇이며 어떻게 사용합니까?



답변

메타 클래스는 클래스의 클래스입니다. 클래스는 클래스의 인스턴스 (예 : 객체)의 동작을 정의하고 메타 클래스는 클래스의 동작 방식을 정의합니다. 클래스는 메타 클래스의 인스턴스입니다.

파이썬에서는 메타 클래스 (예 : Jerub 쇼)에 대해 임의의 호출 가능 항목을 사용할 수 있지만 더 나은 방법은 실제 클래스 자체로 만드는 것입니다. type파이썬에서 일반적인 메타 클래스입니다. type그 자체가 클래스이며 자체 유형입니다. type파이썬에서 순수하게 무언가를 다시 만들 수는 없지만 파이썬은 약간의 속임수를 씁니다. 파이썬으로 자신 만의 메타 클래스를 만들려면 정말로 서브 클래스를 만들고 싶습니다 type.

메타 클래스는 클래스 팩토리로 가장 일반적으로 사용됩니다. 클래스를 호출하여 객체를 만들면 Python은 메타 클래스를 호출하여 새 클래스를 생성합니다 ( ‘class’문을 실행할 때). 메타 클래스 는 일반 __init____new__메소드 와 결합 하여 클래스를 만들 때 새 클래스를 일부 레지스트리에 등록하거나 클래스를 완전히 다른 것으로 대체하는 것과 같이 ‘추가 항목’을 수행 할 수 있습니다.

class문장이 실행될 때 , 파이썬은 먼저 class문장 의 본문을 일반적인 코드 블록으로 실행합니다. 결과 네임 스페이스 (dict)는 클래스의 속성을 보유합니다. 메타 클래스는 클래스의 __metaclass__속성 (메타 클래스가 상 속됨), 클래스 의 속성 (있는 경우) 또는 __metaclass__글로벌 변수를보고 결정됩니다. 그런 다음 메타 클래스가 클래스의 이름, 기본 및 속성과 함께 호출되어이를 인스턴스화합니다.

그러나 메타 클래스는 실제로 팩토리가 아니라 클래스 의 유형 을 정의 하므로 더 많은 작업을 수행 할 수 있습니다. 예를 들어 메타 클래스에 일반 메소드를 정의 할 수 있습니다. 이러한 메타 클래스 메소드는 인스턴스없이 클래스에서 호출 될 수 있다는 점에서 클래스 메소드와 비슷하지만 클래스의 인스턴스에서 호출 할 수 없다는 점에서 클래스 메소드와는 다릅니다. 메타 클래스 type.__subclasses__()에있는 메소드의 예입니다 type. 당신은 또한 같은 정상 ‘마법’방법을 정의 할 수 있습니다 __add__, __iter____getattr__, 구현 또는 변경하는 방법 클래스 동작합니다 할 수 있습니다.

다음은 비트와 조각의 집계 된 예입니다.

def make_hook(f):
    """Decorator to turn 'foo' method into '__foo__'"""
    f.is_hook = 1
    return f

class MyType(type):
    def __new__(mcls, name, bases, attrs):

        if name.startswith('None'):
            return None

        # Go over attributes and see if they should be renamed.
        newattrs = {}
        for attrname, attrvalue in attrs.iteritems():
            if getattr(attrvalue, 'is_hook', 0):
                newattrs['__%s__' % attrname] = attrvalue
            else:
                newattrs[attrname] = attrvalue

        return super(MyType, mcls).__new__(mcls, name, bases, newattrs)

    def __init__(self, name, bases, attrs):
        super(MyType, self).__init__(name, bases, attrs)

        # classregistry.register(self, self.interfaces)
        print "Would register class %s now." % self

    def __add__(self, other):
        class AutoClass(self, other):
            pass
        return AutoClass
        # Alternatively, to autogenerate the classname as well as the class:
        # return type(self.__name__ + other.__name__, (self, other), {})

    def unregister(self):
        # classregistry.unregister(self)
        print "Would unregister class %s now." % self

class MyObject:
    __metaclass__ = MyType


class NoneSample(MyObject):
    pass

# Will print "NoneType None"
print type(NoneSample), repr(NoneSample)

class Example(MyObject):
    def __init__(self, value):
        self.value = value
    @make_hook
    def add(self, other):
        return self.__class__(self.value + other.value)

# Will unregister the class
Example.unregister()

inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()

print inst + inst
class Sibling(MyObject):
    pass

ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__


답변

객체로서의 클래스

메타 클래스를 이해하기 전에 파이썬에서 클래스를 마스터해야합니다. 그리고 파이썬은 스몰 토크 언어에서 빌린 클래스가 무엇인지에 대해 매우 독특합니다.

대부분의 언어에서 클래스는 객체를 생성하는 방법을 설명하는 코드 조각 일뿐입니다. 파이썬에서도 마찬가지입니다.

>>> class ObjectCreator(object):
...       pass
...

>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

그러나 클래스는 파이썬보다 더 많습니다. 클래스도 객체입니다.

예, 물건.

키워드를 사용하자마자 classPython은 키워드 를 실행하고 OBJECT를 만듭니다. 지시

>>> class ObjectCreator(object):
...       pass
...

이름이 “ObjectCreator”인 오브젝트를 메모리에 작성합니다.

이 객체 (클래스) 자체는 객체 (인스턴스)를 만들 수 있으며 이것이 클래스 인 이유 입니다.

그러나 여전히 객체이므로 다음과 같습니다.

  • 변수에 할당 할 수 있습니다
  • 당신은 그것을 복사 할 수 있습니다
  • 속성을 추가 할 수 있습니다
  • 함수 매개 변수로 전달할 수 있습니다

예 :

>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
...       print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>

동적으로 클래스 만들기

클래스는 객체이기 때문에 다른 객체처럼 즉시 만들 수 있습니다.

먼저 다음을 사용하여 함수에서 클래스를 만들 수 있습니다 class.

>>> def choose_class(name):
...     if name == 'foo':
...         class Foo(object):
...             pass
...         return Foo # return the class, not an instance
...     else:
...         class Bar(object):
...             pass
...         return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

그러나 여전히 전체 수업을 직접 작성해야하기 때문에 그렇게 역동적이지는 않습니다.

클래스는 객체이므로 무언가로 생성해야합니다.

class키워드 를 사용하면 Python이이 객체를 자동으로 만듭니다. 그러나 파이썬에서 대부분의 것들과 마찬가지로 수동으로 수행하는 방법을 제공합니다.

기능을 기억 type하십니까? 객체가 어떤 유형인지 알 수있는 좋은 오래된 기능 :

>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

글쎄, type완전히 다른 능력을 가지고, 그것은 또한 즉시 수업을 만들 수 있습니다. type클래스의 설명을 매개 변수로 사용하여 클래스를 반환 할 수 있습니다.

(나는 동일한 함수가 전달하는 매개 변수에 따라 두 가지 완전히 다른 용도를 가질 수 있다는 것은 바보 같은 사실입니다. Python의 하위 호환성으로 인해 문제가됩니다)

type 이 방법으로 작동합니다 :

type(name, bases, attrs)

어디:

  • name: 수업 명
  • bases: 부모 클래스의 튜플 (상속을 위해 비어있을 수 있음)
  • attrs: 속성 이름 및 값을 포함하는 사전

예 :

>>> class MyShinyClass(object):
...       pass

이 방법으로 수동으로 만들 수 있습니다.

>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>

“MyShinyClass”를 클래스 이름으로 사용하고 클래스 참조를 보유하는 변수로 사용합니다. 그것들은 다를 수 있지만 문제를 복잡하게 할 이유는 없습니다.

type클래스의 속성을 정의하기위한 사전을 허용합니다. 그래서:

>>> class Foo(object):
...       bar = True

다음과 같이 번역 될 수 있습니다.

>>> Foo = type('Foo', (), {'bar':True})

그리고 일반 클래스로 사용됩니다.

>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True

물론 당신은 그것을 상속받을 수 있습니다.

>>>   class FooChild(Foo):
...         pass

될 것입니다 :

>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True

결국 클래스에 메소드를 추가하려고합니다. 적절한 서명으로 함수를 정의하고 속성으로 지정하십시오.

>>> def echo_bar(self):
...       print(self.bar)
...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True

그리고 일반적으로 생성 된 클래스 객체에 메소드를 추가하는 것처럼 클래스를 동적으로 생성 한 후에 더 많은 메소드를 추가 할 수 있습니다.

>>> def echo_bar_more(self):
...       print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

우리가 어디로 가고 있는지 알 수 있습니다. 파이썬에서 클래스는 객체이며 동적으로 클래스를 즉석에서 만들 수 있습니다.

이것은 키워드를 사용할 때 파이썬이하는 class일이며 메타 클래스를 사용하여 그렇게합니다.

메타 클래스 (최종)

메타 클래스는 클래스를 만드는 ‘재료’입니다.

객체를 생성하기 위해 클래스를 정의합니다.

그러나 파이썬 클래스는 객체라는 것을 배웠습니다.

메타 클래스가 이러한 객체를 만드는 것입니다. 그들은 수업의 수업입니다, 당신은 이런 식으로 그들을 그림으로 만들 수 있습니다

MyClass = MetaClass()
my_object = MyClass()

당신은 type이것을 다음과 같이 할 수 있다는 것을 보았습니다 .

MyClass = type('MyClass', (), {})

함수 type가 실제로 메타 클래스 이기 때문 입니다. type파이썬은 배후에서 모든 클래스를 만드는 데 사용하는 메타 클래스입니다.

이제 도대체 왜 소문자로 쓰여 졌는지 궁금하지 Type않습니까?

글쎄, 그것은 str문자열 객체 int를 만드는 클래스 와 정수 객체를 만드는 클래스 와의 일관성 문제라고 생각 합니다. type클래스 객체를 만드는 클래스입니다.

__class__속성 을 확인하면 알 수 있습니다 .

모든 것은 모든 것을 의미합니다. 파이썬의 객체입니다. 여기에는 정수, 문자열, 함수 및 클래스가 포함됩니다. 그들 모두는 대상입니다. 그리고 그들 모두는 수업에서 만들어졌습니다.

>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

자, 무엇 __class__모든의는 __class__?

>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

따라서 메타 클래스는 클래스 객체를 만드는 것입니다.

원한다면 ‘클래스 팩토리’라고 부를 수 있습니다.

type 파이썬이 사용하는 내장 메타 클래스이지만, 물론 자신 만의 메타 클래스를 만들 수 있습니다.

__metaclass__속성

Python 2에서는 __metaclass__클래스를 작성할 때 속성을 추가 할 수 있습니다 (Python 3 구문은 다음 섹션 참조).

class Foo(object):
    __metaclass__ = something...
    [...]

그렇게하면 파이썬은 메타 클래스를 사용하여 클래스를 만듭니다 Foo.

조심해, 까다 롭다.

class Foo(object)먼저 작성 하지만 클래스 객체 Foo는 아직 메모리에 작성되지 않았습니다.

파이썬은 __metaclass__클래스 정의에서 찾을 것입니다 . 발견되면이를 사용하여 객체 클래스를 만듭니다 Foo. 그렇지 않으면 type클래스를 만드는 데 사용
됩니다.

여러 번 읽어보세요.

할 때 :

class Foo(Bar):
    pass

파이썬은 다음을 수행합니다.

__metaclass__속성이 Foo있습니까?

예를 메모리에 클래스 객체를 생성하는 경우 이름으로, (I 클래스 객체 나 여기에 숙박했다) Foo에 무엇을 사용하여 __metaclass__.

파이썬이 찾을 수 없다면 , MODULE 레벨에서을 __metaclass__찾고 __metaclass__같은 것을 시도합니다 (그러나 기본적으로 구식 클래스를 상속받지 않는 클래스에 대해서만).

그 다음은 어떤 찾을 수없는 경우 __metaclass__에 모두를, 그것은 사용 Bar의 (첫 번째 부모) (기본이 될 수있는 자신의 메타 클래스를 type클래스 객체를 생성).

여기서 __metaclass__속성이 상속되지 않고 부모 ( Bar.__class__) 의 메타 클래스가 상속 된다는 점에주의하십시오 . 경우 Bar사용 된 __metaclass__만든 속성 Bartype()(와하지를 type.__new__()), 서브 클래스는 행동 것을 상속하지 않습니다.

이제 큰 문제는 무엇을 넣을 수 __metaclass__있습니까?

답은 클래스를 만들 수있는 것입니다.

그리고 무엇이 클래스를 만들 수 있습니까? type또는 서브 클래스로 사용하거나 사용하는 모든 것.

파이썬 3의 메타 클래스

메타 클래스를 설정하는 구문이 Python 3에서 변경되었습니다.

class Foo(object, metaclass=something):
    ...

즉, __metaclass__기본 클래스 목록의 키워드 인수를 위해 속성이 더 이상 사용되지 않습니다.

그러나 메타 클래스의 동작은 거의 동일하게 유지됩니다 .

파이썬 3에서 메타 클래스에 추가 된 한 가지는 키워드 인수로 속성을 메타 클래스에 전달할 수 있다는 것입니다.

class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
    ...

파이썬이 이것을 어떻게 처리하는지 아래 섹션을 읽으십시오.

커스텀 메타 클래스

메타 클래스의 주요 목적은 클래스가 생성 될 때 클래스를 자동으로 변경하는 것입니다.

일반적으로 현재 컨텍스트와 일치하는 클래스를 작성하려는 API에 대해이 작업을 수행합니다.

모듈의 모든 클래스에 특성이 대문자로 작성되어야한다고 결정하는 어리석은 예를 상상해보십시오. 여러 가지 방법이 있지만 한 가지 방법은 __metaclass__모듈 수준에서 설정 하는 것입니다.

이런 식으로이 모듈의 모든 클래스는이 메타 클래스를 사용하여 생성되며 모든 속성을 대문자로 바꾸도록 메타 클래스에 지시하면됩니다.

운 좋게도 __metaclass__실제로 호출 가능 할 수는 있지만 공식적인 클래스 일 필요는 없습니다 (이름에 ‘class’가있는 클래스는 클래스 일 필요는 없습니다.하지만 도움이됩니다).

함수를 사용하여 간단한 예부터 시작하겠습니다.

# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attrs):
    """
      Return a class object, with the list of its attribute turned
      into uppercase.
    """
    # pick up any attribute that doesn't start with '__' and uppercase it
    uppercase_attrs = {
        attr if attr.startswith("__") else attr.upper(): v
        for attr, v in future_class_attrs.items()
    }

    # let `type` do the class creation
    return type(future_class_name, future_class_parents, uppercase_attrs)

__metaclass__ = upper_attr # this will affect all classes in the module

class Foo(): # global __metaclass__ won't work with "object" though
    # but we can define __metaclass__ here instead to affect only this class
    # and this will work with "object" children
    bar = 'bip'

점검 해보자:

>>> hasattr(Foo, 'bar')
False
>>> hasattr(Foo, 'BAR')
True
>>> Foo.BAR
'bip'

이제 정확히 동일하게 수행하지만 메타 클래스에 실제 클래스를 사용하십시오.

# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
    # __new__ is the method called before __init__
    # it's the method that creates the object and returns it
    # while __init__ just initializes the object passed as parameter
    # you rarely use __new__, except when you want to control how the object
    # is created.
    # here the created object is the class, and we want to customize it
    # so we override __new__
    # you can do some stuff in __init__ too if you wish
    # some advanced use involves overriding __call__ as well, but we won't
    # see this
    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attrs):
        uppercase_attrs = {
            attr if attr.startswith("__") else attr.upper(): v
            for attr, v in future_class_attrs.items()
        }
        return type(future_class_name, future_class_parents, uppercase_attrs)

위의 내용을 다시 작성하지만 더 짧고 현실적인 변수 이름을 사용하여 의미를 알 수 있습니다.

class UpperAttrMetaclass(type):
    def __new__(cls, clsname, bases, attrs):
        uppercase_attrs = {
            attr if attr.startswith("__") else attr.upper(): v
            for attr, v in attrs.items()
        }
        return type(clsname, bases, uppercase_attrs)

당신은 여분의 주장을 알아 차렸을 것 cls입니다. 특별한 것은 없습니다 : __new__항상 정의 된 클래스를 첫 번째 매개 변수로받습니다. 그냥 당신이이 같은 self첫 번째 매개 변수 또는 클래스 메소드의 정의 클래스로 인스턴스를받을 일반 메소드.

그러나 이것은 적절한 OOP가 아닙니다. 우리는 type직접 전화를하고 부모의 것을 재정의하거나 전화하지 않습니다 __new__. 대신 해보자 :

class UpperAttrMetaclass(type):
    def __new__(cls, clsname, bases, attrs):
        uppercase_attrs = {
            attr if attr.startswith("__") else attr.upper(): v
            for attr, v in attrs.items()
        }
        return type.__new__(cls, clsname, bases, uppercase_attrs)

super상속을 용이하게하는 을 사용하여 더 깔끔하게 만들 수 있습니다.

class UpperAttrMetaclass(type):
    def __new__(cls, clsname, bases, attrs):
        uppercase_attrs = {
            attr if attr.startswith("__") else attr.upper(): v
            for attr, v in attrs.items()
        }
        return super(UpperAttrMetaclass, cls).__new__(
            cls, clsname, bases, uppercase_attrs)

아, 그리고 파이썬 3에서는 다음과 같이 키워드 인수 로이 호출을 수행하십시오.

class Foo(object, metaclass=MyMetaclass, kwarg1=value1):
    ...

그것을 사용하기 위해 메타 클래스에서 이것을 번역합니다.

class MyMetaclass(type):
    def __new__(cls, clsname, bases, dct, kwargs1=default):
        ...

그게 다야. 메타 클래스에 대해서는 더 이상 아무것도 없습니다.

메타 클래스를 사용하는 코드의 복잡성 뒤에있는 이유는 메타 클래스 때문이 아닙니다. 일반적으로 메타 클래스를 사용하여 내성, 상속 조작,와 같은 var __dict__등에 의존하는 트위스트 된 항목을 수행하기 때문입니다 .

실제로 메타 클래스는 블랙 매직을 수행하는 데 특히 유용하므로 복잡한 작업을 수행하는 데 특히 유용합니다. 그러나 그 자체로는 간단합니다.

  • 클래스 생성을 가로 채다
  • 수업을 수정하다
  • 수정 된 클래스를 반환

함수 대신 메타 클래스 클래스를 사용하는 이유는 무엇입니까?

__metaclass__호출 가능한 것을 받아 들일 수 있기 때문에 왜 더 복잡한 클래스를 사용해야합니까?

몇 가지 이유가 있습니다.

  • 의도는 분명하다. 당신이 읽을 때 UpperAttrMetaclass(type), 당신은 무엇을 따를 지 알고
  • OOP를 사용할 수 있습니다. 메타 클래스는 메타 클래스에서 상속 할 수 있고 부모 메소드를 대체 할 수 있습니다. 메타 클래스는 메타 클래스를 사용할 수도 있습니다.
  • 메타 클래스 클래스를 지정했지만 메타 클래스 함수가 ​​아닌 클래스의 서브 클래스는 메타 클래스의 인스턴스가됩니다.
  • 코드를 더 잘 구성 할 수 있습니다. 위의 예제처럼 사소한 것에 메타 클래스를 사용하지 마십시오. 일반적으로 복잡한 것입니다. 여러 메소드를 작성하고 하나의 클래스로 그룹화하는 기능이 있으면 코드를보다 쉽게 ​​읽을 수 있습니다.
  • 당신은에 연결할 수 있습니다 __new__, __init__하고 __call__. 다른 일을 할 수 있습니다. 일반적으로 모든 것을 할 수 있지만 __new__어떤 사람들은 사용하기가 더 편합니다 __init__.
  • 이것을 메타 클래스라고합니다. 뭔가를 의미해야합니다!

왜 메타 클래스를 사용 하시겠습니까?

이제 큰 질문입니다. 왜 모호한 오류가 발생하기 쉬운 기능을 사용 하시겠습니까?

글쎄, 보통 당신은하지 않습니다 :

메타 클래스는 99 %의 사용자가 걱정할 필요가없는 더 깊은 마법입니다. 필요한지 궁금하다면 필요하지 않습니다 (실제로 필요한 사람들은 필요하다는 것을 확실하게 알고 있으며 이유에 대한 설명이 필요하지 않습니다).

파이썬 전문가 팀 피터스

메타 클래스의 주요 사용 사례는 API를 만드는 것입니다. 이것의 전형적인 예는 Django ORM입니다. 다음과 같이 정의 할 수 있습니다.

class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

그러나 이렇게하면 :

person = Person(name='bob', age='35')
print(person.age)

IntegerField객체를 반환하지 않습니다 . 를 반환 int하고 데이터베이스에서 직접 가져올 수도 있습니다.

이것은 간단한 문장으로 정의한 것을 데이터베이스 필드에 대한 복잡한 고리로 바꾸는 마법을 사용하고 models.Model정의 하기 때문에 가능 합니다.__metaclass__Person

Django는 간단한 API를 노출하고 메타 클래스를 사용하여이 API에서 코드를 다시 작성하여 배후에서 실제 작업을 수행함으로써 복잡한 것을 단순하게 만듭니다.

마지막 단어

먼저 클래스는 인스턴스를 생성 할 수있는 객체라는 것을 알고 있습니다.

실제로 클래스 자체는 실례입니다. 메타 클래스

>>> class Foo(object): pass
>>> id(Foo)
142630324

모든 것은 파이썬의 객체이며, 모두 클래스의 인스턴스 또는 메타 클래스의 인스턴스입니다.

제외 type.

type실제로는 자체 메타 클래스입니다. 이것은 순수한 파이썬에서 재현 할 수있는 것이 아니며 구현 수준에서 약간의 부정 행위로 수행됩니다.

둘째, 메타 클래스가 복잡합니다. 매우 간단한 수업 변경에는 사용하지 않을 수도 있습니다. 두 가지 기술을 사용하여 클래스를 변경할 수 있습니다.

수업 변경이 필요한 시간의 99 %는이를 사용하는 것이 좋습니다.

그러나 시간의 98 %는 수업 변경이 전혀 필요하지 않습니다.


답변

이 답변은 2008 년에 작성된 Python 2.x에 대한 것이며 메타 클래스는 3.x에서 약간 다릅니다.

메타 클래스는 ‘클래스’가 작동하는 비밀 소스입니다. 새 스타일 객체의 기본 메타 클래스는 ‘type’입니다.

class type(object)
  |  type(object) -> the object's type
  |  type(name, bases, dict) -> a new type

메타 클래스는 3 개의 인수를 취합니다. ‘ name ‘, ‘ bases ‘및 ‘ dict

여기 비밀이 시작됩니다. 이 예제 클래스 정의에서 이름,베이스 및 딕션의 출처를 찾으십시오.

class ThisIsTheName(Bases, Are, Here):
    All_the_code_here
    def doesIs(create, a):
        dict

class : ‘가 어떻게 그것을 호출 하는지 보여줄 메타 클래스를 정의하자 .

def test_metaclass(name, bases, dict):
    print 'The Class Name is', name
    print 'The Class Bases are', bases
    print 'The dict has', len(dict), 'elems, the keys are', dict.keys()

    return "yellow"

class TestName(object, None, int, 1):
    __metaclass__ = test_metaclass
    foo = 1
    def baz(self, arr):
        pass

print 'TestName = ', repr(TestName)

# output => 
The Class Name is TestName
The Class Bases are (<type 'object'>, None, <type 'int'>, 1)
The dict has 4 elems, the keys are ['baz', '__module__', 'foo', '__metaclass__']
TestName =  'yellow'

그리고 이제 실제로 무언가를 의미하는 예제입니다. 그러면 목록의 변수가 클래스에서 “속성”으로 설정되고 없음으로 설정됩니다.

def init_attributes(name, bases, dict):
    if 'attributes' in dict:
        for attr in dict['attributes']:
            dict[attr] = None

    return type(name, bases, dict)

class Initialised(object):
    __metaclass__ = init_attributes
    attributes = ['foo', 'bar', 'baz']

print 'foo =>', Initialised.foo
# output=>
foo => None

Initialised메타 클래스를 가짐으로써 얻는 마법의 행동은 init_attributes의 서브 클래스로 전달되지 않습니다 Initialised.

다음은 클래스를 만들 때 작업을 수행하는 메타 클래스를 만들기 위해 ‘type’을 서브 클래 싱하는 방법을 보여주는보다 구체적인 예입니다. 이것은 매우 까다 롭습니다.

class MetaSingleton(type):
    instance = None
    def __call__(cls, *args, **kw):
        if cls.instance is None:
            cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)
        return cls.instance

class Foo(object):
    __metaclass__ = MetaSingleton

a = Foo()
b = Foo()
assert a is b


답변

다른 사람들은 메타 클래스의 작동 방식과 파이썬 유형 시스템에 어떻게 적합한 지 설명했습니다. 다음은 이들이 사용할 수있는 예입니다. 필자가 작성한 테스트 프레임 워크에서 클래스가 정의 된 순서를 추적하여 나중에이 순서대로 클래스를 인스턴스화 할 수 있기를 원했습니다. 메타 클래스를 사용하여 가장 쉬운 방법이라는 것을 알았습니다.

class MyMeta(type):

    counter = 0

    def __init__(cls, name, bases, dic):
        type.__init__(cls, name, bases, dic)
        cls._order = MyMeta.counter
        MyMeta.counter += 1

class MyType(object):              # Python 2
    __metaclass__ = MyMeta

class MyType(metaclass=MyMeta):    # Python 3
    pass

하위 클래스 인 클래스는 클래스가 정의 된 순서를 기록 MyType하는 클래스 속성을 가져 _order옵니다.


답변

메타 클래스의 한 가지 용도는 인스턴스에 새로운 속성과 메서드를 자동으로 추가하는 것입니다.

예를 들어 Django models 을 보면 그 정의가 약간 혼란스러워 보입니다. 클래스 속성 만 정의하는 것처럼 보입니다.

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

그러나 런타임시 Person 오브젝트는 모든 종류의 유용한 메소드로 채워집니다. 놀라운 메타 클래스에 대한 소스 를 참조하십시오 .


답변

메타 클래스 프로그래밍에 대한 ONLamp 소개는 잘 작성되어 있으며 이미 몇 년이 지났음에도 불구하고 주제에 대한 좋은 소개를 제공합니다.

http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html (에 보관 https://web.archive.org/web/20080206005253/http://www.onlamp. com / pub / a / python / 2003 / 04 / 17 / metaclasses.html )

한마디로 : 클래스는 인스턴스 생성을위한 청사진이고, 메타 클래스는 클래스 생성을위한 청사진입니다. 파이썬 클래스에서는이 동작을 가능하게하기 위해 일류 객체 여야한다는 것을 쉽게 알 수 있습니다.

필자는 직접 작성한 적이 없지만 장고 프레임 워크 에서 메타 클래스를 가장 잘 사용하는 방법 중 하나를 볼 수 있다고 생각 합니다. 모델 클래스는 메타 클래스 접근 방식을 사용하여 선언적인 스타일의 새 모델 또는 양식 클래스를 작성할 수 있습니다. 메타 클래스가 클래스를 작성하는 동안 모든 멤버는 클래스 자체를 사용자 정의 할 수 있습니다.

말에 남은 것은입니다 : 당신이 메타 클래스가 무엇인지 모른다면, 당신은 확률 을 필요로하지 않습니다는 99 %입니다.


답변

메타 클래스 란 무엇입니까? 무엇을 위해 사용합니까?

TLDR : 메타 클래스는 클래스와 마찬가지로 클래스의 동작을 인스턴스화하고 정의하며 인스턴스의 동작을 정의합니다.

의사 코드 :

>>> Class(...)
instance

위의 내용은 익숙해야합니다. 글쎄, 어디서 Class온거야? 메타 클래스 (의사 코드)의 인스턴스입니다.

>>> Metaclass(...)
Class

실제 코드에서는 기본 메타 클래스 인 type클래스를 인스턴스화하고 클래스를 얻는 데 필요한 모든 것을 전달할 수 있습니다 .

>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
<class '__main__.Foo'>

다르게 퍼팅

  • 메타 클래스가 클래스에 대한 클래스는 인스턴스에 대한 것입니다.

    객체를 인스턴스화하면 인스턴스가 생성됩니다.

    >>> object()                          # instantiation of class
    <object object at 0x7f9069b4e0b0>     # instance

    마찬가지로 기본 메타 클래스로 클래스를 명시 적으로 정의하면 type인스턴스화됩니다.

    >>> type('Object', (object,), {})     # instantiation of metaclass
    <class '__main__.Object'>             # instance
  • 다시 말해서 클래스는 메타 클래스의 인스턴스입니다.

    >>> isinstance(object, type)
    True
  • 세 번째 방법으로 메타 클래스는 클래스의 클래스입니다.

    >>> type(object) == type
    True
    >>> object.__class__
    <class 'type'>

클래스 정의를 작성하고 파이썬이이를 실행하면 메타 클래스를 사용하여 클래스 객체를 인스턴스화합니다 (그러면 해당 클래스의 인스턴스를 인스턴스화하는 데 사용됨).

클래스 정의를 사용하여 사용자 정의 객체 인스턴스의 동작 방식을 변경할 수있는 것처럼 메타 클래스 클래스 정의를 사용하여 클래스 객체의 동작 방식을 변경할 수 있습니다.

그들은 무엇을 위해 사용될 수 있습니까? 로부터 문서 :

메타 클래스의 잠재적 용도는 무한합니다. 탐색 된 일부 아이디어에는 로깅, 인터페이스 검사, 자동 위임, 자동 속성 생성, 프록시, 프레임 워크 및 자동 리소스 잠금 / 동기화가 포함됩니다.

그럼에도 불구하고 일반적으로 사용자가 절대적으로 필요한 경우가 아니면 메타 클래스를 사용하지 않는 것이 좋습니다.

클래스를 만들 때마다 메타 클래스를 사용합니다.

예를 들어 다음과 같이 클래스 정의를 작성할 때

class Foo(object):
    'demo'

클래스 객체를 인스턴스화합니다.

>>> Foo
<class '__main__.Foo'>
>>> isinstance(Foo, type), isinstance(Foo, object)
(True, True)

type적절한 인수를 사용하여 기능적으로 호출 하고 결과를 해당 이름의 변수에 지정하는 것과 같습니다.

name = 'Foo'
bases = (object,)
namespace = {'__doc__': 'demo'}
Foo = type(name, bases, namespace)

__dict__네임 스페이스에 자동으로 추가되는 항목 이 있습니다.

>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>,
'__module__': '__main__', '__weakref__': <attribute '__weakref__'
of 'Foo' objects>, '__doc__': 'demo'})

두 경우 모두 생성 한 객체 의 메타 클래스type입니다.

(클래스의 내용에 측면 참고 __dict__: __module__클래스가 정의되는 곳 알고 있어야하기 때문에 존재 __dict__하고 __weakref__우리는 정의하지 않기 때문에이입니다 __slots__– 우리는 경우에 정의__slots__ 우리가 경우에 공간의 비트를 절약 할 수로, 우리는 허용하지 않을 수 있습니다 __dict____weakref__이를 제외하여 예를 들면 다음과 같습니다.

>>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
>>> Baz.__dict__
mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})

…하지만 나는 산만하다.)

type다른 클래스 정의처럼 확장 할 수 있습니다 :

기본 __repr__클래스 는 다음과 같습니다 .

>>> Foo
<class '__main__.Foo'>

Python 객체를 작성할 때 기본적으로 할 수있는 가장 유용한 작업 중 하나는 좋은 객체를 제공하는 것 __repr__입니다. 우리가 부를 때 우리 는 동등성에 대한 테스트를 요구하는 help(repr)좋은 테스트가 있다는 것을 알게 됩니다. 다음과 같은 간단한 구현 하고 우리 형 클래스의 클래스 인스턴스는 기본 개선 할 수있는 데모 우리에게 제공 클래스 :__repr__obj == eval(repr(obj))__repr____eq____repr__

class Type(type):
    def __repr__(cls):
        """
        >>> Baz
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        >>> eval(repr(Baz))
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        """
        metaname = type(cls).__name__
        name = cls.__name__
        parents = ', '.join(b.__name__ for b in cls.__bases__)
        if parents:
            parents += ','
        namespace = ', '.join(': '.join(
          (repr(k), repr(v) if not isinstance(v, type) else v.__name__))
               for k, v in cls.__dict__.items())
        return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)
    def __eq__(cls, other):
        """
        >>> Baz == eval(repr(Baz))
        True
        """
        return (cls.__name__, cls.__bases__, cls.__dict__) == (
                other.__name__, other.__bases__, other.__dict__)

이제이 메타 클래스를 사용하여 객체를 만들면 __repr__명령 줄에 에코 표시가 기본값보다 훨씬 덜보기 좋습니다.

>>> class Bar(object): pass
>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})

__repr__클래스 인스턴스를 잘 정의 하면 코드를 디버깅 할 수있는 능력이 향상됩니다. 그러나 eval(repr(Class))기능을 기본 값에서 평가하는 것은 다소 불가능하므로 훨씬 더 자세한 검사 는 거의 불가능합니다 __repr__.

예상 사용법 : __prepare__네임 스페이스

예를 들어, 클래스의 메소드가 어떤 순서로 작성되는지 알고 싶다면, 클래스의 네임 스페이스로서 순서화 된 dict를 제공 할 수 있습니다. 우리는 함께이 할 것이 __prepare__있는 가 파이썬 3에서 구현되는 경우 클래스의 네임 스페이스 딕셔너리를 반환합니다 :

from collections import OrderedDict

class OrderedType(Type):
    @classmethod
    def __prepare__(metacls, name, bases, **kwargs):
        return OrderedDict()
    def __new__(cls, name, bases, namespace, **kwargs):
        result = Type.__new__(cls, name, bases, dict(namespace))
        result.members = tuple(namespace)
        return result

그리고 사용법 :

class OrderedMethodsObject(object, metaclass=OrderedType):
    def method1(self): pass
    def method2(self): pass
    def method3(self): pass
    def method4(self): pass

그리고 이제 우리는이 메소드들과 다른 클래스 속성들이 생성 된 순서에 대한 기록을 가지고 있습니다 :

>>> OrderedMethodsObject.members
('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')

이 예제는 문서 에서 수정 되었습니다. 표준 라이브러리 의 새 열거 형 이이를 수행합니다.

우리가 한 일은 클래스를 만들어 메타 클래스를 인스턴스화하는 것이 었습니다. 다른 클래스와 마찬가지로 메타 클래스를 처리 할 수도 있습니다. 메소드 해결 순서가 있습니다.

>>> inspect.getmro(OrderedType)
(<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)

그리고 그것은 거의 정확합니다 repr(함수를 나타내는 방법을 찾을 수 없다면 더 이상 평가할 수 없습니다).

>>> OrderedMethodsObject
OrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMet
hodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__d
ict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})