在理解什么是元类之前,有必要先理解下,什么是类。
什么是类?通俗的讲,类就是用来创建对象的代码片。在python中,类还有一个奇特的特性,就是类,本身也是一个对象。怎么理解?——在你定义一个类的时候,就会在内存中创建一个名字为类名的对象。
能够创建对象(实体)的对象(类),就称之为类。(说起来很别扭,但大致就是这么个意思)。
既然是一个对象,那就可以:
传递给函数的参数
我们知道,对象不可能凭空产生,它总是由某些”事物”生成的。既然类也是个对象,那也不例外。
当你在使用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能够将一个类的描述作为参数并返回一个类。
(一个函数有两种完全不同的用途的确很怪,但这只是为了后向兼容而已了)
这样使用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>
type可以使用字典来定义该类的属性,如:
>>> class Foo(object):
... bar = True
可以转换成:
>>> Foo = type('Foo', (), {'bar':True})
你可以继承这个类:
>>> class FooChild(Foo):
... pass
这种继承也能用type来实现:
>>> 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
是的,当你使用class关键字时,python做了所有该做的事。而这些事是通过元类(metaclass)来完成的。
现在应该就知道元类是什么以及干什么的了吧?
元类就是那个帮你创建类的"家伙"。
为了创建对象(实体),你定义了类,是不是?
而在python中,类也是对象,这样的对象就是通过元类来创建的。元类就是"类的类"。
上文中提到的type事实上就是一个元类,在python中,所有的类都是使用type创建的。
到此,我们也能够更加理解为什么说在python中,一切皆对象了吧?int,string,function以及class,所有的多少对象。他们都能够从一个类中创建。通过_class_可以验证:
>>> age = 35
>>> age._class_
'int'>
>>> name = 'bob'
>>> name._class_
'str'>
>>> def foo(): pass
>>> foo._class_
'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b._class_
<class '_main_.Bar'>
25这个整数对象是有类"int"创建的。那"int"这个对象是由谁创建的呢?继续查看_class_:
>>> age._class.class_
<type 'type'>
>>> name._class.class_
<type 'type'>
>>> foo._class.class_
<type 'type'>
>>> b._class.class_
<type 'type'>
是的,元类创建了这些"类"对象。你也可以称元类为创建类的工厂。
type是python内置的元类,那能不能自定义元类呢?
能!
在学习如何定制元类前,先来了解一个重要的属性。
当你创建一个类的时候,你能够给它增加metaclass属性:
class Foo(object):
metaclass = something…
[…]
当你写下class Foo(object)的时候,类对象Foo并不会在内存中创建:Python首先会看类定义中的metaclass。如果找到,就会使用它创建类对象Foo。否则,就使用type来创建。
继续说明:
当你写下如下代码:
class Foo(Bar):
pass
Python:
Foo里面有没有metaclass属性?
如果有,通过使用metaclass里面的元类在内存中创建名为Foo的类对象(在本文中类对象就是指类这个对象,请与类创建的实体对象区别开)。
如果没有,则会在模块(MODULE)层寻找metaclass,并执行相同的创建类对象操作(这种情况只针对老类型定义的类,也就是没有继承任何类的类)。
最终如果找不到任何metaclass,Foo就会使用Bar(第一个父类)的元类来创建类对象。
现在的问题是,metaclass能赋什么值?
回答:能够创建一个类的”事物”,也就是元类type或type的子类(或组合了type的类)
想象一个愚蠢的例子。当你想要你模块中的所有类的属性名都是大写字母。有很多种实现方法,其中一种是在模块层设置_metaclass_。这样,模块中的所有类都会使用这个原来来创建,我们只要告诉元类将所有属性转换成大写就行了。
_metaclass_的值不一定要一个正式的类,可以为任何可调用的事物。
比如,函数也可以(当然,记住上面说过的,要组合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
# 使用'type'来创建!
return type(future_class_name, future_class_parents, uppercase_attr)
_metaclass_ = upper_attr #这会影响模块中的类
class Foo(): # 如果这里继承了object,那全局_metaclass_就不会对这个Foo类起作用了哦
# 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,而没有调用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,类方法第一个参数是cls一样。
上面的定义使用的参数都太长了,按约定俗成的名称会是这样的:
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)
元类在做很多"黑魔法"事情方面的确非常有用,这也是让他看起来复杂的原因。元类本身还是很简单的:
1)拦截类的创建;2)修改这个类;3)返回修改后的类。
为什么要使用元类类(metaclasses classes)而不是函数呢?
考虑到_metaclass_可以接受任何可调用事物,那为什么要用一个看起来明显复杂的类呢?
原因主要有以下几个:
意图更明显。当读到UpperAttrMetaclass(type),你清楚知道要干什么
你能够使用OOP
能够更好的组织你的代码
能够利用new、init和call干点别的。
毕竟它们是元类啊,是不?(意思就是函数不能体现元类作为一个类,一个事物。)
python大师Tim Peters说过:元类是一种99%的人都不需要关心的深度魔法。当你在好奇你是否需要它,通常这就说明你并不需要(确切需要它的人不需要找任何原因解析为什么需要)。
元类的主要使用场景是创建一个API。典型的例子就是Django ORM。
首先,除了type,一切皆对象,不是类的实体,就是元类的实体。
其次,元类恨复杂。在简单的改变类时你可能并不想要使用。你能通过使用以下两种技术来改变类:
monkey patching
class decorators
需要改变类的99%的情况,你使用这两种更好。而99%的场景,你其实并不需要元类。
【参考文献】
http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python
这篇里面也介绍元类也介绍的非常不错,虽然它标题是关于抽象类的:
http://blog.thedigitalcatonline.com/blog/2016/04/03/abstract-base-classes-in-python/