Python中的元类是什么?

时间:2020-03-06 14:24:52  来源:igfitidea点击:

元类是什么,我们将它们用于什么?

解决方案

注意,此答案适用于2008年编写的Python 2.x,3.x中的元类略有不同,请参见注释。

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

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

元类带有3个参数。 '名称','基础'和'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

请注意,通过使元类" init_attributes"获得" Initalized"所获得的魔术行为不会传递到Initalized的子类上。

这是一个更具体的示例,显示了如何子类化"类型"以使元类在创建类时执行操作。这很棘手:

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

我认为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 = 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__