什么是甲状腺?它们用于什么?


当前回答

在 Python 或任何其他语言中,我们对每个变量或对象都有一个类型. 在 Python 中,我们可以使用 Type() 函数来获得任何类型(变量、对象等)。

通过在课堂定义中的 meta 类关键词,我们可以自定义课堂创建过程。

class meta(type):
    pass
class baseclass(metaclass=meta): # This is Mestaclass
    pass
class derivedclass(baseclass):
    pass
print(type(meta))
print(type(baseclass))
print(type(derivedclass))

在定义新类时,如果没有定义的甲型,则使用默认类型甲型;如果一个甲型不是类型(例)的对象(例),则在这种情况下,它直接用作甲型。

其他回答

当班级声明执行时,Python 首先将班级声明的身体作为一个正常的代码块执行。 结果的名称空间(dict)保留了班级的属性. 金属阶级通过观察班级的基层(金属阶级继承),在 __金属阶级__属性的班级(如果有)或 __金属阶级__全球变量来确定。

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 3 更新

在一个甲状腺中,有(目前)两个关键方法:

__prepare__ 允许您提供自定义地图(如 OrderedDict)作为名称空间使用,而类正在创建。

__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. 没有一个 metaclass,你的类会看起来像:

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)

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

注意:这个例子是简单的,它也可能已经完成了一个类装饰师,但假设一个真正的金属玻璃会做得更多。

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

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

class SomeClass(object):
    # ...
    # some definition here ...
    # ...

    def __call__(self, foo, bar):
        return bar + foo

但是,正如我们从以前的答案中看到的那样,一个类本身就是一个金属类的例子,所以当我们使用这个类作为一个金属类(即当我们创建一个例子时),我们实际上称它为金属类的 __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

这是一个使用这个MetaClass的班级。

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__()."

现在,让我们创建一个类_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.

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

从上述情况下,它表明,MetaClass的 __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

除了发布的答案,我可以说,一个甲状腺可以定义一个类的行为,所以,你可以明确设置你的甲状腺,每当Python获得一个关键词类,然后它开始搜索甲状腺,如果它没有找到 - 默认甲状腺类型用于创建一个类的对象,使用 __metaclass__属性,你可以设置你的甲状腺类:

class MyClass:
   __metaclass__ = type
   # write here other method
   # write here one more method

print(MyClass.__metaclass__)

它将产生这样的产量:

class 'type'

当然,你可以创建自己的金属类来定义使用你的类创建的任何类的行为。

要做到这一点,您的默认金属类型类必须继承,因为这是主要金属类:

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'

上面的答案是正确的。

但读者可能来到这里寻找关于类似名称的内部课程的答案,他们在受欢迎的图书馆,如Django和WTForms。

相反,这些是班级的命令之内的名称空间,它们是用内部班级为可读性而建造的。

在这个特殊的例子领域,抽象是显而易见地与作者模型的领域分开。

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 Form
from wtforms.csrf.session import SessionCSRF
from wtforms.fields import StringField

class MyBaseForm(Form):
    class Meta:
        csrf = True
        csrf_class = SessionCSRF

    name = StringField("name")

这个合成不会在Python编程语言中得到特别的处理. Meta 不是这里的一个关键词,也不会引发 meta 类行为. 相反,第三方图书馆代码在 Django 和 WTForms 等包中,在某些类的构建者和其他地方读到这个属性。

这些声明的存在改变了具有这些声明的类别的行为. 例如,WTForms 阅读 self.Meta.csrf 以确定表格是否需要一个 csrf 字段。