python python元类详解 python3元类 - 什么是Python中的元类?




8 Answers

元类是类的类。 就像类定义了类的实例的行为一样,元类定义了类的行为方式。 类是元类的实例。

虽然在Python中你可以为元类使用任意的callables(比如Jerub节目),实际上更有用的方法是使它成为一个真正的类本身。 type是Python中常用的元类。 如果您想知道,是的, type本身就是一个类,它是它自己的类型。 你将无法在Python中重新创建纯type东西,但是Python会有所作为。 要在Python中创建自己的元类,你真的只想子type

元类最常用作类工厂。 就像你通过调用类来创建类的实例一样,Python通过调用元类来创建一个新类(当它执行'class'语句时)。 结合正常的__init____new__方法,元类因此允许您在创建类时执行“额外的事情”,例如使用某个注册表注册新类,或者甚至完全用其他类替换该类。

执行class语句时,Python首先执行class语句的主体作为正常的代码块。 生成的命名空间(dict)保存了将要进行的类的属性。 元类是通过在__metaclass__类(如果有)的__metaclass__属性或__metaclass__全局变量中查看要被定义的类的基类(继承元类)来确定的。 然后使用类的名称,基数和属性调用元类来实例化它。

但是,元类实际上定义了类的类型 ,而不仅仅是它的工厂,所以你可以用它们做更多的事情。 例如,您可以在元类上定义常规方法。 这些元类方法类似于类方法,因为它们可以在没有实例的类上调用,但它们也不像类方法,因为它们不能在类的实例上调用。 type.__subclasses__()type元类的方法示例。 您还可以定义常规的“魔术”方法,如__add__ __iter____getattr__ __add____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元编程 元类ios

什么是元类,我们用它们做什么?




注意,这个答案适用于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 '

这是秘密开始的地方。 在此示例类定义中查找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'

现在,一个实际意味着什么的例子,这将自动使列表中的变量“属性”设置在类上,并设置为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



其他人已经解释了元类如何工作以及它们如何适合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 ,它记录了类的定义顺序。




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

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执行它时,它使用元类来实例化类对象(反过来,它将用于实例化该类的实例)。

就像我们可以使用类定义来改变自定义对象实例的行为方式一样,我们可以使用元类定义来改变类对象的行为方式。

它们可以用于什么? 来自docs

元类的潜在用途是无限的。 已探索的一些想法包括日志记录,接口检查,自动委托,自动属性创建,代理,框架和自动资源锁定/同步。

尽管如此,除非绝对必要,否则通常鼓励用户避免使用元类。

每次创建类时都使用元类:

当你编写类定义时,例如,像这样,

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

(关于类__module____module__的内容的__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')

注意,这个例子是从docs中改编而来的- 标准库中的新枚举就是这样做的。

所以我们所做的是通过创建一个类来实例化一个元类。我们也可以像对待任何其他类一样对待元类。它有一个方法解析顺序:

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



__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

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



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




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

无论如何,这两个是最常用的钩子。元类化是强大的,并且上面是远程接近和详尽的元类别用途列表。




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



Related