提问



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

最佳参考


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


[276]


虽然在Python中你可以为元类使用任意的callables(比如Jerub节目),实际上更有用的方法是使它成为一个真正的类本身。 type是Python中常用的元类。如果你想知道,是的,type本身就是一个类,它是它自己的类型。你不能仅仅用Python重新创建type之类的东西,但是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__

其它参考1


作为对象的类



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


嗯,type具有完全不同的能力,它也可以动态创建类。 type可以将类的描述作为参数,
并返回一个班级。[278]


(我知道,根据您传递给它的参数,相同的功能可以有两种完全不同的用途,这很愚蠢。这是一个问题,因为倒退
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)
<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 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


在动态创建类之后,您可以添加更多方法,就像向正常创建的类对象添加方法一样。


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


__metaclass__属性



您可以在编写类时添加__metaclass__属性:[279]


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


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


小心,这很棘手。


首先编写class Foo(object),但不创建类对象Foo
在记忆中。


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


多读一遍。


当你这样做时:


class Foo(Bar):
    pass


Python执行以下操作:


Foo中有__metaclass__属性吗?


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


如果Python找不到__metaclass__,它将在MODULE级别寻找__metaclass__,并尝试做同样的事情(但仅适用于不继承任何内容的类,基本上是旧式类)。


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


这里要小心,__metaclass__属性不会被继承,父类的元类(Bar.__class__)将被继承。如果Bar使用Bar创建Bartype()(而不是type.__new__()),则子类不会继承该行为。


现在最大的问题是,你能在__metaclass__中加入什么?


答案是:可以创建一个类的东西。


什么可以创造一个类? type,或任何子类或使用它的东西。


自定义元类



元类的主要目的是自动更改类,
当它被创造出来的时候。


您通常会在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_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'


现在,让我们完全相同,但使用真正的类来进行元类:


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


但这不是真正的OOP。我们直接调用type而不是覆盖
或致电父__new__。我们开始做吧:


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)


你可能已经注意到额外的论点upperattr_metaclass。有
没什么特别的:__new__​​]]总是收到它定义的类,作为第一个参数。就像你有self接收实例作为第一个参数的普通方法,或者类的定义类方法。


当然,为了清楚起见,我在这里使用的名称很长,但是喜欢
对于self,所有参数都有常规名称。所以真正的生产
元类看起来像这样:


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)


我们可以通过使用super来使它更清晰,这将简化继承(因为是的,你可以有元类,继承自元类,继承自类型):


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)


这就是它。实际上没有更多关于元类的信息。


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


实际上,元类特别适用于黑魔法,因此
复杂的东西。但它们本身很简单:



  • 拦截班级创作

  • 修改课程

  • 返回修改后的课程



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



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


有几个原因可以这样做:



  • 目的很明确。当你阅读UpperAttrMetaclass(type)时,你知道
    接下来要做什么

  • 您可以使用OOP。 Metaclass可以从元类继承,覆盖父方法。元类甚至可以使用元类。

  • 如果您指定了元类,但没有使用元类函数,则类的子类将是其元类的实例。

  • 您可以更好地构建代码。你从不使用元类作为
    像上面的例子那样微不足道。这通常是为了复杂的事情
    制作多个方法并将它们分组到一个类中的能力非常有用
    使代码更容易阅读。

  • 你可以挂钩__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()


但是如果你这样做:


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


它不会返回IntegerField对象。它将返回int,甚至可以直接从数据库中获取它。


这是可能的,因为models.Model定义__metaclass__
它使用了一些魔法,可以通过简单的语句转换刚刚定义的Person
到数据库字段的复杂挂钩。


Django通过公开一个简单的API使复杂的东西变得简单
并使用元类,从此API重新创建代码以完成实际工作
在幕后。


最后一个字



首先,您知道类是可以创建实例的对象。


事实上,类本身就是实例。元类。


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


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


除了type


type实际上是它自己的元类。这不是你能做到的
在纯Python中重现,并通过在实现中作弊一点来完成
水平。


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



  • 猴子补丁

  • 类装饰器



99%的时间你需要改变课程,你最好不要使用这些。[280]


但是,98%的时间,你根本不需要改变课程。

其它参考2


注意,这个答案适用于Python 2.x,因为它是在2008年编写的,元数据在3.x中略有不同,请参阅注释。


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


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


元类需要3个参数。 名称基础 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的子类。


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


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

其它参考3


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


例如,如果你看看Django模型,他们的定义看起来有点令人困惑。看起来好像只是定义了类属性:[281]


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


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

其它参考4


其他人已经解释了元类如何工作以及它们如何适合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


我认为ONLamp对元类编程的介绍写得很好,并且尽管已有几年的历史,却给出了很好的主题介绍。


http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html[283]


简而言之:类是创建实例的蓝图,元类是创建类的蓝图。可以很容易地看出,Python类中也需要是第一类对象来启用此行为。


我自己从未写过一个,但我认为在Django框架中可以看到元类的最好用法之一。模型类使用元类方法来启用编写新模型或表单类的声明式样式。而元类是创建类,所有成员都可以自定义类本身。[284]



  • 创建新模型

  • 启用此
  • 的元类


剩下要说的是:如果你不知道什么是元类,你不需要它们的可能性是99%。[285] [286]

其它参考6



  

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




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


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


它们可以用于什么?来自文件:[287]



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



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


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



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


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__在那里,因为我们不t define __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__和[[]]的以下简单实现226]]我们的类类的类实例为我们提供了一个可以改进类的默认__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__,我们有更强的调试代码的能力。但是,m用eval(repr(Class))进一步检查是不太可能的(因为函数不可能从它们的默认值__repr__中得到评估)。


预期用法:__prepare__命名空间



例如,如果我们想知道创建类的方法的顺序,我们可以提供一个有序的dict作为类的名称空间。我们将使用__prepare__执行此操作,返回命名空间dict为该类是否在Python 3中实现:[289]


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


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


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


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

其它参考7


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)


一个简单的例子:


假设您希望在您的属性上运行一些简单的验证代码 - 例如它必须始终是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 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')





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


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

其它参考8


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


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


#!/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('<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_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()

其它参考9


创建类实例

时元类的__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.__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__()方法并初始化为实例s __init__()。它也是最终返回实例的那个。


从上面可以看出,元类s __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

其它参考10


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


Note:


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

其它参考11


tl; dr version



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


类的type()是元类。


要使用元类:


class Foo(object):
    __metaclass__ = MyMetaClass

其它参考12


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


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

其它参考13


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 (),此时,我们可以修改类定义,例如,添加一个新方法,然后返回修改后的定义。

其它参考14


掌握Python最困难知识点的两句话:Metaclass



原始来源:segmentfault.com/a/1190000011447445 [292]


由我翻译和纠正。


本文的原始作者保留所有权利,但翻译工作仍然不容忽视


如果PEP8存在某些错误或格式,请帮我纠正。感谢!!!


刚开始时,有一些中国传统文化的例子(我不是中国人,但我对它有一些了解。如果你喜欢它,那就很好。如果你不这样做,那就不要理会了对元类的理解是最重要的事情)


它简要介绍了Python中的Metaclass,并提供了一些实用且有用的示例。希望你会喜欢它。


不要被像99%的Python程序员不使用元类的所谓特征这样的修辞所吓倒。因为每个人都是自然用户。


要理解元类,您只需要知道两个句子:


第1句:一个来自真理,两个来自一个,三个来自两个,所有的东西来自三个


句子2:我是谁?我是从哪里来的?我该去哪儿?


在蟒蛇世界中,有一个永恒的真理,就是类型,请记住,类型是真理。如此庞大的python生态系统是按类型生成的。


一个来自真理,两个来自一个,三个来自两个,所有的东西都来自三个:


事实是类型


一个是元类(元类或类生成器)


第二个是类(类或实例生成器)


三是一个实例(例子)


一切都是实例的各种属性和方法。当我们使用Python时,我们称之为。


真理和一个是我们今天讨论的主张。第二个,第三个和所有东西都是我们经常使用的类,实例,属性和方法。我们以hello world为例:


# Create a Hello class that has the attributes say_hello ---- Second Origin

class Hello () :

     def say_hello ( self ,name= 'world' ) :

         print( 'Hello, %s.' % name )





# Create an instance hello from the Hello class ---- Two students three

Hello = Hello ()



# Use hello to call the method say_hello ---- all three things

Hello.say_hello () 


输出效果:


Hello, world. 


这是一个标准的三个来自两个,所有的东西都来自三个的过程。从类到我们可以调用的方法,使用这两个步骤。


那么我们无法从主要问题中得到帮助,这个类来自哪里?回到第一行代码。


Hello类实际上是函数的语义缩写,只是为了使代码更容易理解。另一种写作方式是:


def  fn(self ,name='world' ) : # If we have a function called fn
     print ( 'Hello, %s.' % name )


Hello = type ('Hello',(object,),dict(say_hello = fn))
# Create Hello class by type ---- Mysterious "Truth", you can change everything, this time we directly from the "Truth" gave birth to "2" 


这种类型的写作与之前的类Hello编写完全相同。您可以尝试创建一个实例并调用它。


# Create an instance of hello from the Hello class.

hello = Hello ()



# Use hello call method say_hello ---- all three things, exactly the same

Hello . say_hello () 


输出效果:


Hello, world. ---- The result of the call is exactly the same. 


我们回顾了最激动人心的地方。这条路直接催生了两个:


Hello = type('Hello', (object,), dict(say_hello=fn)) 


这就是真理,是蟒蛇世界的起源。你可以惊叹于此。


注意它的三个参数!三个永恒的命题与人类相吻合:我是谁,我从哪里来,我该去哪里?


The first parameter: who I am. Here, I need a name that distinguishes everything else. The above example names me "Hello."

The second parameter: where do I come from. Here, I need to know where I come from, which is my "parent". In my example above, my parent is "object" - a very primitive class in Python.

The third parameter: Where do I go? Here, we include the methods and properties that we need to call into a dictionary and pass them as parameters. In the above example, we have a say_hello method packed into a dictionary. 


值得注意的是,三个主要的永恒命题是所有类,所有实例,甚至所有实例属性和方法。应该如此,他们的创造者,真理和一个,即类型和元类,也有这三个参数。但通常,该类的三个永恒命题不作为参数传递,而是按如下方式传递


class Hello(object):{

After class # statement "Who Am I?"

# In the parentheses declare "where do I come from"

# In curly brackets declare "Where do I go?"

     def say_hello ():{



     }

} 


The Creator can create a single person directly, but this is a hard labor. The Creator will first create the species "human" and then create a specific individual in batches. And pass on the three eternal propositions.

"Truth" can produce "2" directly, but it will produce "1" and then make "2" in batches.

Type can directly generate a class, but it can also be a metaclass and then use a metaclass to customize a class. 


Metaclass - 一个来自真理,两个来自一个。


通常,元类名为后缀Metaclass。想象一下,我们需要一个可以自动打招呼的元类。其中的类方法,有时候需要say_Hello,有时候说_Hi,有时候说是_ayolala,有时候说_Nihao。


如果每个内置的say_xxx需要在一个类中声明一次,那么它将是多么艰苦的工作!最好使用元类来解决问题。


以下是用于创建特殊问候的元类​​代码:


class SayMetaClass(type):



     def __new__ (cls, Name ,Bases ,Attrs) :

         attrs[ 'say_' + name ] = lambda   self, value , saying = name : print ( saying + ',' + value + '!' )

         Return   Type . __new__ ( cls ,name, bases ,   attrs) 


记住两件事:


Metaclasses are derived from "type", so the parent class needs to pass in the type. [Taosheng 1, so one must include Tao]

Metaclass operations are done in __new__. The first parameter is the class that will be created. The following parameters are the three eternal propositions: Who am I, where do I come from, and where do I go. The objects it returns are also the three eternal propositions. Next, these three parameters will always be with us.


中,我只执行了一项操作。


Attrs['say_'+name] = lambda self,value,saying=name: print(saying+','+value+'!') 


它使用类的名称创建一个类方法。例如,我们从元类创建的类称为Hello。创建它时,它会自动拥有一个名为say_Hello的类方法。然后它将使用类名Hello作为默认参数来表示,并将其传递给方法。然后将hello方法调用作为值传入,最后将其打印出来。


那么元类如何从创建到调用呢?


来!结合道生,益生友,璧生三,三生物的原则,进入元级的生命周期!


# Tao Shengyi: incoming type

class SayMetaClass(type):



     # Incoming three eternal propositions: class name, parent class, attribute

     def __new__(cls ,name ,bases ,attrs):

         # Create "talent"

         attrs[ 'say_' + name ] = lambda   self, value , saying = name : print( saying + ',' + value + '!' )

         # Three eternal propositions: class name, parent class, attribute

         return type . __new__ ( cls ,name ,bases ,attrs )



# Lifetime 2: Create class

class Hello ( object ,metaclass = SayMetaClass):
     pass



# two students three: create a real column

Hello = Hello ()



# Three things: call the instance method

hello.say_Hello('world!') 


输出是


Hello, world! 


注意:由元类创建的类,第一个参数是父类,第二个参数是元类


普通人在出生时就无法说话,但有些人在出生时会打招呼,你好和sayolala。这是人才的力量。它将为我们提供面向对象的编程,以挽救无数的麻烦。


现在,保持元类不变,我们可以继续创建Sayolala,Nihao类,如下所示:


# Two came from one: Create class

class Sayolala ( object ,metaclass = SayMetaClass ) :
    pass



# three came from two: create a real column

s = Sayolala ()



# all things came from two: call the instance method

s.say_Sayolala ( 'japan!' ) 


产量


Sayolala, japan! 


也会说中文


# Two came from one: Create class

class Nihao(object ,metaclass = SayMetaClass ) :
    pass



# two students three: create a real column

n = Nihao()



# Three things: call the instance method

n.say_Nihao ( '中 中华!' ) 


产量


Nihao, China! 


另一个小例子:


# one came from truth.

class ListMetaclass (type) :

     def   __new__ ( cls ,name, bases ,   attrs) :

         # Talent: Bind values ​​by the add method

         attrs[ 'add' ] = lambda   self, value: self.append(value)

         return type . __new__ ( cls ,name ,bases ,attrs )



# One lifetime

class MyList ( list ,   Metaclass = ListMetaclass ) :
    pass



# Two students three

L = MyList ()



# Three things

L.add( 1 ) 


现在我们打印L


print(L)



>>> [ 1 ] 


普通列表没有add()方法


L2 = list ()

L2 . add ( 1 )



>>> AttributeError : 'list'   Object   Has no attribute   'add' 


真棒!在这里学到了,你是否经历过造物主的快乐?


Everything in the python world is at your fingertips. 


年轻的创造者,请跟我来创造一个新的世界。


我们选择两个方面,一个是Django的核心思想,对象关系映射,对象关系映射,简称ORM。


这是一个主要的Django难度,但在学习了元类后,一切都变得清晰。您对Django的理解会更好!


另一个领域是爬虫(黑客),自动搜索网络上的可用代理,然后更改IP以打破其他人的反爬虫限制。


这两项技能非常实用,非常有趣!


挑战1:通过Metaclass创建ORM


准备创建一个Field类


class Field ( object ) :
     def __init__ ( self, name, column_type ) :

         Self.name = name

         Self.column_type = column_type



     def   __str__ ( self ) :

         return   '<%s:%s>' % ( self . __class__ . __name__ ,   self. name ) 


它的作用是


实例化Field类时,它将获得两个参数,name和column_type。它们将绑定到Field的私有属性。如果要将Field转换为字符串,它将返回Field:XXX.XXX将被传入。名称名称。


准备:创建StringField和IntergerField


class StringField ( Field ) :



    def   __init__ ( self ,   name ) :

         super( StringField ,   self). __init__ ( name ,   'varchar(100)' )



class IntegerField ( Field ) :
     def   __init__ ( self ,name) :

         super( IntegerField ,   self). __init__ ( name ,   'bigint' ) 


它的作用是


初始化StringField,IntegerField实例时,将自动调用父级的初始化方法。


一个来自真相


class ModelMetaclass ( type ) :



     def __new__ ( cls ,name, bases ,   attrs) :

         Ifname== 'Model' :

             Return   Type . __new__ ( cls ,name, bases ,   attrs)

         print( 'Found model: %s' % name )

         Mappings = dict ()

         for k ,   v   In   attrs. items () :

             If   Isinstance ( v ,   Field ) :

                 print( 'Found mapping: %s ==> %s' % ( k ,   v ))

                 Mappings [ k ] = v

         for k   In   Mappings . keys () :

             attrs. pop ( k )

         attrs[ '__mappings__' ] = mappings   # Save the mapping between attributes and columns

         attrs[ '__table__' ] = name   # Assume that the table name and class name are the same

         Return   Type . __new__ ( cls ,name, bases ,   attrs) 


它做了以下事情


Create a new dictionary mapping

Each property of the class is traversed through its .items() key-value pair. If the value is a Field class, the key is printed and the key is bound to the mapping dictionary.

Delete the property that was just passed in as the Field class.

Create a special __mappings__ attribute and save the dictionary mapping.

Create a special __table__ attribute and save the name of the passed in class. 


两个来自一个


class Model ( dict ,   Metaclass = ModelMetaclass ) :



     def __init__ ( self , ** kwarg ) :

         super(model ,   self). __init__ ( ** kwarg )



     def __getattr__ ( self ,   Key ) :

         Try :

             Return   self[ key ]

         except KeyError :

             Raise   AttributeError ( "'Model' object has no attribute '%s'" % key )



     def __setattr__ ( self ,   Key ,   Value ) :

         self[ key ] = value



     # Simulate table creation operation

     def save( self ) :

         Fields = []

         Args = []

         for k ,   v   In   self. __mappings__ . items () :

             Fields . append ( v . name )

             Args . append ( getattr ( self ,   k ,   None ))

         Sql = 'insert into %s (%s) values ​​(%s)' % ( self . __table__ ,   ',' . join ( fields ),   ',' . join ([ str ( i )   for i   In   Args ]))

         print( 'SQL: %s' % sql )

         print( 'ARGS: %s' % str ( args )) 


如果从模型创建子类User:


class User (model ) :

     # Define the mapping of attributes's attributes to columns:

     Id = IntegerField ( 'id' )

  name= StringField ( 'username' )

     Email = StringField ( 'email' )

     Password = StringField ( 'password' ) 


此时


Id=IntegerField(id)将自动解析为:


Model。 setattr (self,id,IntegerField(id))


因为IntergerField(id)是Field的子类的实例,所以元类的 new 会自动触发,因此IntergerField(id)存储在映射并删除键值对。


两个学生,三个学生,所有的东西


初始化实例并调用save()方法时


u = User ( id = 12345 ,name= 'Batman' ,   Email = 'batman@nasa.org' ,   Password = 'iamback' )

u . save () 


这时,两个学生的过程首先完成:


First call Model.__setattr__ to load key values ​​into private objects

Then call the "genius" of the metaclass, ModelMetaclass.__new__, and private objects in the Model, as long as they are instances of Field, are automatically stored in u.__mappings__. 


下一步是完成以下三件事:


通过u.save()模拟数据库存操作。这里我们只是做一点遍历映射操作,虚拟sql和打印,实际上,通过输入sql语句和数据库来运行。


输出是


Found model : User

Found mapping : name ==> < StringField : username >

Found mapping : password ==> < StringField : password >

Found mapping : id ==> < IntegerField : id >

Found mapping : email ==> < StringField : email >

SQL : insert into User   ( username , password , id , email )   Values   ( Batman , iamback , 12345 , batman @ nasa . org )

ARGS : [ 'Batman' ,   'iamback'   12345 ,   'batman@nasa.org' ] 


    Young Creator, you have experienced with me the great course of evolution of Everything from the Tao, which is also the core principle of the Model section in Django.

    Next, join me in a more fun reptile battle (well, you are now a junior hacker): crawling web agents! 


挑战二:网络代理的爬行


准备爬页玩


请确保已安装包,请求和pyquery。


# File: get_page.py

import Requests



Base_headers = {

     'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36' ,

     'Accept-Encoding' : 'gzip, deflate, sdch' ,

     'Accept-Language' : 'zh-CN,zh;q=0.8'

}





def Get_page ( url ) :

     Headers = dict ( base_headers )

     print( 'Getting' ,   Url )

     Try :

         r = requests . get ( url ,   Headers = headers )

         print( 'Getting result' ,   Url ,   r . status_code )

         If   r . status_code == 200 :

             Return   r .

     exceptConnectionError :

         print( 'Crawling Failed' ,   Url )

         Return   None 


在这里,我们使用请求包来爬出百度的源代码。


尝试尝试百度


将此段落在get_page.py后面并尝试删除


If ( __name__ == '__main__' ) :

     Rs = get_page ( 'https://www.baidu.com' )

     print( 'result: ' ,   Rs ) 


试着捕捉特工


将此段落在get_page.py后面并尝试删除


If ( __name__ == '__main__' ) :

     from Pyquery import   PyQuery as   Pq

     Start_url = 'http://www.proxy360.cn/Region/China'

     print( 'Crawling' ,   Start_url )

     Html = get_page ( start_url )

     If   Html :

         Doc = pq ( html )

         Lines = doc ( 'div[name="list_proxy_ip"]' ). items ()

         for Line in   Lines :

             Ip = line . find ( '.tbBottomLine:nth-child(1)' ). text ()

             Port = line . find ( '.tbBottomLine:nth-child(2)' ). text ()

             print( ip + ':' + port ) 


Next, go to the topic: Use the metaclass batch fetch proxy 


批处理爬网代理


from Getpage import   Get_page

from Pyquery import   PyQuery as   Pq





# one came from truth: Create metaclass of extraction agent

class ProxyMetaclass ( type ) :

     """

Metaclass, added in the FreeProxyGetter class

__CrawlFunc__ and __CrawlFuncCount__

Two parameters, which represent the crawler function and the number of crawler functions, respectively.

"""

     def __new__ ( cls ,name, bases ,   attrs) :

         Count = 0

         attrs[ '__CrawlFunc__' ] = []

         attrs[ '__CrawlName__' ] = []

         for k ,   v   In   attrs. items () :

             If   'crawl_'   In   k :

                 attrs[ '__CrawlName__' ]. append ( k )

                 attrs[ '__CrawlFunc__' ]. append ( v )

                 Count += 1

         for k   In   attrs[ '__CrawlName__' ] :

             attrs. pop ( k )

         attrs[ '__CrawlFuncCount__' ] = count

         Return   Type . __new__ ( cls ,name, bases ,   attrs)





# two came from one: Create an agent to get the class



class ProxyGetter ( object ,   Metaclass = ProxyMetaclass ) :

     def Get_raw_proxies ( self ,   Site ) :

         Proxies = []

         print( 'Site' ,   Site )

         for Func in   self. __CrawlFunc__ :

             If   Func . __name__ == site :

                 This_page_proxies = func ( self )

                 for Proxy in   This_page_proxies :

                     print( 'Getting' ,   Proxy ,   'from' ,   Site )

                     Proxies . append ( proxy )

         Return   Proxies





     def Crawl_daili66 ( self ,   Page_count = 4 ) :

         Start_url = 'http://www.66ip.cn/{}.html'

         Urls = [ start_url . format ( page )   for Page in   Range ( 1 ,   Page_count + 1 )]

         for Url in   Urls :

             print( 'Crawling' ,   Url )

             Html = get_page ( url )

             If   Html :

                 Doc = pq ( html )

                 Trs = doc ( '.containerbox table tr:gt(0)' ). items ()

                 for Tr in   Trs :

                     Ip = tr . find ( 'td:nth-child(1)' ). text ()

                     Port = tr . find ( 'td:nth-child(2)' ). text ()

                     Yield   ':' . join ([ ip ,   Port ])



     def Crawl_proxy360 ( self ) :

         Start_url = 'http://www.proxy360.cn/Region/China'

         print( 'Crawling' ,   Start_url )

         Html = get_page ( start_url )

         If   Html :

             Doc = pq ( html )

             Lines = doc ( 'div[name="list_proxy_ip"]' ). items ()

             for Line in   Lines :

                 Ip = line . find ( '.tbBottomLine:nth-child(1)' ). text ()

                 Port = line . find ( '.tbBottomLine:nth-child(2)' ). text ()

                 Yield   ':' . join ([ ip ,   Port ])



     def Crawl_goubanjia ( self ) :

         Start_url = 'http://www.goubanjia.com/free/gngn/index.shtml'

         Html = get_page ( start_url )

         If   Html :

             Doc = pq ( html )

             Tds = doc ( 'td.ip' ). items ()

             for Td in   Tds :

                 Td . find ( 'p' ). remove ()

                 Yield   Td . text (). replace ( ' ' ,   '' )





If   __name__ == '__main__' :

     # Two students three: Instantiate ProxyGetter

     Crawler = ProxyGetter ()

     print(crawler . __CrawlName__ )

     # Three things

     for Site_label in   Range ( crawler . __CrawlFuncCount__ ) :

         Site = crawler . __CrawlName__ [ site_label ]

         myProxies = crawler . get_raw_proxies ( site ) 


一个来自真理:在元类中,他做了四件事:


Push the name of the class method that starts with "crawl_" into ProxyGetter.__CrawlName__

Push the class method that starts with "crawl_" itself into ProxyGetter.__CrawlFunc__

Calculate the number of class methods that match "crawl_"

Delete all class methods that match "crawl_" 


how about it? Is it very similar to the __mappings__ process used to create an ORM? 


两个来自一个:该类定义了使用pyquery来获取页面元素的方法


页面上显示的每个代理都是从三个自由代理站点进行爬网的。


如果你不熟悉产量用法,请查看:廖雪峰的python教程:发生器


三个来自两个:创建实例对象爬虫





三件事:遍历每个 CrawlFunc


Above ProxyGetter.__CrawlName__, get the URL name that can be crawled.

Trigger class method ProxyGetter.get_raw_proxies(site)

Traverse ProxyGetter.__CrawlFunc__, if the method name and URL are the same, then execute this method

Integrate the proxy obtained from each URL into an array output. 


So. . . How to use bulk agents, impact other people's websites, capture other people's passwords, frantically advertise water stickers, and regularly harass customers? Uh! Think it! These self-realization! If you do not realize it, please listen to the next decomposition! 


年轻的造物主,创造世界的工具,已经在你手中。请充分利用它的力量!


记住挥舞工具的嘴巴:


One came from truth, two came from one, three came from two, all the thing came from three.

Who am I, where do I come from, where do I go