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





메타프로그래밍 리스트 (13)


다른 것들은 메타 클래스의 작동 방식과 파이썬 유형 시스템에 어떻게 맞는지 설명했습니다. 다음은 이들을 사용할 수있는 예입니다. 내가 작성한 테스트 프레임 워크에서 클래스가 정의 된 순서를 추적하여 나중에이 순서로 인스턴스화 할 수 있도록했습니다. 메타 클래스를 사용하여이 작업을 수행하는 것이 가장 쉬운 것으로 나타났습니다.

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 를 가져 _order .

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




메타 클래스는 클래스의 클래스입니다. 클래스와 마찬가지로 클래스의 인스턴스가 작동하는 방식을 정의합니다. 메타 클래스는 클래스의 작동 방식을 정의합니다. 클래스는 메타 클래스의 인스턴스입니다.

Python에서는 (예 : Jerub 쇼와 같이) 메타 클래스에 임의의 호출 가능 Jerub 를 사용할 수 있지만 더 유용한 접근 방법은 실제 클래스를 실제 클래스로 만드는 것입니다. type 은 Python의 일반적인 메타 클래스입니다. 궁금한 점이 있으시면, type 은 그 자체가 클래스이고, 그것 자체의 타입입니다. 순전히 Python에서 type 과 같은 것을 재현 할 수는 없지만 Python은 약간의 속임수를 사용합니다. 파이썬에서 자신 만의 메타 클래스를 만들려면 type 을 서브 클래스 화하기 만하면됩니다.

메타 클래스는 가장 일반적으로 클래스 팩토리로 사용됩니다. 클래스를 호출하여 클래스의 인스턴스를 생성하는 것처럼, Python은 메타 클래스를 호출하여 새로운 클래스를 작성합니다 ( 'class'문을 실행할 때). 따라서 __init____new__ 메소드와 함께 메타 클래스를 사용하면 새 클래스를 레지스트리에 등록하는 것과 같이 클래스를 만들 때 '추가 작업'을 수행 할 수 있으며 클래스를 완전히 다른 것으로 대체 할 수도 있습니다.

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

그러나 메타 클래스는 실제로 클래스가 아닌 클래스의 유형 을 정의하므로 훨씬 더 많은 작업을 수행 할 수 있습니다. 예를 들어 메타 클래스에서 정상적인 메서드를 정의 할 수 있습니다. 이러한 메타 클래스 메소드는 인스턴스없이 클래스에서 호출 될 수 있다는 점에서 클래스 메소드와 비슷하지만 클래스 인스턴스에서 호출 할 수 없다는 점에서 클래스 메소드와 다르다. type.__subclasses__() 는 metaclass 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__



type () 함수는 객체의 유형을 반환하거나 새로운 유형을 만들 수 있습니다.

예를 들어, type () 함수로 Hi 클래스를 만들 수 있고 Hi (클래스) 클래스에서이 방법을 사용할 필요가 없습니다.

def func(self, name='mike'):
    print('Hi, %s.' % name)

Hi = type('Hi', (object,), dict(hi=func))
h = Hi()
h.hi()
Hi, mike.

type(Hi)
type

type(h)
__main__.Hi

클래스를 동적으로 생성하기 위해 type ()을 사용하는 것 외에도 클래스의 생성 동작을 제어하고 메타 클래스를 사용할 수 있습니다.

파이썬 객체 모델에 따르면 클래스는 객체이므로 클래스는 다른 특정 클래스의 인스턴스 여야합니다. 기본적으로 Python 클래스는 type 클래스의 인스턴스입니다. 즉, type은 대부분의 내장 클래스와 사용자 정의 클래스의 메타 클래스 인 메타 클래스입니다.

class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

class CustomList(list, metaclass=ListMetaclass):
    pass

lst = CustomList()
lst.add('custom_list_1')
lst.add('custom_list_2')

lst
['custom_list_1', 'custom_list_2']

Magic은 metaclass에서 키워드 인수를 전달할 때 적용됩니다. ListMetaclass를 통해 CustomList를 작성하는 Python 해석기를 나타냅니다. new ()는이 시점에서 예를 들어 클래스 정의를 수정하고 새 메서드를 추가 한 다음 수정 된 정의를 반환 할 수 있습니다.




나는 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 에서 보관 됨) https://web.archive.org/web/20080206005253/http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html )

즉, 클래스는 인스턴스 생성을위한 청사진이며, 메타 클래스는 클래스 생성을위한 청사진입니다. Python 클래스에서이 동작을 가능하게하는 퍼스트 클래스 객체가되어야한다는 것을 쉽게 알 수 있습니다.

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

말할 필요가있는 것은 : 만약 당신이 어떤 메타 클래스인지 모른다면, 당신이 필요로하지 않을 확률은 99 %입니다.




이 답변은 2008 년에 작성된 Python 2.x에 대한 것이며, 메타 클래스는 3.x에서 약간 다릅니다. 주석을 참고하십시오.

메타 클래스는 '수업'을 만드는 비밀스런 소스입니다. 새 스타일 객체의 기본 메타 클래스는 '유형'이라고합니다.

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'

그리고 실제로, 실제로 의미가있는 예제는 목록의 변수를 클래스에 자동으로 설정하고 None으로 설정합니다.

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

metaclass init_attributes 를 가짐으로써 'Initalised'이득을 얻는 마법 동작은 Initalised의 하위 클래스로 전달되지 않습니다.

클래스가 생성 될 때 액션을 수행하는 메타 클래스를 만들기 위해 '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



오브젝트로서의 클래스

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

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

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

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

그러나 클래스는 파이썬보다 더 중요합니다. 클래스도 객체입니다.

네, 물건들.

키워드 class 를 사용하자마자 Python은 그것을 실행하고 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>

동적으로 클래스 만들기

클래스는 객체이기 때문에 어떤 객체와 마찬가지로 즉석에서 작성할 수 있습니다.

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

>>> 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 은 클래스에 대한 설명을 매개 변수로 취하고 클래스를 반환 할 수 있습니다.

(필자가 알고있는 바, 동일한 함수가 전달한 매개 변수에 따라 완전히 다른 두 가지 용도를 가질 수 있다는 것은 바보 같은 생각입니다. 파이썬에서의 하위 호환성으로 인한 문제입니다)

type 은 다음과 같이 작동합니다.

type(name of the class,
     tuple of the parent class (for inheritance, can be empty),
     dictionary containing attributes names and values)

예 :

>>> 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 를 사용할 때 파이썬이하는 일이며, 메타 클래스를 사용하면됩니다.

메타 클래스 란 무엇입니까 (최종적으로)?

메타 클래스는 클래스를 만드는 '물건'입니다.

객체를 만들려면 클래스를 정의해야합니다.

그러나 우리는 Python 클래스가 객체라는 것을 알게되었습니다.

글쎄, 메타 클래스는 이러한 객체를 만드는 것입니다. 클래스의 클래스이므로 다음과 같이 그림을 그릴 수 있습니다.

MyClass = MetaClass()
my_object = MyClass()

type 사용하면 다음과 같은 작업을 수행 할 수 있습니다.

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

함수 type 이 실제로는 메타 클래스이기 때문입니다. type 은 모든 클래스를 만드는 데 사용되는 메타 클래스입니다.

이제 도대체 왜 소문자로 쓰여져 있고 Type 이 아닌지 궁금해하십니까?

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

__class__ 속성을 확인하여이를 볼 수 있습니다.

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

>>> 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 은 내장 된 메타 클래스 Python이 사용하지만, 물론 여러분 만의 메타 클래스를 만들 수 있습니다.

__metaclass__ 속성

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

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

그렇게하면 Python은 metaclass를 사용하여 Foo 클래스를 만듭니다.

조심해, 까다 롭다.

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

파이썬은 클래스 정의에서 __metaclass__ 을 찾는다. 발견되면 Foo 라는 객체 클래스를 만드는 데 사용합니다. 그렇지 않은 경우, 클래스를 작성하기 위해서 type 를 사용합니다.

몇 번 읽어보십시오.

할 일 :

class Foo(Bar):
    pass

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

Foo __metaclass__ 속성이 있습니까?

그렇다면 메모리에 클래스 객체 (클래스 객체를 말하고 여기에 머물러 라.)를 작성하고, Foo 라는 이름을 __metaclass__ 있는 것을 사용하여 만듭니다.

파이썬이 __metaclass__ 찾지 못하면, 모듈 수준에서 __metaclass__ 을 찾아서 똑같이하려고 시도합니다 (그러나 아무것도 상속하지 않는 클래스, 기본적으로 구식 클래스).

그런 다음 __metaclass__ 을 전혀 찾을 수 없으면 클래스 객체를 만들기 위해 Bar (첫 번째 상위) 자체 메타 클래스 (기본 type 이 될 수 있음)를 사용합니다.

여기에서 __metaclass__ 속성이 상속되지 않도록주의하십시오. 부모 ( Bar.__class__ )의 메타 클래스가 상속됩니다. Bartype() ( type.__new__() )이 아닌 Bar 를 만든 __metaclass__ 속성을 사용하면 하위 클래스는 해당 동작을 상속하지 않습니다.

이제 큰 질문은 __metaclass__ 무엇을 넣을 수 있는가입니다.

그 답은 클래스를 생성 할 수있는 것입니다.

무엇이 수업을 만들 수 있습니까? type 또는 하위 클래스 또는 사용하는 모든 것.

파이썬 3의 메타 클래스

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

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

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

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

맞춤 메타 클래스

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

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

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

이렇게하면이 모듈의 모든 클래스가이 메타 클래스를 사용하여 만들어지며 모든 속성을 대문자로 전환하도록 메타 클래스에 지시해야합니다.

운 좋게도, __metaclass__ 는 실제로 호출 가능한 클래스가 될 수 있습니다. 공식 클래스 일 필요는 없습니다. (이름에서 '클래스'가 클래스 일 필요는 없습니다. 그림이 있지만 도움이됩니다.)

그래서 우리는 함수를 사용하여 간단한 예제로 시작합니다.

# 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_attr):
    """
      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_attr = {}
    for name, val in future_class_attr.items():
        if not name.startswith('__'):
            uppercase_attr[name.upper()] = val
        else:
            uppercase_attr[name] = val

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

__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'

print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True

f = Foo()
print(f.BAR)
# Out: '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_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type(future_class_name, future_class_parents, uppercase_attr)

그러나 이것은 실제로 OOP가 아닙니다. 우리는 직접 type 호출하고 우리는 부모를 __new__ 대체하거나 호출하지 않습니다. 해보 죠.

class UpperAttrMetaclass(type):

    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        # reuse the type.__new__ method
        # this is basic OOP, nothing magic in there
        return type.__new__(upperattr_metaclass, future_class_name,
                            future_class_parents, uppercase_attr)

추가 인수 인 upperattr_metaclass 주목 upperattr_metaclass 입니다. 특별한 것은 없습니다 : __new__ 항상 정의 된 클래스를 첫 번째 매개 변수로받습니다. 인스턴스를 첫 번째 매개 변수로받는 일반 메서드 나 클래스 메서드의 정의 클래스에 대한 self 처럼.

물론 여기에 사용 된 이름은 명확성을 위해 오래 있지만, self 과 마찬가지로 모든 주장은 일반적인 이름을 사용합니다. 따라서 실제 제작 메타 클래스는 다음과 같습니다.

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type.__new__(cls, clsname, bases, uppercase_attr)

상속을 쉽게 할 수있는 super 를 사용하면 더 깨끗하게 만들 수 있습니다 (예, 메타 클래스를 가질 수 있고, 메타 클래스를 상속하고, 상속받을 수 있기 때문입니다).

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

그게 전부 야. 메타 클래스에 관해서는 실제로 아무것도 없습니다.

메타 클래스를 사용하는 코드의 복잡성에 대한 이유는 메타 클래스 때문이 아니라 인트로 __dict__ , 상속 조작, __dict__ 과 같은 변수 등을 사용하여 꼬인 물건을 만들기 위해 일반적으로 메타 클래스를 사용하기 때문입니다.

실제로, 메타 클래스는 흑 마술을하기에 특히 유용하며 따라서 복잡한 작업을 수행합니다. 하지만 그 자체로는 단순합니다.

  • 클래스 생성을 가로 채기
  • 수업을 수정하다
  • 수정 한 클래스를 돌려 준다

왜 함수 대신 메타 클래스를 사용합니까?

__metaclass__ 는 호출 가능을 받아 들일 수 있기 때문에 분명히 더 복잡하기 때문에 클래스를 사용하는 이유는 무엇입니까?

이렇게해야 할 몇 가지 이유가 있습니다.

  • 그 의도는 분명합니다. UpperAttrMetaclass(type) 을 읽을 때, 당신은 무엇을 따라야하는지 안다.
  • OOP를 사용할 수 있습니다. 메타 클래스는 메타 클래스를 상속하고 부모 메서드를 재정의 할 수 있습니다. 메타 클래스는 심지어 메타 클래스도 사용할 수 있습니다.
  • 메타 클래스 클래스를 지정한 경우 클래스의 하위 클래스는 해당 메타 클래스의 인스턴스이지만 메타 클래스 함수는 포함하지 않습니다.
  • 코드를 더 잘 구조화 할 수 있습니다. 위의 예제처럼 사소한 것으로는 결코 메타 클래스를 사용하지 마십시오. 대개 뭔가 복잡한 일이 있습니다. 하나의 클래스에서 여러 메소드를 만들고 그룹화하는 기능을 사용하면 코드를 읽기 쉽게 만들 수 있습니다.
  • __new__ , __init____call__ 에 연결할 수 있습니다. 그러면 다른 것들을 할 수있게됩니다. 일반적으로 __new__ 에서 모든 작업을 수행 할 수 있다고하더라도 일부 사람들은 __init__ 사용하는 것이 더 편합니다.
  • 이것들은 metaclasses라고 불 렸습니다. 젠장! 뭔가 의미가 있어야합니다!

왜 당신은 메타 클래스를 사용할 것인가?

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

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

메타 크릭은 99 %의 사용자가 걱정할 필요가없는 더 깊은 마술입니다. 당신이 필요로하는지 궁금하게 생각한다면, 실제로는 필요로하는 사람들은 그들이 필요하다는 것을 확실히 알고 있고, 이유에 대해 설명 할 필요가 없습니다.

파이썬 전문가 Tim Peters

메타 클래스의 주요 사용 사례는 API를 만드는 것입니다. 이것의 전형적인 예가 Django ORM입니다.

다음과 같이 정의 할 수 있습니다.

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

그러나 이렇게하면 :

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

IntegerField 객체를 반환하지 않습니다. 그것은 int 를 리턴 할 것이고 심지어 데이터베이스에서 직접 가져올 수도 있습니다.

이것은 models.Model__metaclass__ 정의하고 단순한 명령문으로 방금 정의한 Person 을 데이터베이스 필드에 대한 복잡한 후크로 변환시키는 일부 마법을 사용하기 때문에 가능합니다.

Django는 간단한 API를 노출하고 메타 클래스를 사용하여이 API의 코드를 다시 작성하여 복잡한 작업을 단순하게 처리합니다.

마지막 단어

첫째, 클래스는 인스턴스를 만들 수있는 객체라는 것을 알고 있습니다.

사실, 클래스 자체가 인스턴스입니다. 메타 클래스 중.

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

모든 것은 파이썬에서 하나의 객체이며, 모두 클래스 나 메타 클래스 인스턴스 중 하나입니다.

type 제외하고.

type 은 실제로 자체 metaclass입니다. 이것은 순수한 파이썬에서 재현 할 수있는 것이 아니며 구현 레벨에서 약간의 부정 행위로 끝납니다.

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

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

그러나 시간의 98 %, 당신은 클래스 변경이 전혀 필요하지 않습니다.




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

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

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

그러나 런타임에 Person 객체는 모든 유용한 메소드로 채워진다. 놀라운 metaclassery에 대한 source 를 참조하십시오.




메타 클래스 란 무엇입니까? 당신은 그들을 위해 무엇을 사용합니까?

TLDR : 메타 클래스 (metaclass)는 클래스가 클래스의 인스턴스를 인스턴스화하고 인스턴스의 동작을 정의하는 것처럼 클래스의 동작을 인스턴스화하고 정의합니다.

의사 코드 :

>>> 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'>
    

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

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

그 (것)들을 위해 무엇을 사용할 수 있습니까? docs :

메타 클래스의 잠재 용도는 무한합니다. 탐색 된 몇 가지 아이디어로는 로깅, 인터페이스 확인, 자동 위임, 자동 속성 만들기, 프록시, 프레임 워크 및 자동 리소스 잠금 / 동기화가 있습니다.

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

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

클래스 정의를 작성할 때 (예 : this)

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__ 정의 __slots__ - __slots__ 을 정의 하면 공간을 제외 __weakref__ __dict____weakref__ 을 (를) 제외하여 허용 할 수 있습니다. 예를 들면 다음과 같습니다.

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

...하지만 나는 빗 나간다.)

다른 클래스 정의와 마찬가지로 type 을 확장 할 수 있습니다.

다음은 클래스의 기본 __repr__ 입니다.

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

파이썬 객체를 작성할 때 우리가 기본적으로 할 수있는 가장 소중한 것들 중 하나는 좋은 __repr__ 을 제공하는 것입니다. help(repr) 을 요청할 때 help(repr) 우리는 equality - obj == eval(repr(obj)) 대한 테스트가 필요한 __repr__ 대한 좋은 테스트가 있음을 알게됩니다. 우리의 타입 클래스의 클래스 인스턴스에 대한 __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')

이 예제는 docs된 것입니다. 표준 라이브러리 의 새로운 enum 이 이것을합니다.

그래서 우리가 한 것은 클래스를 생성하여 메타 클래스를 인스턴스화하는 것입니다. 우리는 다른 클래스처럼 메타 클래스도 처리 할 수 ​​있습니다. 메서드 확인 순서가 있습니다.

>>> 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>})



__call__()클래스 인스턴스를 만들 때 metaclass 메서드의 역할

파이썬 프로그래밍을 몇 달 이상 해왔다면 다음과 같은 코드가 우연히 발견됩니다.

# define a class
class SomeClass(object):
    # ...
    # some definition here ...
    # ...

# create an instance of it
instance = SomeClass()

# then call the object as if it's a function
result = instance('foo', 'bar')

후자는 __call__()클래스에 마법 메서드 를 구현할 때 가능합니다 .

class SomeClass(object):
    # ...
    # some definition here ...
    # ...

    def __call__(self, foo, bar):
        return bar + foo

__call__()메소드는 클래스의 인스턴스가 호출 가능 객체로 사용될 때 호출됩니다. 그러나 이전 답변에서 보았 듯이 클래스 자체는 메타 클래스의 인스턴스이므로 클래스를 호출 가능 클래스로 사용할 때 (즉 인스턴스를 만들 때) 실제로는 메타 클래스 __call__()메서드를 호출합니다 . 이 시점에서 대부분의 파이썬 프로그래머는 약간의 혼란을 겪 instance = SomeClass()습니다 __init__(). 조금 더 깊이 파고했습니다 어떤 사람들은 전에 알고 __init__()있다 __new__(). 음, 오늘은 진실의 또 다른 층은 전에 공개되는 __new__()'메타 클래스있다 __call__().

클래스의 인스턴스를 만드는 관점에서 메서드 호출 체인을 연구 해 봅시다.

이것은 인스턴스가 생성되기 전의 순간과 반환 할 순간을 정확히 기록하는 메타 클래스입니다.

class Meta_1(type):
    def __call__(cls):
        print "Meta_1.__call__() before creating an instance of ", cls
        instance = super(Meta_1, cls).__call__()
        print "Meta_1.__call__() about to return instance."
        return instance

이 메타 클래스를 사용하는 클래스입니다.

class Class_1(object):

    __metaclass__ = Meta_1

    def __new__(cls):
        print "Class_1.__new__() before creating an instance."
        instance = super(Class_1, cls).__new__(cls)
        print "Class_1.__new__() about to return instance."
        return instance

    def __init__(self):
        print "entering Class_1.__init__() for instance initialization."
        super(Class_1,self).__init__()
        print "exiting Class_1.__init__()."

이제 인스턴스를 만듭니다. Class_1

instance = Class_1()
# Meta_1.__call__() before creating an instance of <class '__main__.Class_1'>.
# Class_1.__new__() before creating an instance.
# Class_1.__new__() about to return instance.
# entering Class_1.__init__() for instance initialization.
# exiting Class_1.__init__().
# Meta_1.__call__() about to return instance.

위의 코드는 실제로 작업을 로깅하는 것 이상을 수행하지 않습니다. 각 메소드는 실제 작업을 상위 구현에 위임하므로 기본 동작을 유지합니다. 이후 typeIS Meta_1의 상위 클래스 ( type기본 부모 메타 클래스 인) 이상 출력의 순서 순서를 고려, 우리는 지금의 의사 구현 될 사항에 대한 단서를 가지고 type.__call__():

class type:
    def __call__(cls, *args, **kwarg):

        # ... maybe a few things done to cls here

        # then we call __new__() on the class to create an instance
        instance = cls.__new__(cls, *args, **kwargs)

        # ... maybe a few things done to the instance here

        # then we initialize the instance with its __init__() method
        instance.__init__(*args, **kwargs)

        # ... maybe a few more things done to instance here

        # then we return it
        return instance

우리는 메타 클래스의 __call__()방법이 처음이라는 것을 볼 수 있습니다 . 그런 다음 인스턴스 생성을 클래스의 __new__()메서드에 위임 하고 인스턴스의 초기화를 위임 __init__()합니다. 또한 인스턴스를 궁극적으로 반환하는 인스턴스이기도합니다.

위에서 그것은 메타 클래스는 '고 줄기 __call__()도 여부를 전화를 결정할 수있는 기회를 부여 Class_1.__new__()하거나 Class_1.__init__()결국 될 것이다. 실행 과정에서 실제로 이러한 메서드 중 하나에서 다루지 않은 개체를 반환 할 수 있습니다. 싱글 톤 패턴에 대한이 접근법을 예로 들어 보겠습니다.

class Meta_2(type):
    singletons = {}

    def __call__(cls, *args, **kwargs):
        if cls in Meta_2.singletons:
            # we return the only instance and skip a call to __new__()
            # and __init__()
            print ("{} singleton returning from Meta_2.__call__(), "
                   "skipping creation of new instance.".format(cls))
            return Meta_2.singletons[cls]

        # else if the singleton isn't present we proceed as usual
        print "Meta_2.__call__() before creating an instance."
        instance = super(Meta_2, cls).__call__(*args, **kwargs)
        Meta_2.singletons[cls] = instance
        print "Meta_2.__call__() returning new instance."
        return instance

class Class_2(object):

    __metaclass__ = Meta_2

    def __new__(cls, *args, **kwargs):
        print "Class_2.__new__() before creating instance."
        instance = super(Class_2, cls).__new__(cls)
        print "Class_2.__new__() returning instance."
        return instance

    def __init__(self, *args, **kwargs):
        print "entering Class_2.__init__() for initialization."
        super(Class_2, self).__init__()
        print "exiting Class_2.__init__()."

반복적으로 타입의 객체를 만들려고 할 때 어떤 일이 일어나는지 관찰 해 봅시다. Class_2

a = Class_2()
# Meta_2.__call__() before creating an instance.
# Class_2.__new__() before creating instance.
# Class_2.__new__() returning instance.
# entering Class_2.__init__() for initialization.
# exiting Class_2.__init__().
# Meta_2.__call__() returning new instance.

b = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

c = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

a is b is c # True



tl; dr 버전

type(obj)함수는 객체의 유형을 가져옵니다.

type()클래스의 그것입니다 메타 클래스 .

메타 클래스를 사용하려면 다음을 수행하십시오.

class Foo(object):
    __metaclass__ = MyMetaClass



게시 된 답변 외에도 metaclass클래스에 대한 동작을 정의 한다고 말할 수 있습니다 . 따라서 명시 적으로 메타 클래스를 설정할 수 있습니다. 파이썬이 키워드 class를 얻을 때마다 metaclass. 발견되지 않으면 기본 메타 클래스 유형을 사용하여 클래스의 객체를 만듭니다. 이 __metaclass__속성을 사용 metaclass하여 클래스를 설정할 수 있습니다 .

class MyClass:
   __metaclass__ = type
   # write here other method
   # write here one more method

print(MyClass.__metaclass__)

다음과 같은 출력을 생성합니다.

class 'type'

물론 metaclass클래스를 사용하여 생성 된 모든 클래스의 비헤이비어를 정의 하기 위해 자신 만의 클래스를 만들 수 있습니다.

이를 위해 기본 metaclass유형 클래스가 상속되어야 합니다. 기본 클래스는 기본 클래스입니다 metaclass.

class MyMetaClass(type):
   __metaclass__ = type
   # you can write here any behaviour you want

class MyTestClass:
   __metaclass__ = MyMetaClass

Obj = MyTestClass()
print(Obj.__metaclass__)
print(MyMetaClass.__metaclass__)

출력은 다음과 같습니다.

class '__main__.MyMetaClass'
class 'type'



파이썬 3 업데이트

메타 클래스에는 두 가지 주요 방법이 있습니다 (이 시점에서).

  • __prepare__ , 및
  • __new__

__prepare__OrderedDict클래스를 만들 때 네임 스페이스로 사용할 사용자 지정 매핑 (예 :)을 제공 할 수 있습니다 . 선택한 네임 스페이스의 인스턴스를 반환해야합니다. 구현하지 않으면 __prepare__정상 dict을 사용합니다.

__new__ 최종 수업의 실제 생성 / 수정을 담당합니다.

맨손으로, 아무 것도 할 수없는 여분의 메타 클래스는 다음을 원합니다.

class Meta(type):

    def __prepare__(metaclass, cls, bases):
        return dict()

    def __new__(metacls, cls, bases, clsdict):
        return super().__new__(metacls, cls, bases, clsdict)

간단한 예 :

그것은 항상해야처럼 - 당신은 몇 가지 간단한 검증 코드가 속성에 실행하려는 말 int또는를 str. 메타 클래스가 없다면 수업은 다음과 같이 보일 것입니다.

class Person:
    weight = ValidateType('weight', int)
    age = ValidateType('age', int)
    name = ValidateType('name', str)

보시다시피 속성 이름을 두 번 반복해야합니다. 이것은 자극적 인 버그와 함께 오타를 가능하게합니다.

간단한 메타 클래스를 사용하면이 문제를 해결할 수 있습니다.

class Person(metaclass=Validator):
    weight = ValidateType(int)
    age = ValidateType(int)
    name = ValidateType(str)

이것은 메타 클래스가 다음과 같이 보일 것입니다 (필요하지 않으므로 사용 __prepare__하지 않음).

class Validator(type):
    def __new__(metacls, cls, bases, clsdict):
        # search clsdict looking for ValidateType descriptors
        for name, attr in clsdict.items():
            if isinstance(attr, ValidateType):
                attr.name = name
                attr.attr = '_' + name
        # create final class and return it
        return super().__new__(metacls, cls, bases, clsdict)

샘플 실행 :

p = Person()
p.weight = 9
print(p.weight)
p.weight = '9'

생산 :

9
Traceback (most recent call last):
  File "simple_meta.py", line 36, in <module>
    p.weight = '9'
  File "simple_meta.py", line 24, in __set__
    (self.name, self.type, value))
TypeError: weight must be of type(s) <class 'int'> (got '9')

참고 :이 예제는 클래스 데코레이터로도 수행 할 수있을만큼 간단하지만 실제 메타 클래스는 훨씬 더 많은 작업을 수행 할 것입니다.

참조 용 'ValidateType'클래스 :

class ValidateType:
    def __init__(self, type):
        self.name = None  # will be set by metaclass
        self.attr = None  # will be set by metaclass
        self.type = type
    def __get__(self, inst, cls):
        if inst is None:
            return self
        else:
            return inst.__dict__[self.attr]
    def __set__(self, inst, value):
        if not isinstance(value, self.type):
            raise TypeError('%s must be of type(s) %s (got %r)' %
                    (self.name, self.type, value))
        else:
            inst.__dict__[self.attr] = value



문서에서 :

조건식 ( "삼항 연산자"라고도 함)은 모든 파이썬 연산 중에서 우선 순위가 가장 낮습니다.

x if C else y 먼저 조건 C ( x가 아님)를 계산합니다. C 가 true이면 x 가 계산되고 그 값이 반환됩니다. 그렇지 않으면 y 가 평가되고 그 값이 리턴됩니다.

조건식에 대한 자세한 내용은 PEP 308 을 참조하십시오.

버전 2.5부터 새로 추가되었습니다.





python oop metaclass python-datamodel