Python很简单?学会魔术方法才算入门!

 点击上方“中兴开发者社区”,关注咱们python

天天读一篇一线开发者原创好文640?wx_fmt=png&wxfrom=5&wx_lazy=1git

Python中的元类是什么?github

原问题地址:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python数据库

问题

什么是元类?使用它们能作什么?编程

答案 1

元类是类的一种。正如类定义了实例功能,元类也定义了类的功能。类是元类的实例。ide

在Python中你能够随意调用元类(参考Jerub的回答),实际上更有用的方法是使其自己成为一个真正的类。type是Python中经常使用的元类。你可能以为奇怪,是的,type自己就是一个类,并且它就是type类型。你没法在Python中从新创造出彻底像type这样的东西,但Python提供了一个小把戏。你只须要把type做为子类,就能够在Python中建立本身的元类。函数

元类是最经常使用的一类工厂。就像你经过调用类来建立类实例,Python也是经过调用元类来建立一个新类(当它执行class语句的时候),结合常规的 __init____new__方法,元类能够容许你在建立类的时候作一些“额外的事情”,like registering the new class with some registry(暂时不知道这句话的含义,不知道怎么翻译,字面意思是:就像用某个注册表来注册新的类那样),甚至能够用别的东西彻底代替已有的类。优化

当Python执行class语句时,它首先把整个的class语句做为一个正常的代码块来执行。由此产生的命名空间(一个字典)具备待定类的属性。元类取决于待定类的基类(元类是具备继承性的)、或待定类的__metaclass__属性(若是有的话)或__metaclass__全局变量。接下来,用类的名称、基类和属性调用元类,从而把元类实例化。this

然而,元类实际上定义的一个类的类型,而不仅是类工厂,因此你能够用元类来作更多的事情。例如,你能够定义元类的通常方法。这些元类方法和类方法有类似之处,由于它们能够被没有实例化的类调用。但这些元类方法和类方法也有不一样之处,元类方法不能在类的实例中被调用。type.__subclasses__()是关于type的一个方法。你也能够定义常规的“魔法”函数,如__add__, __iter____getattr__,以便实现或修改类的功能。spa

摘抄一个例子:

def make_hook(f):
 """Decorator to turn 'foo' method into '__foo__'"""
 f.is_hook = 1
 return f

class MyType(type):
 def __new__(cls, 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, cls).__new__(cls, 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__
shareedit

答案 2

做为对象的类

在理解元类以前,你须要掌握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
...

这个对象(类)自己就可以建立一些对象(实例),这就是为何它是类。

但它仍然是一个对象,于是:

  • 你能够将它分配给一个变量

  • 你能够复制它

  • 你能够增长它的属性

  • 你能够把它做为一个功能参数来用

例如:

>>> 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>
Creating classes dynamically

类的动态建立

既然类是对象,你就能动态地建立它们,就像建立任何对象那样。

首先,你能够在一个使用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能够把对于类的描述做为参数,并返回一个类。

(一样的函数根据你传入的参数而有彻底不一样的用途。我知道这看起来有点怪。这是由于Python向后兼容。)

type是这样应用的:

例如:

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

你如今明白了:在Python中,类就是对象,你能够动态地建立类。

这就是关键字class在Python中的应用,它是经过使用元类来发挥做用。

元类是什么

元类是用来建立类的东西。

你经过定义类来建立对象,对吧?

但咱们知道Python的类就是对象。

元类用于建立这些对象,元类是类的类,你能够这样来描述它们:

MyClass = MetaClass()
MyObject = MyClass()

你已经看到了type能够这样来用:

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

这是由于type其实是一个元类,做为元类的type在Python中被用于在后台建立全部的类。

如今你感到疑惑的是为何这里是小写的type,而不是大写的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__属性:

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

若是你这样作,Python会使用元类来建立Foo这个类。

当心,这是棘手的。

这是你是首次建立class Foo(object),可是类对象Foo在内存中尚未被建立。

Python会在类定义中寻找__metaclass__。若是找到它,Python会用它来建立对象类Foo。若是没有找到它,Python将使用type来建立这个类。

把上面的话读几遍。

当你写下:

class Foo(Bar):
 pass

Python会实现如下功能:

Foo有没有__metaclass__的属性?

若是有,经过借鉴__metaclass__,用Foo这个名字在内存中建立一个类对象(我说的是一个类对象,记住个人话)。

若是Python找不到__metaclass__,它会在模块层级寻找__metaclass__,并尝试作一样的事情(但这只适用于不继承任何东西的类,基本上是旧式类)。

若是它根本找不到任何__metaclass__,它将使用Bar(第一个父类)本身的元类(这多是默认的type)来建立类对象。

当心点,__metaclass__属性不会被继承,而父类的元类(Bar.__class__)将会被继承。若是Bar所用的__metaclass__属性是用type()来建立Bar(而不是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)

但这不是真正的面向对象编程。咱们直接调用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__upperattr_metaclass为第一参数,而且在upperattr_metaclass中被定义。就像你把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使它更清楚,这样将缓解继承(由于,是的,你能够拥有元类,继承metaclasses,继承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)

就是这样。关于元类真的是没有更多要讲的了。

使用元类的代码复杂的缘由并不在于元类,那是由于你一般用元类来实现一些奇怪的功能,而这些功能要依靠自省、继承、变量,如:__dict__等。

的确,元类对于“魔法”特别有用,这些事务是复杂的,但元类自己很简单:

  • 拦截类的建立

  • 修改类

  • 返回修改后的类

你为何要使用元类而不是函数?

__metaclass__能够被任意调用。既然类明显更复杂,你为何还要使用它呢?

这样作有几个理由:

  • 目的明确。当你读UpperAttrMetaclass(type)时,你知道要遵循的是什么。

  • 可使用面向对象编程。元类能够继承元类、重写父类的方法。元类甚至可使用元类。

  • 能够优化代码。你从不为像上面例子中琐碎的东西而使用元类。它一般用于复杂的东西。在一个类中有多种方法而且将它们优化组合,是很是有价值的,使得代码可读性更强。

  • 你可能喜欢使用__new____init____call__。它们能帮你实现不一样的功能。尽管你一般能够用__new__来实现全部的功能,有些人仍是更喜欢使用__init__

  • 这些被称为元类。可恶!它确定意味着什么!
    你为何会使用元类?

如今的大问题是:为何你会使用一些复杂难懂、容易出错的特性?

嗯,一般你不会这样作:

元类是更深层次的魔法,超过99%的用户不须要担忧。不要怀疑你是否须要元类(那些真正须要元类的人对此肯定无疑,而且不须要解释为何)。
——Python专家蒂姆﹒彼得斯

元类的主要使用案例是建立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转化成一个复杂的钩子链接到数据库字段。

经过显示一个简单的API和元类,Django使复杂的东西看起来简单,再从API重构代码去完成真正的幕后工做。

最后的话

首先,你知道,类是能够建立实例的对象。

事实上,类自己就是实例。在元类中

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

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

可是type除外。

type其实是本身的元类。你没法在纯粹的Python中复制它,因此只能在实施层面作点小把戏。

其次,元类是复杂的。你可能不想把它们用于很是简单的类。你能够用两种不一样的技术来改变类:

  • 猴子补丁monkey patching

  • 类装饰器

当你须要改变类的时候,99%的状况下,使用它们是明智之举。

但99%的时间,你根本不须要改变类。

转自:https://github.com/qiwsir/StackOverFlowCn/blob/master/302.md

640?wx_fmt=jpeg