python 메타프로그래밍 c++ - 파이썬에서 메타 클래스 란 무엇입니까?





7 Answers

오브젝트로서의 클래스

메타 클래스를 이해하기 전에 파이썬에서 클래스를 마스터해야합니다. 파이썬은 스몰 토크 (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 %, 당신은 클래스 변경이 전혀 필요하지 않습니다.

메타클래스 object 내부

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




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

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

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

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




나는 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 %입니다.




파이썬 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



메타 클래스는 다른 클래스가 어떻게 생성되어야하는지 알려주는 클래스입니다.

이것은 내가 내 문제에 대한 해결책으로 메타 클래스를 보았던 경우이다. 나는 정말로 복잡한 문제가 있었지만 아마도 다르게 풀릴 수 있었지만 나는 메타 클래스를 사용하여 그것을 해결하기로 결정했다. 복잡성 때문에 모듈에있는 주석이 작성된 코드의 양을 능가하는 곳에서 필자가 작성한 소수의 모듈 중 하나입니다. 여기있어...

#!/usr/bin/env python

# Copyright (C) 2013-2014 Craig Phillips.  All rights reserved.

# This requires some explaining.  The point of this metaclass excercise is to
# create a static abstract class that is in one way or another, dormant until
# queried.  I experimented with creating a singlton on import, but that did
# not quite behave how I wanted it to.  See now here, we are creating a class
# called GsyncOptions, that on import, will do nothing except state that its
# class creator is GsyncOptionsType.  This means, docopt doesn't parse any
# of the help document, nor does it start processing command line options.
# So importing this module becomes really efficient.  The complicated bit
# comes from requiring the GsyncOptions class to be static.  By that, I mean
# any property on it, may or may not exist, since they are not statically
# defined; so I can't simply just define the class with a whole bunch of
# properties that are @property @staticmethods.
#
# So here's how it works:
#
# Executing 'from libgsync.options import GsyncOptions' does nothing more
# than load up this module, define the Type and the Class and import them
# into the callers namespace.  Simple.
#
# Invoking 'GsyncOptions.debug' for the first time, or any other property
# causes the __metaclass__ __getattr__ method to be called, since the class
# is not instantiated as a class instance yet.  The __getattr__ method on
# the type then initialises the class (GsyncOptions) via the __initialiseClass
# method.  This is the first and only time the class will actually have its
# dictionary statically populated.  The docopt module is invoked to parse the
# usage document and generate command line options from it.  These are then
# paired with their defaults and what's in sys.argv.  After all that, we
# setup some dynamic properties that could not be defined by their name in
# the usage, before everything is then transplanted onto the actual class
# object (or static class GsyncOptions).
#
# Another piece of magic, is to allow command line options to be set in
# in their native form and be translated into argparse style properties.
#
# Finally, the GsyncListOptions class is actually where the options are
# stored.  This only acts as a mechanism for storing options as lists, to
# allow aggregation of duplicate options or options that can be specified
# multiple times.  The __getattr__ call hides this by default, returning the
# last item in a property's list.  However, if the entire list is required,
# calling the 'list()' method on the GsyncOptions class, returns a reference
# to the GsyncListOptions class, which contains all of the same properties
# but as lists and without the duplication of having them as both lists and
# static singlton values.
#
# So this actually means that GsyncOptions is actually a static proxy class...
#
# ...And all this is neatly hidden within a closure for safe keeping.
def GetGsyncOptionsType():
    class GsyncListOptions(object):
        __initialised = False

    class GsyncOptionsType(type):
        def __initialiseClass(cls):
            if GsyncListOptions._GsyncListOptions__initialised: return

            from docopt import docopt
            from libgsync.options import doc
            from libgsync import __version__

            options = docopt(
                doc.__doc__ % __version__,
                version = __version__,
                options_first = True
            )

            paths = options.pop('<path>', None)
            setattr(cls, "destination_path", paths.pop() if paths else None)
            setattr(cls, "source_paths", paths)
            setattr(cls, "options", options)

            for k, v in options.iteritems():
                setattr(cls, k, v)

            GsyncListOptions._GsyncListOptions__initialised = True

        def list(cls):
            return GsyncListOptions

        def __getattr__(cls, name):
            cls.__initialiseClass()
            return getattr(GsyncListOptions, name)[-1]

        def __setattr__(cls, name, value):
            # Substitut option names: --an-option-name for an_option_name
            import re
            name = re.sub(r'^__', "", re.sub(r'-', "_", name))
            listvalue = []

            # Ensure value is converted to a list type for GsyncListOptions
            if isinstance(value, list):
                if value:
                    listvalue = [] + value
                else:
                    listvalue = [ None ]
            else:
                listvalue = [ value ]

            type.__setattr__(GsyncListOptions, name, listvalue)

    # Cleanup this module to prevent tinkering.
    import sys
    module = sys.modules[__name__]
    del module.__dict__['GetGsyncOptionsType']

    return GsyncOptionsType

# Our singlton abstract proxy class.
class GsyncOptions(object):
    __metaclass__ = GetGsyncOptionsType()



tl; dr 버전

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

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

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

class Foo(object):
    __metaclass__ = MyMetaClass



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 ()는이 시점에서 예를 들어 클래스 정의를 수정하고 새 메서드를 추가 한 다음 수정 된 정의를 반환 할 수 있습니다.




Related