메타클래스란 무엇입니까? 그들은 무엇을 위해 사용됩니까?
[Python] 파이썬에서 메타클래스란 무엇입니까?
객체로서의 클래스
메타클래스를 이해하기 전에 Python의 클래스를 마스터해야 합니다. 그리고 Python은 Smalltalk 언어에서 차용한 클래스가 무엇인지에 대한 매우 독특한 아이디어를 가지고 있습니다.
대부분의 언어에서 클래스는 객체를 생성하는 방법을 설명하는 코드 조각일 뿐입니다. 그것은 Python에서도 사실입니다.
>>> class ObjectCreator(object):
... pass
...
>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>
그러나 클래스는 Python에서 그 이상입니다. 클래스도 객체입니다.
예, 개체입니다.
키워드 를 사용 하자마자 class
Python 은 이를 실행 하고 객체 를 생성 합니다 . 지시
>>> 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에서 클래스는 객체이며 동적으로 즉석에서 클래스를 생성할 수 있습니다.
이것은 키워드를 사용할 때 Python이 수행하는 작업 class
이며 메타 클래스를 사용하여 수행합니다.
메타 클래스 란 무엇입니까 (마지막으로)
메타클래스는 클래스를 생성하는 '물건'입니다.
객체를 생성하기 위해 클래스를 정의합니다. 맞죠?
그러나 우리는 파이썬 클래스가 객체라는 것을 배웠습니다.
글쎄, 메타 클래스는 이러한 객체를 만드는 것입니다. 그것들은 클래스의 클래스이며 다음과 같이 그릴 수 있습니다.
MyClass = MetaClass()
my_object = MyClass()
type
다음과 같은 작업을 수행할 수 있습니다 .
MyClass = type('MyClass', (), {})
함수 type
가 실제로 메타클래스이기 때문입니다. type
Python이 무대 뒤에서 모든 클래스를 생성하는 데 사용하는 메타 클래스입니다.
이제 "도대체 왜 소문자가 아닌 소문자로 쓰여졌 Type
습니까?"
글쎄, 나는 그것이 str
문자열 객체를 생성 int
하는 클래스와 정수 객체를 생성하는 클래스와의 일관성 문제라고 생각합니다. type
클래스 객체를 생성하는 클래스일 뿐입니다.
__class__
속성 을 확인하면 알 수 있습니다 .
모든 것, 내가 의미하는 모든 것은 파이썬에서 객체입니다. 여기에는 정수, 문자열, 함수 및 클래스가 포함됩니다. 모두 객체입니다. 그리고 그들 모두는 클래스에서 생성되었습니다.
>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>
이제, __class__
의 무엇 __class__
입니까?
>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>
따라서 메타 클래스는 클래스 개체를 생성하는 것입니다.
원한다면 '클래스 팩토리'라고 부를 수 있습니다.
type
는 Python이 사용하는 내장 메타클래스이지만 물론 자신만의 메타클래스를 만들 수도 있습니다.
__metaclass__
속성 _
Python 2에서는 __metaclass__
클래스를 작성할 때 속성을 추가할 수 있습니다(Python 3 구문은 다음 섹션 참조).
class Foo(object):
__metaclass__ = something...
[...]
그렇게 하면 파이썬은 메타클래스를 사용하여 클래스를 생성합니다 Foo
.
조심해, 까다롭다.
먼저 작성 class Foo(object)
하지만 클래스 객체 Foo
는 아직 메모리에 생성되지 않습니다.
파이썬은 __metaclass__
클래스 정의에서 찾을 것입니다. 찾으면 개체 클래스를 만드는 데 사용합니다 Foo
. 그렇지 않은 경우
type
클래스를 만드는 데 사용됩니다.
여러 번 읽으십시오.
당신이 할 때:
class Foo(Bar):
pass
파이썬은 다음을 수행합니다.
에 __metaclass__
속성이 Foo
있습니까?
그렇다면 에 있는 것을 사용하여 이름 Foo
을 사용하여 메모리 내 클래스 개체(클래스 개체라고 말했습니다. 여기에서 함께 하세요.)를 __metaclass__
만듭니다.
Python이 찾을 수 없으면 MODULE 수준에서 __metaclass__
찾고 __metaclass__
동일한 작업을 시도합니다(그러나 기본적으로 구식 클래스를 상속하지 않는 클래스에만 해당).
__metaclass__
그런 다음 전혀 찾을 수 없으면 Bar
의 (첫 번째 부모) 자체 메타클래스(기본값일 수 있음 type
)를 사용하여 클래스 개체를 만듭니다.
__metaclass__
여기서 속성은 상속되지 않고 부모( )의 메타클래스가 상속 된다는 점에 주의하십시오 Bar.__class__
. 로 생성 된 속성을 Bar
사용하는 경우 (및 하지 않음 ) 하위 클래스는 해당 동작을 상속하지 않습니다.__metaclass__
Bar
type()
type.__new__()
이제 가장 큰 문제는 무엇을 넣을 수 __metaclass__
있습니까?
답은 클래스를 만들 수 있는 것입니다.
그리고 클래스를 만들 수 있는 것은 무엇입니까? type
, 또는 그것을 서브클래스화하거나 사용하는 모든 것.
Python 3의 메타클래스
메타클래스를 설정하는 구문이 Python 3에서 변경되었습니다.
class Foo(object, metaclass=something):
...
즉, __metaclass__
기본 클래스 목록에서 키워드 인수를 위해 속성이 더 이상 사용되지 않습니다.
그러나 메타클래스의 동작은 거의 동일하게 유지됩니다 .
Python 3의 메타클래스에 추가된 한 가지는 다음과 같이 속성을 키워드 인수로 메타클래스에 전달할 수도 있다는 것입니다.
class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
...
Python이 이것을 처리하는 방법에 대해서는 아래 섹션을 읽으십시오.
커스텀 메타클래스
메타 클래스의 주요 목적은 클래스가 생성될 때 자동으로 클래스를 변경하는 것입니다.
일반적으로 현재 컨텍스트와 일치하는 클래스를 생성하려는 API에 대해 이 작업을 수행합니다.
모듈의 모든 클래스가 속성을 대문자로 작성해야 한다고 결정한 어리석은 예를 상상해 보십시오. 이를 수행하는 방법에는 여러 가지가 있지만 한 가지 방법은 __metaclass__
모듈 수준에서 설정하는 것입니다.
이런 식으로 이 모듈의 모든 클래스는 이 메타클래스를 사용하여 생성되며 모든 속성을 대문자로 바꾸도록 메타클래스에 지시해야 합니다.
운 좋게도 __metaclass__
실제로 모든 호출 가능 항목이 될 수 있으며 형식 클래스일 필요는 없습니다(이름에 'class'가 포함된 항목은 클래스일 필요가 없습니다. 하지만 도움이 됩니다).
그래서 우리는 함수를 사용하여 간단한 예부터 시작할 것입니다.
# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attrs):
"""
Return a class object, with the list of its attribute turned
into uppercase.
"""
# pick up any attribute that doesn't start with '__' and uppercase it
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in future_class_attrs.items()
}
# let `type` do the class creation
return type(future_class_name, future_class_parents, uppercase_attrs)
__metaclass__ = upper_attr # this will affect all classes in the module
class Foo(): # global __metaclass__ won't work with "object" though
# but we can define __metaclass__ here instead to affect only this class
# and this will work with "object" children
bar = 'bip'
점검 해보자:
>>> hasattr(Foo, 'bar')
False
>>> hasattr(Foo, 'BAR')
True
>>> Foo.BAR
'bip'
이제 정확히 동일하게 수행하되 메타 클래스에 실제 클래스를 사용합니다.
# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
# __new__ is the method called before __init__
# it's the method that creates the object and returns it
# while __init__ just initializes the object passed as parameter
# you rarely use __new__, except when you want to control how the object
# is created.
# here the created object is the class, and we want to customize it
# so we override __new__
# you can do some stuff in __init__ too if you wish
# some advanced use involves overriding __call__ as well, but we won't
# see this
def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attrs):
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in future_class_attrs.items()
}
return type(future_class_name, future_class_parents, uppercase_attrs)
위의 내용을 다시 작성해 보겠습니다. 그러나 이제 의미를 알았으므로 더 짧고 현실적인 변수 이름을 사용합니다.
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, attrs):
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in attrs.items()
}
return type(clsname, bases, uppercase_attrs)
추가 인수를 눈치채셨을 수도 있습니다 cls
. 특별한 것은 없습니다. __new__
항상 첫 번째 매개변수로 정의된 클래스를 받습니다. self
인스턴스를 첫 번째 매개변수로 받는 일반 메서드나 클래스 메서드에 대한 정의 클래스와 마찬가지로 .
그러나 이것은 적절한 OOP가 아닙니다. 우리는 type
직접 호출하며 부모의 __new__
. 대신 해봅시다:
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, attrs):
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in attrs.items()
}
return type.__new__(cls, clsname, bases, uppercase_attrs)
상속을 용이하게 하는 를 사용하여 더 깔끔하게 만들 수 있습니다 super
(예, 메타클래스에서 상속, 유형에서 상속).
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, attrs):
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in attrs.items()
}
return super(UpperAttrMetaclass, cls).__new__(
cls, clsname, bases, uppercase_attrs)
아, 그리고 Python 3에서 다음과 같이 키워드 인수를 사용하여 이 호출을 수행하면:
class Foo(object, metaclass=MyMetaclass, kwarg1=value1):
...
그것을 사용하기 위해 메타 클래스에서 다음과 같이 번역됩니다.
class MyMetaclass(type):
def __new__(cls, clsname, bases, dct, kwargs1=default):
...
그게 다야 메타 클래스에 대해 더 이상 아무것도 없습니다.
메타클래스를 사용하는 코드가 복잡한 이유는 메타클래스 때문이 아니라 일반적으로 메타클래스를 사용하여 내성, 상속 조작, vars __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%의 경우 클래스 변경이 전혀 필요하지 않습니다.
메타 클래스는 클래스의 클래스입니다. 클래스는 클래스의 인스턴스(즉, 객체)가 어떻게 동작하는지 정의하는 반면 메타클래스는 클래스가 동작하는 방식을 정의합니다. 클래스는 메타클래스의 인스턴스입니다.
Python에서는 Jerub이 보여주는 것처럼 메타클래스에 임의의 호출 가능 항목을 사용할 수 있지만 더 나은 접근 방식은 실제 클래스 자체로 만드는 것입니다. type
Python의 일반적인 메타 클래스입니다. type
자체가 클래스이고 자체 유형입니다. 순수하게 Python에서 와 같은 것을 다시 만들 수는 type
없지만 Python은 약간의 속임수를 사용합니다. 파이썬에서 자신만의 메타클래스를 만들려면 정말로 하위 클래스를 만들고 싶을 뿐입니다 type
.
메타 클래스는 가장 일반적으로 클래스 팩토리로 사용됩니다. 클래스를 호출하여 객체를 생성할 때, 파이썬은 메타클래스를 호출하여 ('class' 문을 실행할 때) 새로운 클래스를 생성합니다. 따라서 일반 __init__
및 __new__
메서드와 결합된 메타클래스를 사용하면 새 클래스를 레지스트리에 등록하거나 클래스를 완전히 다른 것으로 교체하는 것과 같이 클래스를 생성할 때 '추가 작업'을 수행할 수 있습니다.
class
명령문이 실행되면 Python은 먼저 명령문의 본문을 일반 class
코드 블록으로 실행합니다. 결과 네임스페이스(딕셔너리)는 클래스의 속성을 보유합니다. 메타클래스는 대상 클래스(메타클래스가 상속됨) __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__
참고로 이 답변은 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