stackoverflow上的答案,最高票的答案说得很清楚,同时网上也有人对此进行了翻译。我对其进行翻译主要是在过程中读清楚具体讲了什么,并且实际编写代码测试其正确性和解除自己的疑惑(此处在另外博客中)。
Python中的类也是对象
在了解元类之前,你需要掌握Python中的类。Python中类的概念借鉴于Smalltalk编程语言并且有些奇特。
在大部分的语言中,类只是描述如何产生一个对象的代码片段,在Python中这点也成立。
In [1]: class ObjectCreator(object):
...: pass
...:
In [2]: my_object = ObjectCreator()
In [3]: print (my_object)
<__main__.ObjectCreator object at 0x10bfa06d0>
但是在Python中类并不只是这样,类本身也是对象。是的,对象。
每当你使用关键词class,Python在执行的时候会产生一个对象。如下的代码:
class ObjectCreator(object):
pass
会在内存中产生一个对象,名字为ObjectCreator。
这个对象(该类)自身能够创建对象(实例),这就是为什么称其为类的原因。但是同时类也是对象,因此你能够:
- 将其赋值给一个变量
- 拷贝它
- 为它添加属性
- 将其作为对象参数传递
示例如下:
In [4]: class ObjectCreator(object):
...: pass
...:
In [5]: print(ObjectCreator)
<class '__main__.ObjectCreator'>
In [6]: def echo(o):
...: print o
...:
In [7]: echo(ObjectCreator)
<class '__main__.ObjectCreator'>
In [8]: print(hasattr(ObjectCreator, 'new_attribute'))
False
In [9]: ObjectCreator.new_attribute = 'foo'
In [10]: print(hasattr(ObjectCreator, 'new_attribute'))
True
In [11]: print(ObjectCreator.new_attribute)
foo
In [12]: ObjectCreatorMirror = ObjectCreator
In [13]: print(ObjectCreatorMirror.new_attribute)
foo
In [14]: print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x10bfa0750>
动态地创建类
由于类也是对象,所以你能够动态地创建它们,像任何对象一样。
首先,你能够通过使用class在一个函数中创建一个类:
In [1]: def choose_class(name):
...: if name == 'foo':
...: class Foo(object):
...: pass
...: return Foo
...: else:
...: class Bar(object):
...: pass
...: return Bar
...:
In [2]: MyClass = choose_class('foo')
In [3]: print(MyClass)
<class '__main__.Foo'>
In [4]: print(MyClass())
<__main__.Foo object at 0x10d996310>
但是这还不够动态,因为你仍然不得不自己去编写这个类。由于类是对象,所以它们必然是由其它的一些东西生成的。当你使用class关键词的时候,Python自动创建了该对象。但是像Python中大部分的事情一样,Python给你提供了手动处理的方法。
还记得type函数吗?这个能够让你明白一个对象类型的古老而强大的函数。
In [2]: print type(1)
<type 'int'>
In [3]: print type("1")
<type 'str'>
In [4]: print type(ObjectCreator)
<type 'type'>
In [5]: print type(ObjectCreator())
<class '__main__.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包含名字和值的属性的字典)
实际的type定义中也有
def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
"""
type(object) -> the object's type
type(name, bases, dict) -> a new type
# (copied from class doc)
"""
pass
例如:
In [6]: class MyShinyClass(object):
...: pass
...:
In [7]: MyShinyClass = type('MyShinyClass', (), {})
In [8]: print MyShinyClass
<class '__main__.MyShinyClass'>
In [9]: print MyShinyClass()
<__main__.MyShinyClass object at 0x103d51350>
你可以看到我们使用“MyShinyClass”作为类的名字,也可以当做一个变量来作为类的引用。类和变量并不相同,但是我们没有理由把事情弄复杂。
type接受一个字典来定义类的属性,所以:
In [10]: class Foo(object):
....: bar = True
能够被写为:
In [11]: Foo = type('Foo', (), {'bar': True})
并且被像一个正常类一样使用:
In [12]: Foo = type('Foo', (), {'bar': True})
In [13]: print Foo
<class '__main__.Foo'>
In [14]: print Foo.bar
True
In [15]: f = Foo()
In [16]: print f
<__main__.Foo object at 0x103d51850>
In [17]: print f.bar
True
当然你可以继承它,所以:
In [18]: Foo = type('Foo', (), {'bar': True})
In [19]: class Foochild(Foo):
....: pass
可以写为:
In [20]: FooChild = type('FooChild', (Foo,), {})
In [21]: print FooChild
<class '\_\_main\_\_.FooChild'>
In [22]: print FooChild.bar
True
并且你可以在动态创建了类之后添加更多的方法,就像添加方法到正常创建的类的对象。
In [6]: def echo_bar_more(self):
...: print 'yet another method'
...:
In [7]: Foochild.echo_bar_more = echo_bar_more
In [8]: hasattr(Foochild, 'echo_bar_more')
Out[8]: True
你可以看到我们所向何方:在Python中,类是对象,你能够动态创建一个类。这就是你使用关键词class时候Python做的事情,其是通过使用元类来实现的。
什么是元类(终于进入主题)
元类是创建类的东东。你定义了类来创建对象,是这样吧?但是我们学习到了在Python中类也是对象。因此,元类就是用于创建这些对象。元类是类的类,你能够这样理解他们:
MyClass = MetaClass()
MyObject = MyClass()
你能够发现type能够让你这样做:
MyClass = type(‘MyClass’, (), {})
这是因为函数type实际上是一个元类。type是Python在背后用来创建所有类的元类。
现在你会怀疑为什么type会用小写的形式,而不是写为Type。我猜这是为了和创建字符串对象的str和创建整数对象的int保持一致。type只是用来创建类对象的类。
你可以通过检查__class__属性来验证这点。
Python中任何东西都是对象,包括整数 ,字符串,函数和类,所有都是对象,所有都能够被从一个类中创建。
In [18]: age = 35
In [19]: print age.__class__
<type 'int'>
In [20]: name = 'bob'
In [21]: print name.__class__
<type 'str'>
In [22]: def foo(): pass
In [23]: print foo.__class__
<type 'function'>
In [24]: class Bar(object): pass
In [25]: b = Bar()
In [26]: print b.__class__
<class '__main__.Bar'>
现在,任意的__class__的__class__是什么?
In [27]: print age.__class__.__class__
<type 'type'>
In [28]: print name.__class__.__class__
<type 'type'>
In [29]: print foo.__class__.__class__
<type 'type'>
In [30]: print b.__class__.__class__
<type 'type'>
所以,一个元类只是用来创建类对象的东西。你愿意的话可以称它为“类工厂”。type是Python使用的内建元类,当然,你能够创建你自己的元类。
__metaclass__属性
当你编写一个类的时候你能够添加一个__metaclass__属性:
class Foo(object):
__metaclass__ = something...
[…]
如果你这么做,Python将会使用指定的元类来创建类Foo,注意的是,这其中有大坑。
你先编写了 class Foo(object),但是类的对象Foo并没有在内存中被创建。Python将会在类定义中寻找__metaclass__,如果发现了__metaclass__,其将会使用指定的元类来创建对象类Foo,如果没有发现,其将会使用type来创建类。
多阅读如下几次:
当你编写
class Foo(Bar):
pass
Python做了如下的事情:
- 是否在Foo中有__metaclass__属性?如果有,在内存中通过使用__metaclass__中指定的和Foo这个名字创建类对象。
- 如果找不到__metaclass__,Python将会使用Bar(Foo的第一个父类)的metaclass(可能是默认的type)来创建类对象。
- 如果Python找不到__metaclass__,Python将会在模块层次寻找__metaclass__,并且尝试做相同的事情(仅仅对没有继承任何类的类,基本上都是old-style classes。基本上可以认为是没有继承object的类,super就不适用于old-style classes)
现在的大问题是,你能在__metaclass__中间加入什么。答案是一些能够创建类的东西。那么怎么创建类呢?type或者任何type的子类或者用到type的东西。
自定义子类
一个metaclass的主要目的是在类创建的时候自动改变类。
你通常为了API这样做,当你想要创建符合当前上下文的类的时候。想想一个很愚蠢的例子,当你决定你模型中的所有的类的属性都必须小写的时候,有一些方法可以做到,其中一种就是在模块层次设置__metaclass__。
这样,所有在模块的类在创建的时候将会使用这个metaclass,我们只需要告诉metaclass去转换所有的属性为小写。幸运的是,__metaclass__可能任意调用,并非需要是一个正式的类(我知道,有些名字里带有“类”的东西并不一定是类)
所以我们以一个简单的例子开始,通过使用一个函数:
# 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'
让我们使用一个真正的类来做元类,然后再做一次上述的事情:
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 sutff 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, future_class_attr)
你可能注意到了额外的参数upperattr_metaclass,这个参数没什么特殊的,__new__总是接受类作为第一个参数,就像你在普通的方法中的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来让它显得更加清晰,super定义了方法解析顺序,解决继承中的一些问题(是的,你能够使用从元类,从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大师 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背后对于代码进行重新创建并完成真正的工作。
结束语
首先,你需要知道类都是对象并且能够创建实例。实际上类自身也是元类的实例。
In [1]: class Foo(object): pass
In [2]: id(Foo)
Out[2]: 140489058020624
在Python中的所有东西都是一个对象,它们要么是类的实例,要么是元类的实例。除了type。type实际上是自己的元类,这是在纯Python中你没办法实现的事情,其是在实现层次中通过一点小手段实现的。
其次,元类很复杂,你可能并不想在很简单的类改造的时候使用元类,你能够通过两个不同的技术来改变类:
- monkey patching
- class decorators
在需要进行类改造的时候,99%情况下你最好使用上面这两个,然而在99%的情况下,你并不需要对类进行改变。