Python 中的元类 metaclasses 是什么?

什么是元类 metaclasses ?它们有什么用?

1069125 次浏览

注意,这个答案是针对Python 2. x的,因为它是在2008年编写的,元类在3. x中略有不同。

元类是使类工作的秘诀。新样式对象的默认元类称为类型。

class type(object)|  type(object) -> the object's type|  type(name, bases, dict) -> a new type

元类需要3个参数。'姓名'、'基地'和'字典'

这里是秘密开始的地方。在这个示例类定义中查找名称、基数和字典的来源。

class ThisIsTheName(Bases, Are, Here):All_the_code_heredef doesIs(create, a):dict

让我们定义一个元类来演示'班级:'如何调用它。

def test_metaclass(name, bases, dict):print 'The Class Name is', nameprint 'The Class Bases are', basesprint 'The dict has', len(dict), 'elems, the keys are', dict.keys()
return "yellow"
class TestName(object, None, int, 1):__metaclass__ = test_metaclassfoo = 1def baz(self, arr):pass
print 'TestName = ', repr(TestName)
# output =>The Class Name is TestNameThe 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_attributesattributes = ['foo', 'bar', 'baz']
print 'foo =>', Initialised.foo# output=>foo => None

请注意,Initialised通过拥有元类init_attributes而获得的神奇行为不会传递给Initialised的子类。

这是一个更具体的例子,展示了如何子类“type”来创建一个元类,在创建类时执行操作。这是相当棘手的:

class MetaSingleton(type):instance = Nonedef __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

我认为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%。

元类的一个用途是自动向实例添加新的属性和方法。

例如,如果您查看django模型,它们的定义看起来有点混乱。看起来好像您只定义类属性:

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

然而,在运行时,Person对象充满了各种有用的方法。

元类是类的类。类定义类的实例(即对象)的行为方式,而元类定义类的行为方式。类是元类的实例。

虽然在Python中,您可以对元类使用任意可调用项(如Jerub所示),但更好的方法是使其本身成为一个实际的类。type是Python中常见的元类。type本身就是一个类,它是自己的类型。你无法纯粹在Python中重新创建像type这样的东西,但Python有一点作弊。要在Python中创建自己的元类,你真的只想子类type

元类最常用作类工厂。当你通过调用类创建对象时,Python通过调用元类创建一个新类(当它执行'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 = 1return 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] = attrvalueelse: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):passreturn 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_hookdef add(self, other):return self.__class__(self.value + other.value)
# Will unregister the classExample.unregister()
inst = Example(10)# Will fail with an AttributeError#inst.unregister()
print inst + instclass 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 ExampleSiblingprint ExampleSibling.__mro__

其他人解释了元类是如何工作的,以及它们如何适应Python类型系统。这是一个它们可以用来做什么的例子。在我写的一个测试框架中,我想跟踪类定义的顺序,这样我以后就可以按照这个顺序实例化它们。我发现使用元类最容易做到这一点。

class MyMeta(type):
counter = 0
def __init__(cls, name, bases, dic):type.__init__(cls, name, bases, dic)cls._order = MyMeta.counterMyMeta.counter += 1
class MyType(object):              # Python 2__metaclass__ = MyMeta
class MyType(metaclass=MyMeta):    # Python 3pass

任何MyType的子类都会获得一个class属性_order,该属性记录类定义的顺序。

类作为对象

在理解元类之前,您需要掌握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'>

好吧,#0也有一个完全不同的能力:它可以动态创建类。type可以将类的描述作为参数,并返回一个类。

(我知道,根据你传递给它的参数,同一个函数可以有两个完全不同的用途是愚蠢的。这是一个由于向后在Python中的兼容性)

type是这样工作的:

type(name, bases, attrs)

在哪里:

  • #0:类的名称
  • #0:父类的元组(对于继承,可以为空)
  • #0:包含属性名称和值的字典

例如:

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

最终,您需要向您的类添加方法。只需定义一个函数使用正确的签名并将其分配为属性。

>>> 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使用的内置元类,但当然,您可以创建您的自己的元类。

#0属性

在Python 2中,您可以在编写类时添加__metaclass__属性(有关Python 3语法,请参阅下一节):

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

如果你这样做,Python将使用元类来创建类Foo

小心,这很棘手。

您先写class Foo(object),但未创建类对象Foo在记忆中

Python会在类定义中查找__metaclass__。如果找到了,它将使用它来创建对象类Foo。如果没有,它将使用type创建类。

读了好几遍。

当你这样做:

class Foo(Bar):pass

Python执行以下操作:

Foo中是否有__metaclass__属性?

如果是,请使用__metaclass__中的内容在内存中创建一个名为Foo的类对象(我说的是类对象,请留在这里)。

如果Python找不到__metaclass__,它会在MODULE级别寻找__metaclass__,并尝试做同样的事情(但仅限于不继承任何东西的类,基本上是老式类)。

然后,如果它根本找不到任何__metaclass__,它将使用Bar(第一个父级)自己的元类(可能是默认的type)来创建类对象。

请注意,__metaclass__属性不会被继承,父类(Bar.__class__)的元类将被继承。如果Bar使用__metaclass__属性创建了Bartype()(而不是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 turnedinto uppercase."""# pick up any attribute that doesn't start with '__' and uppercase ituppercase_attrs = {attr if attr.startswith("__") else attr.upper(): vfor attr, v in future_class_attrs.items()}
# let `type` do the class creationreturn 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" childrenbar = '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 itclass 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 thisdef __new__(upperattr_metaclass, future_class_name,future_class_parents, future_class_attrs):uppercase_attrs = {attr if attr.startswith("__") else attr.upper(): vfor 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(): vfor 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(): vfor 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(): vfor attr, v in attrs.items()}
# Python 2 requires passing arguments to super:return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attrs)
# Python 3 can use no-arg super() which infers them:return super().__new__(cls, clsname, bases, uppercase_attrs)

哦,在Python 3中,如果您使用关键字参数执行此调用,如下所示:

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

它在元类中转换为this以使用它:

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

就是这样。元类真的没有更多的东西了。

使用元类的代码复杂性背后的原因不是因为这是因为你通常使用元类来做扭曲的事情依靠内省,操纵继承,变量,如__dict__等。

事实上,元类对于黑魔法特别有用,因此复杂的东西。但就其本身而言,它们很简单:

  • 拦截一个类的创建
  • 修改类
  • 返回修改后的类

为什么要使用元类而不是函数?

既然__metaclass__可以接受任何可调用,为什么要使用类因为它显然更复杂?

这样做有几个原因:

  • 意图很明确。当你读到UpperAttrMetaclass(type)时,你就知道了接下来会发生什么
  • 您可以使用OOP。元类可以继承元类,覆盖父方法。元类甚至可以使用元类。
  • 如果您指定了元类,则类的子类将是其元类的实例,但不使用元类函数。
  • 你可以更好地构建代码。你永远不会将元类用于像上面例子这样琐碎的事情。它通常用于复杂的事情。能够制作多个方法并将它们分组在一个类中对于使代码更易于阅读非常有用。
  • 你可以挂在__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()

如果你这样做:

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

在Python中,一切都是对象,它们都是类的实例或元类的实例。

除了type

type实际上是它自己的元类。这不是你可以在纯Python中复制,并通过在实现中稍微作弊来完成级别。

其次,元类很复杂。您可能不想将它们用于非常简单的类更改。您可以使用两种不同的技术更改类:

99%的时候,你需要改变类,你最好使用这些。

但是98%的时候,你根本不需要改变类。

metaclass是一个类,告诉(一些)其他类应该如何创建。

在这种情况下,我认为metaclass可以解决我的问题:我遇到了一个非常复杂的问题,可能可以用不同的方式解决,但我选择使用metaclass来解决它。由于复杂性,它是我编写的少数几个模块之一,模块中的注释超过了已经编写的代码量。在这里…

#!/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 docoptfrom libgsync.options import docfrom 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_nameimport rename = re.sub(r'^__', "", re.sub(r'-', "_", name))listvalue = []
# Ensure value is converted to a list type for GsyncListOptionsif isinstance(value, list):if value:listvalue = [] + valueelse:listvalue = [ None ]else:listvalue = [ value ]
type.__setattr__(GsyncListOptions, name, listvalue)
# Cleanup this module to prevent tinkering.import sysmodule = sys.modules[__name__]del module.__dict__['GetGsyncOptionsType']
return GsyncOptionsType
# Our singlton abstract proxy class.class GsyncOptions(object):__metaclass__ = GetGsyncOptionsType()

什么是元类?你用它们做什么?

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
  • Put another way, a class is an instance of a metaclass:

    >>> isinstance(object, type)True
  • Put a third way, a metaclass is a class's class.

    >>> type(object) == typeTrue>>> object.__class__<class 'type'>

When you write a class definition and Python executes it, it uses a metaclass to instantiate the class object (which will, in turn, be used to instantiate instances of that class).

Just as we can use class definitions to change how custom object instances behave, we can use a metaclass class definition to change the way a class object behaves.

What can they be used for? From the docs:

The potential uses for metaclasses are boundless. Some ideas that have been explored include logging, interface checking, automatic delegation, automatic property creation, proxies, frameworks, and automatic resource locking/synchronization.

Nevertheless, it is usually encouraged for users to avoid using metaclasses unless absolutely necessary.

You use a metaclass every time you create a class:

When you write a class definition, for example, like this,

class Foo(object):'demo'

实例化一个类对象。

>>> Foo<class '__main__.Foo'>>>> isinstance(Foo, type), isinstance(Foo, object)(True, True)

这与使用适当的参数在功能上调用type并将结果分配给该名称的变量相同:

name = 'Foo'bases = (object,)namespace = {'__doc__': 'demo'}Foo = type(name, bases, namespace)

注意,有些东西会自动添加到__dict__中,即命名空间:

>>> Foo.__dict__dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>,'__module__': '__main__', '__weakref__': <attribute '__weakref__'of 'Foo' objects>, '__doc__': 'demo'})

在这两种情况下,我们创建的对象的元类都是type

(关于类__dict__的内容的一个旁注:__module__在那里是因为类必须知道它们是在哪里定义的,__dict____weakref__在那里是因为我们没有定义__slots__-如果我们定义#4,我们将在实例中节省一点空间,因为我们可以通过排除它们来禁用__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):""">>> BazType('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})>>> BazType('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})

为类实例定义了一个不错的__repr__,我们有更强的调试代码的能力。然而,不太可能进一步检查eval(repr(Class))(因为函数不可能从默认的__repr__中进行评估)。

预期用法:__prepare__命名空间

例如,如果我们想知道类的方法是以什么顺序创建的,我们可以提供一个有序的字典作为类的命名空间。我们可以用__prepare__如果在Python 3中实现,则返回类的命名空间字典来做到这一点:

from collections import OrderedDict
class OrderedType(Type):@classmethoddef __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): passdef method2(self): passdef method3(self): passdef 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(除非我们能找到一种方法来表示我们的函数,否则我们不能再对其进行评估):

>>> OrderedMethodsObjectOrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMethodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__dict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})

python3更新

元类中(此时)有两个关键方法:

  • __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)

举个简单的例子:

假设您想要一些简单的验证代码在您的属性上运行——就像它必须始终是intstr一样。没有元类,您的类看起来像:

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 descriptorsfor name, attr in clsdict.items():if isinstance(attr, ValidateType):attr.name = nameattr.attr = '_' + name# create final class and return itreturn super().__new__(metacls, cls, bases, clsdict)

示例运行:

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

生产:

9Traceback (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 metaclassself.attr = None  # will be set by metaclassself.type = typedef __get__(self, inst, cls):if inst is None:return selfelse: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

type实际上是一个metaclass一个创建另一个类的类。大多数metaclasstype的子类。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: NewClassBases <class 'object'>:Defining class <class 'NewClass'>get_choch :'dairy'__module__ :'builtins'__qualname__ :'NewClass'

Note:

请注意,类在任何时候都没有实例化;创建类的简单行为触发了metaclass的执行。

元类__call__()方法在创建类实例时的作用

如果你已经做了几个月以上的Python编程,你最终会偶然发现如下代码:

# define a classclass SomeClass(object):# ...# some definition here ...# ...
# create an instance of itinstance = SomeClass()
# then call the object as if it's a functionresult = 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 ", clsinstance = super(Meta_1, cls).__call__()print "Meta_1.__call__() about to return instance."return instance

这是一个使用元类的类

class Class_1(object):
__metaclass__ = Meta_1
def __new__(cls):print "Class_1.__new__() before creating an instance."instance = super(Class_1, cls).__new__(cls)print "Class_1.__new__() about to return instance."return instance
def __init__(self):print "entering Class_1.__init__() for instance initialization."super(Class_1,self).__init__()print "exiting Class_1.__init__()."

现在让我们创建一个Class_1的实例

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

观察到上面的代码实际上除了记录任务之外没有做任何事情。每个方法都将实际工作委托给其父级的实现,从而保持默认行为。由于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 instanceinstance = cls.__new__(cls, *args, **kwargs)
# ... maybe a few things done to the instance here
# then we initialize the instance with its __init__() methodinstance.__init__(*args, **kwargs)
# ... maybe a few more things done to instance here
# then we return itreturn 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 usualprint "Meta_2.__call__() before creating an instance."instance = super(Meta_2, cls).__call__(*args, **kwargs)Meta_2.singletons[cls] = instanceprint "Meta_2.__call__() returning new instance."return instance
class Class_2(object):
__metaclass__ = Meta_2
def __new__(cls, *args, **kwargs):print "Class_2.__new__() before creating instance."instance = super(Class_2, cls).__new__(cls)print "Class_2.__new__() returning instance."return instance
def __init__(self, *args, **kwargs):print "entering Class_2.__init__() for initialization."super(Class_2, self).__init__()print "exiting Class_2.__init__()."

让我们观察一下重复尝试创建Class_2类型的对象时会发生什么

a = Class_2()# Meta_2.__call__() before creating an instance.# Class_2.__new__() before creating instance.# Class_2.__new__() returning instance.# entering Class_2.__init__() for initialization.# exiting Class_2.__init__().# Meta_2.__call__() returning new instance.
b = Class_2()# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.
c = Class_2()# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.
a is b is c # True

tl; dr版本

type(obj)函数获取对象的类型。

一个类的type()是它的元类

要使用元类:

class Foo(object):__metaclass__ = MyMetaClass

type是它自己的元类。类的类是元类——类的主体是传递给用于构造类的元类的参数。

这里您可以阅读有关如何使用元类来自定义类构造的信息。

Python类本身就是它们的元类的对象——就像实例一样。

默认元类,当您确定类为:

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 classesclsdict: 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):...
@classmethoddef 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

总之,这两个是最常用的钩子。元分类是强大的,上面是元分类的详尽使用列表。

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对象模型,类是对象,因此类必须是另一个特定类的实例。默认情况下,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']

当我们在metaclass中传递关键字参数时,Magic会生效,它指示Python解释器通过ListMetaclass创建CustomList。新的(),此时,我们可以修改类定义,例如,添加一个新方法,然后返回修改后的定义。

除了已发布的答案之外,我可以说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'

在面向对象编程中,元类是一个类,其实例是类。就像普通类定义某些对象的行为一样,元类定义某些类及其实例的行为元类这个术语简单地表示用于创建类的东西。换句话说,它是类的类。元类用于创建类,因此就像对象是类的实例一样,类是元类的实例。在python中,类也被认为是对象。

以下是它可以用于的另一个示例:

  • 您可以使用metaclass来更改其实例(类)的函数。
class MetaMemberControl(type):__slots__ = ()
@classmethoddef __prepare__(mcs, f_cls_name, f_cls_parents,  # f_cls means: future classmeta_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 herealias_name = '_' + itemif alias_name in f_cls_attr['__slots__']:item = alias_nameif 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_getattrf_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 = nameself._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@propertydef name(self):return self._name
@propertydef 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很强大,你可以用它做很多事情(比如猴子魔法),但要小心,这可能只有你知道。

在Python中,类是一个对象,就像任何其他对象一样,它是“某物”的实例。这个“某物”就是所谓的元类。这种元类是一种特殊类型的类,可以创建其他类的对象。因此,元类负责创建新类。这允许程序员自定义类的生成方式。

要创建元类,通常会覆盖新的()和init()方法。可以覆盖新的()以更改创建对象的方式,而可以覆盖init()以更改初始化对象的方式。元类可以通过多种方式创建。其中一种方法是使用type()函数。type()函数,当使用3个参数调用时,会创建一个元类。参数是:-

  1. 类名
  2. 具有由类继承的基类的元组
  3. 包含所有类方法和类变量的字典

创建元类的另一种方法包括'metaclass'关键字。将元类定义为一个简单的类。在继承类的参数中,传递metaclass=metaclass_name

元类可以专门用于以下情况:-

  1. 当一个特定的效果必须应用于所有的子类
  2. 需要自动更改类(创建时)
  3. 由API开发人员

请注意,在python 3.6中引入了一个新的dunder方法__init_subclass__(cls, **kwargs)来替换元类的许多常见用例。在创建定义类的子类时调用Is。见python文档

在Python中,元类是子类的子类,它决定了子类的行为方式。类是另一个元类的实例。在Python中,类指定了类实例的行为方式。

由于元类负责类的生成,因此您可以编写自己的自定义元类来通过执行附加操作或注入代码来更改类的创建方式。自定义元类并不总是重要的,但它们可以是。

我在一个名为classutilities的包中看到了一个有趣的元类用例。它检查所有类变量是否为大写格式(配置类的统一逻辑很方便),并检查类中是否没有实例级方法。元代码的另一个有趣的例子是基于复杂条件的单元测试的停用(检查多个环境变量的值)。

最上面的答案是正确的

但是读者可能会来这里搜索类似命名的内部类的答案。它们存在于流行的库中,例如DjangoWTForms

正如DavidW在这个答案下面的评论中指出的那样,这些是库特定的特性,不要与具有类似名称的高级、不相关的Python语言特性混淆

更确切地说,这些是类字典中的命名空间。为了易读性,它们使用内部类构建。

在这个示例特殊字段中,abstract与作者模型的字段明显分开。

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

另一个例子是WTForms的留档:

from wtforms.form import Formfrom wtforms.csrf.session import SessionCSRFfrom wtforms.fields import StringField
class MyBaseForm(Form):class Meta:csrf = Truecsrf_class = SessionCSRF
name = StringField("name")

Meta在这里不是关键字,也不会触发元类行为。相反,DjangoWTForms等包中的第三方库代码在某些类的构造函数和其他地方读取此属性。

这些声明的存在修改了具有这些声明的类的行为。例如,WTForms读取self.Meta.csrf以确定表单是否需要csrf字段。

看看这个:

Python 3.10.0rc2 (tags/v3.10.0rc2:839d789, Sep  7 2021, 18:51:45) [MSC v.1929 64 bit (AMD64)] on win32Type "help", "copyright", "credits" or "license" for more information.>>> class Object:...     pass...>>> class Meta(type):...     test = 'Worked!!!'...     def __repr__(self):...             return 'This is "Meta" metaclass'...>>> class ObjectWithMetaClass(metaclass=Meta):...     pass...>>> Object or type(Object())<class '__main__.Object'>>>> ObjectWithMetaClass or type(ObjectWithMetaClass())This is "Meta" metaclass>>> Object.testAttributeError: ...>>> ObjectWithMetaClass.test'Worked!!!'>>> type(Object)<class 'type'>>>> type(ObjectWithMetaClass)<class '__main__.Meta'>>>> type(type(ObjectWithMetaClass))<class 'type'>>>> Object.__bases__(<class 'object'>,)>>> ObjectWithMetaClass.__bases__(<class 'object'>,)>>> type(ObjectWithMetaClass).__bases__(<class 'type'>,)>>> Object.__mro__(<class '__main__.Object'>, <class 'object'>)>>> ObjectWithMetaClass.__mro__(This is "Meta" metaclass, <class 'object'>)>>>

换句话说,当没有创建对象(对象类型)时,我们会寻找MetaClass。

Meta是一个希腊词,意思是“之后”或“超越”。通常指的是自我。元表示附加信息。元类是一个辅助类,用于显式说明必须提及的任何附加信息或必须添加到超级类或父类的任何附加内容。在python中,当您定义一个类时,它会自动将其视为type=metaclass,并且该类功能(行为方法,属性自信息)在创建时适用于其他子类。

以下是元类的详细解释及其工作原理,以及编码示例。

Python中的元类是一个指定其他类行为方式的类。类是一个指定从中实例化的对象的结构和行为的对象,元类是一个定义其他类行为的类。

元类经常被用来以比标准类更通用和更有效的方式改变类的行为。例如,元类可用于在生成类之前预先验证类的特征或自动向类添加特定方法。

PersonMeta是一个元类,它继承自type元类,这是Python中的默认元类。每当创建Person对象时,都会调用PersonMeta中的__new__方法。然后,添加一个默认值为0age属性。

举个小例子来说明这个概念:

class PersonMeta(type):def __new__(cls, name, bases, dct):# Modify the attributes of the class before it is createddct['age'] = 0return super().__new__(cls, name, bases, dct)
class Person(metaclass=PersonMeta):def __init__(self, name):self.name = name

这里,PersonMeta元类继承自type元类(Python中的默认元类)。每当创建Person对象时,都会调用PersonMeta元类的__new__方法,该对象修改Person类的属性并添加默认值设置为0的age属性。

之后,使用metaclass关键字定义Person类,告诉Python使用PersonMeta元类实例化Person类。当创建Person类的实例时,调用PersonMeta元类的__new__方法,将age属性添加到Person类。