etc./StackOverFlow

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

청렴결백한 만능 재주꾼 2021. 9. 22. 13:27
반응형

질문자 :e-satis


파이썬에서 메타클래스는 무엇이며 무엇을 위해 사용합니까?



답변자 : Community Wiki


객체로서의 클래스

메타클래스를 이해하기 전에 Python의 클래스를 마스터해야 합니다. 그리고 Python은 Smalltalk 언어에서 차용한 클래스가 무엇인지에 대한 매우 독특한 아이디어를 가지고 있습니다.

대부분의 언어에서 클래스는 객체를 생성하는 방법을 설명하는 코드 조각일 뿐입니다. 그것은 Python에서도 사실입니다.

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

그러나 클래스는 Python에서 그 이상입니다. 클래스도 객체입니다.

예, 개체입니다.

class 키워드를 사용하자마자 파이썬은 그것을 실행하고 객체를 생성합니다. 지시

 >>> 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이 이 객체를 자동으로 생성합니다. 그러나 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

우리가 어디로 가고 있는지 알 수 있습니다. Python에서 클래스는 객체이며 동적으로 즉석에서 클래스를 생성할 수 있습니다.

class 를 사용할 때 Python이 하는 일이며 메타클래스를 사용하여 수행합니다.

메타 클래스 란 무엇입니까 (마지막으로)

메타클래스는 클래스를 생성하는 '물건'입니다.

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

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

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

 MyClass = MetaClass() my_object = MyClass()

다음과 같은 작업을 수행할 수 있는 type 보았습니다.

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

type 이 실제로 메타클래스이기 때문입니다. type 은 Python이 배후에서 모든 클래스를 생성하는 데 사용하는 메타 클래스입니다.

이제 "도대체 왜 Type 아닌 소문자로 작성되었습니까?"

str , string형 객체를 생성하는 클래스, 정수 객체를 생성하는 int 클래스와의 일관성 문제인 것 같습니다. type 은 클래스 객체를 생성하는 클래스일 뿐입니다.

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

모든 것, 내가 의미하는 모든 것은 Python의 객체입니다. 여기에는 정수, 문자열, 함수 및 클래스가 포함됩니다. 모두 객체입니다. 그리고 그들 모두는 클래스에서 생성되었습니다.

 >>> 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__ 속성

__metaclass__ 속성을 추가할 수 있습니다(Python 3 구문은 다음 섹션 참조).

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

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

조심해, 까다롭다.

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

Python은 클래스 정의에서 __metaclass__ 찾으면 이를 사용하여 객체 클래스 Foo 를 생성합니다. 그렇지 않은 경우 type 을 사용하여 클래스를 생성합니다.

그것을 여러 번 읽으십시오.

당신이 할 때:

 class Foo(Bar): pass

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

Foo __metaclass__ 속성이 있습니까?

__metaclass__ 있는 것을 사용하여 Foo 클래스 객체를 메모리에 생성합니다(클래스 객체라고 했습니다. 여기에서 함께 하세요).

__metaclass__ 찾을 수 없으면 __metaclass__ 를 찾고 동일한 작업을 시도합니다(그러나 기본적으로 구식 클래스를 상속하지 않는 클래스에만 해당).

__metaclass__ 를 전혀 찾을 수 없으면 Bar 의 (첫 번째 부모) 자체 메타클래스(기본 type 수 있음)를 사용하여 클래스 객체를 생성합니다.

__metaclass__ 속성은 상속되지 않고 부모의 메타클래스( Bar.__class__ )가 상속된다는 점에 주의하세요. Bartype() ( type.__new__() 아님)을 사용하여 Bar 를 생성 __metaclass__ 속성을 사용한 경우 하위 클래스는 해당 동작을 상속하지 않습니다.

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

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

그리고 클래스를 만들 수 있는 것은 무엇입니까? type , 또는 그것을 서브클래싱하거나 사용하는 모든 것.

Python 3의 메타클래스

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

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

즉, 기본 클래스 목록의 키워드 인수를 위해 __metaclass__

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

Python 3의 메타클래스에 추가된 한 가지는 다음과 같이 속성을 키워드 인수로 메타클래스에 전달할 수도 있다는 것입니다.

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

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

커스텀 메타클래스

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

일반적으로 현재 컨텍스트와 일치하는 클래스를 생성하려는 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_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)

아, 그리고 Python 3에서 다음과 같이 키워드 인수를 사용하여 이 호출을 수행하면:

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

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

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

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

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

실제로 메타클래스는 흑마법, 따라서 복잡한 작업을 수행하는 데 특히 유용합니다. 그러나 그 자체로는 간단합니다.

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

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

__metaclass__ 는 모든 콜러블을 허용할 수 있으므로 분명히 더 복잡하기 때문에 클래스를 사용하는 이유는 무엇입니까?

그렇게 하는 데에는 몇 가지 이유가 있습니다.

  • 의도는 분명합니다. UpperAttrMetaclass(type) 을 읽을 때 다음 내용이 무엇인지 알 수 있습니다.
  • OOP를 사용할 수 있습니다. 메타클래스는 메타클래스에서 상속하고 상위 메소드를 재정의할 수 있습니다. 메타클래스는 메타클래스를 사용할 수도 있습니다.
  • metaclass-class를 지정했지만 metaclass-function이 아닌 경우 클래스의 하위 클래스는 해당 메타클래스의 인스턴스가 됩니다.
  • 코드를 더 잘 구성할 수 있습니다. 위의 예와 같이 사소한 것에 메타 클래스를 사용하지 마십시오. 일반적으로 복잡한 작업에 사용됩니다. 여러 메서드를 만들고 하나의 클래스로 그룹화하는 기능은 코드를 읽기 쉽게 만드는 데 매우 유용합니다.
  • __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 은 실제로 자체 메타 클래스입니다. 이것은 순수한 Python에서 재현할 수 있는 것이 아니며 구현 수준에서 약간의 속임수를 사용하여 수행됩니다.

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

클래스 변경이 필요한 경우의 99%는 이를 사용하는 것이 좋습니다.

그러나 98%의 경우 클래스 변경이 전혀 필요하지 않습니다.



답변자 : Thomas Wouters


메타 클래스는 클래스의 클래스입니다. 클래스는 클래스의 인스턴스(즉, 객체)가 어떻게 동작하는지 정의하는 반면 메타클래스는 클래스가 동작하는 방식을 정의합니다. 클래스는 메타클래스의 인스턴스입니다.

Python에서는 Jerub이 보여주는 것처럼 메타클래스에 임의의 호출 가능 항목을 사용할 수 있지만 더 나은 접근 방식은 실제 클래스 자체로 만드는 것입니다. type 은 Python의 일반적인 메타 클래스입니다. type 자체가 클래스이며 자체 유형입니다. type 과 같은 것을 다시 만들 수는 없지만 파이썬은 약간의 속임수를 사용합니다. 파이썬에서 자신만의 메타클래스를 만들려면 정말로 type 을 서브클래스로 만들고 싶을 뿐입니다.

메타클래스는 가장 일반적으로 클래스 팩토리로 사용됩니다. 클래스를 호출하여 객체를 생성할 때, 파이썬은 메타클래스를 호출하여 ('class' 문을 실행할 때) 새로운 클래스를 생성합니다. 따라서 일반 __init____new__ 메서드와 결합된 메타클래스를 사용하면 새 클래스를 레지스트리에 등록하거나 클래스를 완전히 다른 것으로 교체하는 것과 같이 클래스를 생성할 때 '추가 작업'을 수행할 수 있습니다.

class 문이 실행되면 Python은 먼저 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__


답변자 : Jerub


참고로 이 답변은 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'

이제 실제로 의미가 있는 예제는 자동으로 목록의 변수를 클래스에 설정된 "속성"으로 만들고 없음으로 설정합니다.

 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 .

다음은 클래스가 생성될 때 작업을 수행하는 메타클래스를 만들기 위해 '유형'을 하위 클래스로 만드는 방법을 보여주는 훨씬 더 구체적인 예입니다. 이것은 상당히 까다롭습니다:

 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


답변자 : kindall


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

 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



답변자 : Antti Rasinen


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

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

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

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



답변자 : Matthias Kestenholz


나는 메타클래스 프로그래밍에 대한 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 )

간단히 말해서, 클래스는 인스턴스 생성을 위한 청사진이고, 메타클래스는 클래스 생성을 위한 청사진입니다. Python 클래스에서 이 동작을 활성화하려면 일급 객체도 필요하다는 것을 쉽게 알 수 있습니다.

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

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



답변자 : Aaron Hall


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

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

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

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

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

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

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

클래스를 생성할 때마다 메타클래스를 사용합니다.

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

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

클래스의 내용에 (A 측 참고 __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'>

파이썬 객체를 작성할 때 기본적으로 할 수 있는 가장 가치 있는 일 중 하나는 좋은 __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를 제공할 수 있습니다. Python 3에서 구현된 경우 클래스에 대한 네임스페이스 dict를 반환하는 __prepare__ 를 사용하여 이 작업을 수행합니다.

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


답변자 : Ethan Furman


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


답변자 : Michael Ekoka


클래스 인스턴스 생성 시 메타클래스의 __call__()

몇 개월 이상 Python 프로그래밍을 했다면 결국 다음과 같은 코드를 발견하게 될 것입니다.

 # 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__() 메서드를 호출합니다. . 이 시점에서 대부분의 Python 프로그래머는 이 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의 인스턴스를 생성해 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.

위의 코드는 실제로 작업을 기록하는 것 이상을 수행하지 않습니다. 각 메서드는 실제 작업을 부모의 구현에 위임하므로 기본 동작을 유지합니다. typeMeta_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


답변자 : Community Wiki


메타클래스는 (일부) 다른 클래스를 생성하는 방법을 알려주는 클래스입니다.

이것은 내가 메타클래스를 내 문제에 대한 해결책으로 본 경우입니다. 나는 정말 복잡한 문제가 있었고 아마도 다르게 풀릴 수 있었지만 메타클래스를 사용하여 해결하기로 선택했습니다. 복잡성 때문에 모듈의 주석이 작성된 코드의 양을 능가하는 몇 안 되는 모듈 중 하나입니다. 여기있어...

 #!/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()


답변자 : noɥʇʎԀʎzɐɹƆ


tl;dr 버전

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

클래스의 type() 은 해당 메타클래스 입니다.

메타클래스를 사용하려면:

 class Foo(object): __metaclass__ = MyMetaClass

type 은 자체 메타 클래스입니다. 클래스의 클래스는 메타클래스입니다. 클래스의 본문은 클래스를 구성하는 데 사용되는 메타클래스에 전달된 인수입니다.

여기에서 메타클래스를 사용하여 클래스 구성을 사용자 정의하는 방법에 대해 읽을 수 있습니다.



답변자 : Mushahid Khan


type 은 실제로 metaclass 클래스입니다. 다른 클래스를 생성하는 클래스입니다. 대부분의 metaclass type 의 서브클래스입니다. metaclass new 클래스를 첫 번째 인수로 수신하고 아래에 언급된 세부 정보와 함께 클래스 개체에 대한 액세스를 제공합니다.

 >>> class MetaClass(type): ... def __init__(cls, name, bases, attrs): ... print ('class name: %s' %name ) ... print ('Defining class %s' %cls) ... print('Bases %s: ' %bases) ... print('Attributes') ... for (name, value) in attrs.items(): ... print ('%s :%r' %(name, value)) ... >>> class NewClass(object, metaclass=MetaClass): ... get_choch='dairy' ... class name: NewClass Bases <class 'object'>: Defining class <class 'NewClass'> get_choch :'dairy' __module__ :'builtins' __qualname__ :'NewClass'

Note:

클래스는 어떤 경우에도 인스턴스화되지 않았습니다. 클래스를 생성하는 간단한 행위가 metaclass 실행을 촉발시켰습니다.



답변자 : Xingzhou Liu


파이썬 클래스는 그 자체가 메타 클래스의 객체입니다.

클래스를 다음과 같이 결정할 때 적용되는 기본 메타클래스:

 class foo: ...

메타 클래스는 전체 클래스 세트에 일부 규칙을 적용하는 데 사용됩니다. 예를 들어 데이터베이스에 액세스하기 위해 ORM을 구축하고 각 테이블의 레코드가 해당 테이블에 매핑되는 클래스(필드, 비즈니스 규칙 등을 기반으로 함)가 되기를 원한다고 가정해 보겠습니다. 예를 들어 모든 테이블의 모든 레코드 클래스가 공유하는 연결 풀 논리입니다. 또 다른 용도는 여러 레코드 클래스를 포함하는 외래 키를 지원하는 논리입니다.

메타 클래스를 정의할 때 유형을 하위 클래스로 지정하고 논리를 삽입하기 위해 다음 매직 메서드를 재정의할 수 있습니다.

 class somemeta(type): __new__(mcs, name, bases, clsdict): """ mcs: is the base metaclass, in this case type. name: name of the new class, as provided by the user. bases: tuple of base classes clsdict: a dictionary containing all methods and attributes defined on class you must return a class object by invoking the __new__ constructor on the base metaclass. ie: return type.__call__(mcs, name, bases, clsdict). in the following case: class foo(baseclass): __metaclass__ = somemeta an_attr = 12 def bar(self): ... @classmethod def foo(cls): ... arguments would be : ( somemeta, "foo", (baseclass, baseofbase,..., object), {"an_attr":12, "bar": <function>, "foo": <bound class method>} you can modify any of these values before passing on to type """ return type.__call__(mcs, name, bases, clsdict) def __init__(self, name, bases, clsdict): """ called after type has been created. unlike in standard classes, __init__ method cannot modify the instance (cls) - and should be used for class validaton. """ pass def __prepare__(): """ returns a dict or something that can be used as a namespace. the type will then attach methods and attributes from class definition to it. call order : somemeta.__new__ -> type.__new__ -> type.__init__ -> somemeta.__init__ """ return dict() def mymethod(cls): """ works like a classmethod, but for class objects. Also, my method will not be visible to instances of cls. """ pass

어쨌든, 그 두 가지는 가장 일반적으로 사용되는 후크입니다. 메타클래싱은 강력하며, 위에는 메타클래싱의 완전한 용도 목록이 없습니다.



답변자 : binbjz


type() 함수는 객체의 유형을 반환하거나 새 유형을 생성할 수 있습니다.

예를 들어 type() 함수를 사용하여 Hi 클래스를 만들 수 있으며 Hi(object) 클래스와 함께 이 방법을 사용할 필요가 없습니다.

 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 객체 모델에 따르면 클래스는 객체이므로 클래스는 다른 특정 클래스의 인스턴스여야 합니다. 기본적으로 Python 클래스는 유형 클래스의 인스턴스입니다. 즉, 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은 메타클래스에서 키워드 인수를 전달할 때 적용되며, Python 인터프리터가 ListMetaclass를 통해 CustomList를 생성하도록 나타냅니다. new () 이 시점에서 예를 들어 클래스 정의를 수정하고 새 메서드를 추가한 다음 수정된 정의를 반환할 수 있습니다.



답변자 : Andy Fedoroff


게시된 답변 외에도 metaclass 가 클래스의 동작을 정의한다고 말할 수 있습니다. 따라서 메타클래스를 명시적으로 설정할 수 있습니다. Python이 키워드 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'


답변자 : Lars


파이썬 3.6에서 새로운 dunder 메서드 __init_subclass__(cls, **kwargs) 가 도입되어 메타클래스에 대한 많은 일반적인 사용 사례를 대체했습니다. Is는 정의하는 클래스의 하위 클래스가 생성될 때 호출됩니다. 파이썬 문서를 참조하십시오.



답변자 : Venu Gopal Tewari


객체 지향 프로그래밍에서 메타 클래스는 인스턴스가 클래스인 클래스입니다. 일반 클래스가 특정 개체의 동작을 정의하는 것처럼 메타 클래스는 특정 클래스 및 해당 인스턴스의 동작을 정의합니다. 메타 클래스라는 용어는 단순히 클래스를 생성하는 데 사용되는 것을 의미합니다. 즉, 클래스입니다. 메타클래스는 클래스를 생성하는 데 사용되므로 객체가 클래스의 인스턴스인 것처럼 클래스는 메타클래스의 인스턴스입니다. 파이썬에서는 클래스도 객체로 간주됩니다.



답변자 : Carson


다음은 사용할 수 있는 또 다른 예입니다.

  • metaclass 를 사용하여 인스턴스(클래스)의 기능을 변경할 수 있습니다.
 class MetaMemberControl(type): __slots__ = () @classmethod def __prepare__(mcs, f_cls_name, f_cls_parents, # f_cls means: future class meta_args=None, meta_options=None): # meta_args and meta_options is not necessarily needed, just so you know. f_cls_attr = dict() if not "do something or if you want to define your cool stuff of dict...": return dict(make_your_special_dict=None) else: return f_cls_attr def __new__(mcs, f_cls_name, f_cls_parents, f_cls_attr, meta_args=None, meta_options=None): original_getattr = f_cls_attr.get('__getattribute__') original_setattr = f_cls_attr.get('__setattr__') def init_getattr(self, item): if not item.startswith('_'): # you can set break points at here alias_name = '_' + item if alias_name in f_cls_attr['__slots__']: item = alias_name if original_getattr is not None: return original_getattr(self, item) else: return super(eval(f_cls_name), self).__getattribute__(item) def init_setattr(self, key, value): if not key.startswith('_') and ('_' + key) in f_cls_attr['__slots__']: raise AttributeError(f"you can't modify private members:_{key}") if original_setattr is not None: original_setattr(self, key, value) else: super(eval(f_cls_name), self).__setattr__(key, value) f_cls_attr['__getattribute__'] = init_getattr f_cls_attr['__setattr__'] = init_setattr cls = super().__new__(mcs, f_cls_name, f_cls_parents, f_cls_attr) return cls class Human(metaclass=MetaMemberControl): __slots__ = ('_age', '_name') def __init__(self, name, age): self._name = name self._age = age def __getattribute__(self, item): """ is just for IDE recognize. """ return super().__getattribute__(item) """ with MetaMemberControl then you don't have to write as following @property def name(self): return self._name @property def age(self): return self._age """ def test_demo(): human = Human('Carson', 27) # human.age = 18 # you can't modify private members:_age <-- this is defined by yourself. # human.k = 18 # 'Human' object has no attribute 'k' <-- system error. age1 = human._age # It's OK, although the IDE will show some warnings. (Access to a protected member _age of a class) age2 = human.age # It's OK! see below: """ if you do not define `__getattribute__` at the class of Human, the IDE will show you: Unresolved attribute reference 'age' for class 'Human' but it's ok on running since the MetaMemberControl will help you. """ if __name__ == '__main__': test_demo()

metaclass 는 강력하고, (원숭이 마법과 같은) 할 수 있는 많은 일들이 있지만, 이것은 당신만 알고 있을 수 있다는 점에 주의하십시오.



답변자 : Swati Srivastava


파이썬에서 클래스는 객체이며 다른 객체와 마찬가지로 "무언가"의 인스턴스입니다. 이 "무언가"를 메타클래스라고 합니다. 이 메타클래스는 다른 클래스의 객체를 생성하는 특별한 유형의 클래스입니다. 따라서 메타클래스는 새 클래스를 만드는 역할을 합니다. 이를 통해 프로그래머는 클래스가 생성되는 방식을 사용자 정의할 수 있습니다.

메타클래스를 생성하려면 일반적으로 new () 및 init () 메서드를 재정의합니다. new ()를 재정의하여 객체가 생성 되는 방식을 변경할 수 있고 init ()를 재정의하여 객체를 초기화하는 방식을 변경할 수 있습니다. 메타클래스는 여러 가지 방법으로 만들 수 있습니다. 방법 중 하나는 type() 함수를 사용하는 것입니다. type() 함수는 3개의 매개변수로 호출될 때 메타클래스를 생성합니다. 매개변수는 다음과 같습니다.

  1. 클래스 이름
  2. 클래스에 의해 상속된 기본 클래스가 있는 튜플
  3. 모든 클래스 메서드와 클래스 변수가 있는 사전

메타클래스를 만드는 또 다른 방법은 'metaclass' 키워드로 구성됩니다. 메타클래스를 단순 클래스로 정의합니다. 상속받은 클래스의 매개변수에 metaclass=metaclass_name을 전달합니다.

Metaclass는 특히 다음과 같은 상황에서 사용할 수 있습니다.

  1. 특정 효과가 모든 하위 클래스에 적용되어야 하는 경우
  2. 클래스 자동 변경(생성 시)이 필요합니다.
  3. API 개발자 기준


답변자 : DrosnickX


파이썬에서 메타클래스는 서브클래스의 동작 방식을 결정하는 서브클래스의 서브클래스입니다. 클래스는 다른 메타클래스의 인스턴스입니다. Python에서 클래스는 클래스의 인스턴스가 작동하는 방식을 지정합니다.

메타클래스는 클래스 생성을 담당하기 때문에 추가 작업을 수행하거나 코드를 삽입하여 클래스가 생성되는 방식을 변경하는 사용자 정의 메타클래스를 작성할 수 있습니다. 사용자 정의 메타클래스가 항상 중요한 것은 아니지만 중요할 수 있습니다.



답변자 : Emma Brown


classutilities 라는 패키지에서 메타클래스에 대한 흥미로운 사용 사례를 보았습니다. 모든 클래스 변수가 대문자 형식인지 확인하고(구성 클래스에 대한 통합 논리를 갖는 것이 편리함) 클래스에 인스턴스 수준 메서드가 없는지 확인합니다. 메타클라아제에 대한 또 다른 흥미로운 예는 복잡한 조건(여러 환경 변수의 값 확인)에 기반한 단위 테스트의 비활성화입니다.



답변자 : Manukumar


메타클래스는 모델 클래스의 단순한 내부 클래스에 불과합니다. 예를 들어 Django 또는 python에서

 class Author(models.Model): name = models.CharField(max_length=50) email = models.EmailField() class Meta: abstract = True

여기서 abstract = True이면 이 모델은 추상 기본 클래스가 됩니다. 메타클래스는 기본 클래스의 동작을 변경합니다.



답변자 : Technical A.D.


메타 클래스는 클래스가 어떻게 동작할지 정의하는 클래스의 일종입니다. 또는 A 클래스 자체가 메타 클래스의 인스턴스라고 말할 수 있습니다.



출처 : Here


출처 : http:www.stackoverflow.com/questions/100003/what-are-metaclasses-in-python">

반응형