什么是元类,我们用它们做什么?
什么是元类,我们用它们做什么?
元类是类的类。就像类定义了类的实例的行为一样,元类定义了类的行为方式。类是元类的实例。
在Python中,您可以为元类使用任意的callables(比如 耶路 显示),更有用的方法实际上是使它成为一个真正的类本身。 type
是Python中常用的元类。如果你想知道,是的, type
它本身就是一个类,它是它自己的类型。你将无法再创造类似的东西 type
纯粹用Python,但Python有点欺骗。要在Python中创建自己的元类,你真的只想要子类 type
。
元类最常用作类工厂。就像你通过调用类来创建类的实例一样,Python通过调用元类来创建一个新类(当它执行'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__
元类是类的类。就像类定义了类的实例的行为一样,元类定义了类的行为方式。类是元类的实例。
在Python中,您可以为元类使用任意的callables(比如 耶路 显示),更有用的方法实际上是使它成为一个真正的类本身。 type
是Python中常用的元类。如果你想知道,是的, type
它本身就是一个类,它是它自己的类型。你将无法再创造类似的东西 type
纯粹用Python,但Python有点欺骗。要在Python中创建自己的元类,你真的只想要子类 type
。
元类最常用作类工厂。就像你通过调用类来创建类的实例一样,Python通过调用元类来创建一个新类(当它执行'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__
在理解元类之前,您需要掌握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 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
你会看到我们要去的地方:在Python中,类是对象,你可以动态地创建一个类。
这就是Python在您使用关键字时所做的事情 class
,它通过使用元类来实现。
元类是创建类的“东西”。
你定义类来创建对象,对吗?
但我们了解到Python类是对象。
好吧,元类是创建这些对象的原因。他们是班级的班级, 你可以这样画出来:
MyClass = MetaClass()
my_object = MyClass()
你已经看到了 type
让你做这样的事情:
MyClass = type('MyClass', (), {})
这是因为功能 type
实际上是一个元类。 type
是个
元类Python用于在幕后创建所有类。
现在你想知道为什么它是用小写写的,而不是 Type
?
好吧,我想这是一个与之保持一致的问题 str
,创造的阶级
字符串对象,和 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
是Python使用的内置元类,但当然,你可以创建你的
自己的元类。
__metaclass__
属性你可以添加一个 __metaclass__
写一个类时的属性:
class Foo(object):
__metaclass__ = something...
[...]
如果这样做,Python将使用元类来创建类 Foo
。
小心,这很棘手。
你写 class Foo(object)
首先,但是类对象 Foo
没有创建
在记忆中。
Python会寻找 __metaclass__
在类定义中。如果找到了,
它将使用它来创建对象类 Foo
。如果没有,它将使用
type
创建类。
多读一遍。
当你这样做时:
class Foo(Bar):
pass
Python执行以下操作:
有没有 __metaclass__
属性 Foo
?
如果是的话,在内存中创建一个类对象(我说一个类对象,留在这里),名字 Foo
通过使用中的内容 __metaclass__
。
如果Python找不到 __metaclass__
,它会寻找一个 __metaclass__
在MODULE级别,并尝试执行相同的操作(但仅适用于不继承任何内容的类,基本上是旧式类)。
然后,如果它找不到任何 __metaclass__
根本就会使用 Bar
's(第一个父级)拥有元类(可能是默认的 type
)创建类对象。
这里要小心了 __metaclass__
属性不会被继承,父类的元类(Bar.__class__
) 将会。如果 Bar
用了一个 __metaclass__
创建的属性 Bar
同 type()
(并不是 type.__new__()
),子类不会继承该行为。
现在最大的问题是,你能投入什么? __metaclass__
?
答案是:可以创建一个类的东西。
什么可以创造一个类? type
或者任何子类或使用它的东西。
元类的主要目的是自动更改类, 当它被创建。
您通常会在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_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
。有
没什么特别的: __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)
而已。实际上没有关于元类的更多信息。
使用元类的代码复杂性背后的原因不是因为
这是因为你经常使用元类做扭曲的东西
依靠内省,操纵继承,vars等 __dict__
等
实际上,元类特别适用于黑魔法,因此 复杂的东西。但它们本身很简单:
以来 __metaclass__
可以接受任何可调用的,为什么你会使用一个类
因为它显然更复杂?
有几个原因可以这样做:
UpperAttrMetaclass(type)
, 你懂
接下来会发生什么__new__
, __init__
和 __call__
。哪个会允许的
你要做不同的事情。即使通常你可以做到这一点 __new__
,
有些人使用起来更舒服 __init__
。现在是个大问题。为什么要使用一些不起眼的容易出错的功能?
好吧,通常你不会:
元类是更深刻的魔力 99%的用户永远不必担心。 如果你想知道你是否需要它们, 你没有(实际的人 需要他们肯定地知道 他们需要它们,而不需要它们 解释为什么)。
Python大师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
一切都是Python中的一个对象,它们都是类的实例 或元类的实例。
除了 type
。
type
实际上是它自己的元类。这不是你能做到的
在纯Python中重现,并通过在实现中作弊一点来完成
水平。
其次,元类很复杂。您可能不想使用它们 非常简单的课程改动。您可以使用两种不同的技术更改类:
99%的时间你需要改变课程,你最好使用这些。
但是98%的情况下,你根本不需要改变课程。
注意,这个答案适用于Python 2.x,因为它是在2008年编写的,元类在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
让我们定义一个元类,它将演示如何'类:'打电话给它。
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
请注意,通过使用元类,“初始化”获得的神奇行为 init_attributes
未传递给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
元类的一个用途是自动向实例添加新属性和方法。
例如,如果你看一下 Django模型,他们的定义看起来有点令人困惑。看起来好像只是定义了类属性:
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
但是,在运行时,Person对象充满了各种有用的方法。见 资源 对于一些惊人的metaclassery。
其他人已经解释了元类如何工作以及它们如何适合Python类型系统。以下是它们可用于什么的示例。在我编写的测试框架中,我想跟踪定义类的顺序,以便稍后我可以按此顺序实例化它们。我发现使用元类这样做最容易。
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
记录定义类的顺序。
我认为ONLamp对元类编程的介绍写得很好,并且尽管已有几年的历史,却给出了很好的主题介绍。
http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html
简而言之:类是创建实例的蓝图,元类是创建类的蓝图。可以很容易地看出,Python类中也需要是第一类对象来启用此行为。
我自己从来没有写过,但我认为在网络中可以看到最好用的元类之一 Django框架。模型类使用元类方法来启用编写新模型或表单类的声明式样式。当元类创建类时,所有成员都可以自定义类。
剩下要说的是:如果你不知道什么是元类,你的可能性 不需要它们 是99%。
什么是元类?你用它们做什么的?
TLDR:元类实例化并定义类的行为,就像类实例化一样,并定义实例的行为。
伪代码:
>>> Class(...)
instance
以上应该看起来很熟悉。那么,哪里呢 Class
来自?它是元类(也是伪代码)的一个实例:
>>> Metaclass(...)
Class
在实际代码中,我们可以传递默认的元类, type
,我们需要实例化一个类,我们得到一个类:
>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
<class '__main__.Foo'>
类是一个实例,因为元类是一个类。
当我们实例化一个对象时,我们得到一个实例:
>>> object() # instantiation of class
<object object at 0x7f9069b4e0b0> # instance
同样,当我们使用默认元类明确定义一个类时, type
,我们实例化它:
>>> type('Object', (object,), {}) # instantiation of metaclass
<class '__main__.Object'> # instance
换句话说,类是元类的实例:
>>> isinstance(object, type)
True
换句话说,元类是类的类。
>>> type(object) == type
True
>>> object.__class__
<class 'type'>
当您编写类定义并且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
。
(关于班级内容的附注 __dict__
: __module__
是因为类必须知道它们的定义位置,并且 __dict__
和 __weakref__
是因为我们没有定义 __slots__
- 要是我们 确定 __slots__
我们将在实例中节省一些空间,因为我们可以禁止 __dict__
和 __weakref__
排除它们。例如:
>>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
>>> Baz.__dict__
mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})
......但我离题了。)
type
就像任何其他类定义一样:这是默认值 __repr__
课程:
>>> Foo
<class '__main__.Foo'>
在编写Python对象时,我们默认可以做的最有价值的事情之一就是为它提供一个好的东西 __repr__
。当我们打电话 help(repr)
我们知道有一个很好的测试 __repr__
这也需要对平等进行测试 - obj == eval(repr(obj))
。以下简单实现 __repr__
和 __eq__
对于我们的类类的类实例,我们提供了一个可以改进默认值的演示 __repr__
课程:
class Type(type):
def __repr__(cls):
"""
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> eval(repr(Baz))
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
"""
metaname = type(cls).__name__
name = cls.__name__
parents = ', '.join(b.__name__ for b in cls.__bases__)
if parents:
parents += ','
namespace = ', '.join(': '.join(
(repr(k), repr(v) if not isinstance(v, type) else v.__name__))
for k, v in cls.__dict__.items())
return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)
def __eq__(cls, other):
"""
>>> Baz == eval(repr(Baz))
True
"""
return (cls.__name__, cls.__bases__, cls.__dict__) == (
other.__name__, other.__bases__, other.__dict__)
所以现在当我们用这个元类创建一个对象时, __repr__
在命令行上回显提供了比默认值更不丑陋的视线:
>>> class Bar(object): pass
>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
很好 __repr__
为类实例定义,我们有更强的调试代码的能力。但是,进一步检查 eval(repr(Class))
不太可能(因为函数将无法从默认值中评估) __repr__
的)。
__prepare__
命名空间例如,如果我们想知道创建类的方法的顺序,我们可以提供一个有序的dict作为类的命名空间。我们会这样做 __prepare__
哪一个 如果在Python 3中实现,则返回该类的命名空间dict:
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')
注意,这个例子改编自 文件 - 新的 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>})