当前位置:  开发笔记 > 编程语言 > 正文

什么是Python中的元类?

如何解决《什么是Python中的元类?》经验,为你挑选了15个好方法。

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



1> e-satis..:
Classes as objects

Before understanding metaclasses, you need to master classes in Python. And Python has a very peculiar idea of what classes are, borrowed from the Smalltalk language.

In most languages, classes are just pieces of code that describe how to produce an object. That's kinda true in Python too:

>>> class ObjectCreator(object):
...       pass
...

>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

But classes are more than that in Python. Classes are objects too.

Yes, objects.

As soon as you use the keyword class, Python executes it and creates an OBJECT. The instruction

>>> class ObjectCreator(object):
...       pass
...

creates in memory an object with the name "ObjectCreator".

This object (the class) is itself capable of creating objects (the instances), and this is why it's a class.

But still, it's an object, and therefore:

you can assign it to a variable

you can copy it

you can add attributes to it

您可以将其作为函数参数传递

例如:

>>> print(ObjectCreator) # you can print a class because it's an object

>>> def echo(o):
...       print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter

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

>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

但它不是那么有活力,因为你还是要自己写全班.

由于类是对象,因此它们必须由某些东西生成.

使用class关键字时,Python会自动创建此对象.但与Python中的大多数内容一样,它为您提供了手动执行此操作的方法.

记得功能type吗?一个很好的旧函数,可以让您知道对象的类型:

>>> print(type(1))

>>> print(type("1"))

>>> print(type(ObjectCreator))

>>> print(type(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)

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

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

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

And you can add even more methods after you dynamically create the class, just like adding methods to a normally created class object.

>>> def echo_bar_more(self):
...       print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

You see where we are going: in Python, classes are objects, and you can create a class on the fly, dynamically.

This is what Python does when you use the keyword class, and it does so by using a metaclass.

What are metaclasses (finally)

Metaclasses are the 'stuff' that creates classes.

You define classes in order to create objects, right?

But we learned that Python classes are objects.

Well, metaclasses are what create these objects. They are the classes' classes, you can picture them this way:

MyClass = MetaClass()
my_object = MyClass()

You've seen that type lets you do something like this:

MyClass = type('MyClass', (), {})

It's because the function type is in fact a metaclass. type is the metaclass Python uses to create all classes behind the scenes.

Now you wonder why the heck is it written in lowercase, and not Type?

Well, I guess it's a matter of consistency with str, the class that creates strings objects, and int the class that creates integer objects. type is just the class that creates class objects.

You see that by checking the __class__ attribute.

Everything, and I mean everything, is an object in Python. That includes ints, strings, functions and classes. All of them are objects. And all of them have been created from a class:

>>> age = 35
>>> age.__class__

>>> name = 'bob'
>>> name.__class__

>>> def foo(): pass
>>> foo.__class__

>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__

Now, what is the __class__ of any __class__ ?

>>> age.__class__.__class__

>>> name.__class__.__class__

>>> foo.__class__.__class__

>>> b.__class__.__class__

So, a metaclass is just the stuff that creates class objects.

You can call it a 'class factory' if you wish.

type is the built-in metaclass Python uses, but of course, you can create your own metaclass.

The __metaclass__ attribute

In Python 2, you can add a __metaclass__ attribute when you write a class (see next section for the Python 3 syntax):

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

If you do so, Python will use the metaclass to create the class Foo.

Careful, it's tricky.

You write class Foo(object) first, but the class object Foo is not created in memory yet.

Python will look for __metaclass__ in the class definition. If it finds it, it will use it to create the object class Foo. If it doesn't, it will use type to create the class.

Read that several times.

When you do:

class Foo(Bar):
    pass

Python does the following:

Is there a __metaclass__ attribute in Foo?

If yes, create in memory a class object (I said a class object, stay with me here), with the name Foo by using what is in __metaclass__.

If Python can't find __metaclass__, it will look for a __metaclass__ at the MODULE level, and try to do the same (but only for classes that don't inherit anything, basically old-style classes).

Then if it can't find any __metaclass__ at all, it will use the Bar's (the first parent) own metaclass (which might be the default type) to create the class object.

Be careful here that the __metaclass__ attribute will not be inherited, the metaclass of the parent (Bar.__class__) will be. If Bar used a __metaclass__ attribute that created Bar with type() (and not type.__new__()), the subclasses will not inherit that behavior.

Now the big question is, what can you put in __metaclass__ ?

The answer is: something that can create a class.

And what can create a class? type, or anything that subclasses or uses it.

Metaclasses in Python 3

The syntax to set the metaclass has been changed in Python 3:

class Foo(object, metaclass=something):
    ...

i.e. the __metaclass__ attribute is no longer used, in favor of a keyword argument in the list of base classes.

The behaviour of metaclasses however stays largely the same.

One thing added to metaclasses in python 3 is that you can also pass attributes as keyword-arguments into a metaclass, like so:

class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
    ...

Read the section below for how python handles this.

Custom metaclasses

The main purpose of a metaclass is to change the class automatically, when it's created.

You usually do this for APIs, where you want to create classes matching the current context.

Imagine a stupid example, where you decide that all classes in your module should have their attributes written in uppercase. There are several ways to do this, but one way is to set __metaclass__ at the module level.

This way, all classes of this module will be created using this metaclass, and we just have to tell the metaclass to turn all attributes to uppercase.

Luckily, __metaclass__ can actually be any callable, it doesn't need to be a formal class (I know, something with 'class' in its name doesn't need to be a class, go figure... but it's helpful).

So we will start with a simple example, by using a function.

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

Now, let's do exactly the same, but using a real class for a metaclass:

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

But this is not really OOP. We call type directly and we don't override or call the parent __new__. Let's do it:

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)

You may have noticed the extra argument upperattr_metaclass. There is nothing special about it: __new__ always receives the class it's defined in, as first parameter. Just like you have self for ordinary methods which receive the instance as first parameter, or the defining class for class methods.

Of course, the names I used here are long for the sake of clarity, but like for self, all the arguments have conventional names. So a real production metaclass would look like this:

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)

We can make it even cleaner by using super, which will ease inheritance (because yes, you can have metaclasses, inheriting from metaclasses, inheriting from type):

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)

Oh, and in python 3 if you do this call with keyword arguments, like this:

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

It translates to this in the metaclass to use it:

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

That's it. There is really nothing more about metaclasses.

The reason behind the complexity of the code using metaclasses is not because of metaclasses, it's because you usually use metaclasses to do twisted stuff relying on introspection, manipulating inheritance, vars such as __dict__, etc.

Indeed, metaclasses are especially useful to do black magic, and therefore complicated stuff. But by themselves, they are simple:

intercept a class creation

modify the class

return the modified class

Why would you use metaclasses classes instead of functions?

Since __metaclass__ can accept any callable, why would you use a class since it's obviously more complicated?

There are several reasons to do so:

The intention is clear. When you read UpperAttrMetaclass(type), you know what's going to follow

You can use OOP. Metaclass can inherit from metaclass, override parent methods. Metaclasses can even use metaclasses.

Subclasses of a class will be instances of its metaclass if you specified a metaclass-class, but not with a metaclass-function.

You can structure your code better. You never use metaclasses for something as trivial as the above example. It's usually for something complicated. Having the ability to make several methods and group them in one class is very useful to make the code easier to read.

You can hook on __new__, __init__ and __call__. Which will allow you to do different stuff. Even if usually you can do it all in __new__, some people are just more comfortable using __init__.

These are called metaclasses, damn it! It must mean something!

Why would you use metaclasses?

Now the big question. Why would you use some obscure error prone feature?

Well, usually you don't:

Metaclasses are deeper magic that 99% of users should never worry about. If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why).

Python Guru Tim Peters

The main use case for a metaclass is creating an API. A typical example of this is the Django ORM.

It allows you to define something like this:

class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

But if you do this:

guy = Person(name='bob', age='35')
print(guy.age)

It won't return an IntegerField object. It will return an int, and can even take it directly from the database.

This is possible because models.Model defines __metaclass__ and it uses some magic that will turn the Person you just defined with simple statements into a complex hook to a database field.

Django makes something complex look simple by exposing a simple API and using metaclasses, recreating code from this API to do the real job behind the scenes.

The last word

First, you know that classes are objects that can create instances.

Well in fact, classes are themselves instances. Of metaclasses.

>>> class Foo(object): pass
>>> id(Foo)
142630324

Everything is an object in Python, and they are all either instances of classes or instances of metaclasses.

Except for type.

type is actually its own metaclass. This is not something you could reproduce in pure Python, and is done by cheating a little bit at the implementation level.

Secondly, metaclasses are complicated. You may not want to use them for very simple class alterations. You can change classes by using two different techniques:

monkey patching

class decorators

99% of the time you need class alteration, you are better off using these.

But 98% of the time, you don't need class alteration at all.


似乎在Django`modeles.Model`中它不使用`__metaclass__`而是使用`类Model(metaclass = ModelBase):`来引用一个`ModelBase`类,然后执行前面提到的元类魔术.好帖子!这是Django的来源:https://github.com/django/django/blob/master/django/db/models/base.py#L382
这是社区维基的答案(因此,那些评论有更正/改进的人可能会考虑将他们的评论编辑到答案中,如果他们确定它们是正确的话).
@MaxGoodridge这是元类的Python 3语法.参见[Python 3.6数据模型](https://docs.python.org/3.6/reference/datamodel.html#metaclasses)VS [Python 2.7数据模型](https://docs.python.org/2.7/reference/ datamodel.html?#定制级的创作)
<<请注意,`__metaclass__`属性不会被继承,父类的元类(`Bar .__ class__`)将被继承.如果`Bar`使用`__metaclass__`属性创建`bar`和`type()`(而不是`type .__ new __()`),子类将不会继承该行为.>> - 你/有人请你这篇文章解释得更深一点?

2> Thomas Woute..:

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

虽然在Python中你可以为元类使用任意的callables(比如Jerub节目),实际上更有用的方法是使它成为一个真正的类本身.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__


ppperry他显然意味着你不能在不使用类型本身作为元类的情况下重新创建类型.这是公平的说法.
`class A(type):传递类B(类型,元类= A):传递 b .__ class__ = b`
请注意,Python 3不支持`__metaclass__`。在Python 3中使用`class MyObject(metaclass = MyType)`,请参见https://www.python.org/dev/peps/pep-3115/以及以下答案。

3> Jerub..:

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

请注意,通过使用元类Initialised来获得"初始化"的神奇行为不会传递给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



4> kindall..:

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



5> Antti Rasine..:

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

例如,如果你看一下Django模型,它们的定义看起来有点令人困惑.看起来好像只是定义了类属性:

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

但是,在运行时,Person对象充满了各种有用的方法.查看源代码,了解一些神奇的元素.


是不是使用元类将新属性和方法添加到**类**而不是实例?据我所知,元类改变了类本身,因此实例可以通过改变的类来不同地构造.对于试图获得元类性质的人来说可能有点误导.通过常规固有可以实现对实例的有用方法.尽管如此,对Django代码的引用仍然很好.

6> Matthias Kes..:

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



7> Aaron Hall..:

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

TLDR:元类实例化并定义类的行为,就像类实例化一样,并定义实例的行为.

伪代码:

>>> Class(...)
instance

以上应该看起来很熟悉.嗯,哪里Class来的?它是元类(也是伪代码)的一个实例:

>>> Metaclass(...)
Class

在实际代码中,我们可以传递默认的元类,type我们需要实例化一个类,然后我们得到一个类:

>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace

换句话说

类是一个实例,因为元类是一个类.

当我们实例化一个对象时,我们得到一个实例:

>>> object()                          # instantiation of class
     # instance


同样,当我们使用默认元类显式定义一个类时type,我们实例化它:

>>> type('Object', (object,), {})     # instantiation of metaclass
             # instance

换句话说,类是元类的实例:

>>> isinstance(object, type)
True

换句话说,元类是类的类.

>>> type(object) == type
True
>>> object.__class__

当您编写类定义并且Python执行它时,它使用元类来实例化类对象(反过来,它将用于实例化该类的实例).

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

它们可以用于什么?来自文档:

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

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

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

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

class Foo(object): 
    'demo'

您实例化一个类对象.

>>> 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__': , 
'__module__': '__main__', '__weakref__': , '__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

在编写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作为类的命名空间.如果它在Python 3中实现,我们将使用__prepare__它返回类的命名空间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')

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

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

>>> inspect.getmro(OrderedType)
(, , , )

并且它大致正确repr(除非我们能找到表示我们函数的方法,否则我们不能再进行评估).

>>> OrderedMethodsObject
OrderedType('OrderedMethodsObject', (object,), {'method1': , 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': , 'method2': , '__module__': '__main__', '__weakref__': , '__doc__': None, '__d
ict__': , 'method4': })



8> Ethan Furman..:

Python 3更新

(在这一点上)元类中有两个关键方法:

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

一个简单的例子:

假设您需要一些简单的验证代码来运行您的属性 - 就像它必须始终是一个int或一个str.没有元类,你的类看起来像:

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

示例运行:

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

生产:

9
Traceback (most recent call last):
  File "simple_meta.py", line 36, in 
    p.weight = '9'
  File "simple_meta.py", line 24, in __set__
    (self.name, self.type, value))
TypeError: weight must be of type(s)  (got '9')

注意:这个例子很简单,它也可以用类装饰器完成,但可能实际的元类会做得更多.

'ValidateType'类供参考:

class ValidateType:
    def __init__(self, type):
        self.name = None  # will be set by metaclass
        self.attr = None  # will be set by metaclass
        self.type = type
    def __get__(self, inst, cls):
        if inst is None:
            return self
        else:
            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



9> Michael Ekok..:
__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_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()
#  singleton returning from Meta_2.__call__(), skipping creation of new instance.

c = Class_2()
#  singleton returning from Meta_2.__call__(), skipping creation of new instance.

a is b is c # True



10> Craig..:

元类是一个类,它告诉我们应该如何(某些)创建其他类.

这是我看到元类作为我的问题的解决方案的情况:我有一个非常复杂的问题,可能已经解决了不同的问题,但我选择使用元类来解决它.由于其复杂性,它是我编写的少数模块之一,其中模块中的注释超过了已编写的代码量.这里是...

#!/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 docopt
            from libgsync.options import doc
            from libgsync import __version__

            options = docopt(
                doc.__doc__ % __version__,
                version = __version__,
                options_first = True
            )

            paths = options.pop('', 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_name
            import re
            name = re.sub(r'^__', "", re.sub(r'-', "_", name))
            listvalue = []

            # Ensure value is converted to a list type for GsyncListOptions
            if isinstance(value, list):
                if value:
                    listvalue = [] + value
                else:
                    listvalue = [ None ]
            else:
                listvalue = [ value ]

            type.__setattr__(GsyncListOptions, name, listvalue)

    # Cleanup this module to prevent tinkering.
    import sys
    module = sys.modules[__name__]
    del module.__dict__['GetGsyncOptionsType']

    return GsyncOptionsType

# Our singlton abstract proxy class.
class GsyncOptions(object):
    __metaclass__ = GetGsyncOptionsType()



11> Mushahid Kha..:

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 : 
Defining class 
get_choch :'dairy'
__module__ :'builtins'
__qualname__ :'NewClass'

Note:

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



12> noɥʇʎԀʎzɐɹƆ..:

tl;博士版

type(obj)函数可以获取对象的类型.

type()一类是它的元类.

要使用元类:

class Foo(object):
    __metaclass__ = MyMetaClass



13> Xingzhou Liu..:

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": , "foo": }

      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

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



14> binbjz..:

type()函数可以返回对象的类型或创建新类型,

例如,我们可以使用type()函数创建一个Hi类,而不需要在类Hi(object)中使用这种方式:

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是大多数内置类的元类和用户定义类的元类.

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

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



15> ARGeo..:

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

推荐阅读
LEEstarmmmmm
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有