1、Python中的类
在理解元类之前,你需要掌握Python中的类。Python中借用了SmallTalk语言特性对类进行实现。
在大部分的语言中,类一般是一段生成对象的代码,这在Python中也一样。
>>> class ObjectCreator(object):
... pass
...
>>> my_object = ObjectCreator()
>>> print(my_object)
在Python中,类也是一种对象。当执行如下代码时,Python会申请一块内存保存名为ObjectCreator的对象。
>>> class ObjectCreator(object):
... pass
...
生成的对象(类)拥有创造对象(实例)的能力,因此称为类。
作为对象,因此它能够像对象一样操作:
可以给变量赋值
可以复制
可以增加属性
可以作为函数参数传递
>>> print(ObjectCreator) # you can print a class because it's an object
>>> def echo(o):
... print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
>>> 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())
2.动态生成类
类可以像对象一样动态生成。
首先使用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
>>> print(MyClass()) # you can create an object from this class
以上方式并不是那么动态,因为你需要自己定义整个类。而类本身作为一个对象是可以通过其他来生成的,Python中允许手动定义类----通过使用type关键字完成类的定义
>>> print(type(1))
>>> print(type("1"))
>>> print(type(ObjectCreator))
>>> print(type(ObjectCreator()))
以上是通过type关键字进行类型查看,但是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)
>>> class MyShinyClass(object):
... pass
%可以换成另一种方式:
>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
>>> print(MyShinyClass()) # create an instance with the class
在手动创建类时,使用了MyShinyClass作为类的名字,同时也用同样的名字对该类进行引用。这两个名字可以不一样,如a=type('MyShinyClass',....),但没必要搞复杂。
type接受一个字典来定义类中的属性
>>> class Foo(object):
... bar = True
%可以转换成:
>>> Foo = type('Foo', (), {'bar':True})
%可以作为正常的类来使用:
>>> print(Foo)
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
>>> print(f.bar)
True
这样的类也能继承:
>>> class FooChild(Foo):
... pass
%可以写为:
>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
>>> print(FooChild.bar) # bar is inherited from Foo
True
当你需要给你的类添加方法(属性)的时候,通过函数段定义,并将其传递给type输入属性参数即可。
>>> 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
这样你就可以为创建的类动态的添加你需要的属性。
3、那么什么是元类呢?
元类(Metaclass)是创建类的元素。之前已经了解了可以通过类来创建实例对象,那么元类可以用来创建类对象,是类的类。
MyClass = MetaClass()
my_object = MyClass()
结合上文通过type来创建MyClass,我们可以了解type实际上就是元类,Python中使用type作为所有类的元类。可以通过__class__属性来查看类别。在Python中一切都是对象,不管是整数、字符串、函数还是类。但追根溯源,type是python中所有对象的元类,以下代码解释了这个机制。
>>> age = 35
>>> age.__class__
>>> name = 'bob'
>>> name.__class__
>>> def foo(): pass
>>> foo.__class__
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
>>> age.__class__.__class__
>>> name.__class__.__class__
>>> foo.__class__.__class__
>>> b.__class__.__class__
type是內建元类(built-in),但是可以创建自己的元类。
__metaclass__属性
在Python2中,你可以在创建类时给类添加一个__metaclass__属性,Python会使用这个元类属性来创建类对象Foo
class Foo(object):
__metaclass__ = something...
当执行class Foo(object)时,Python首先在__metaclass__中寻找类的定义,如果有,就用来创建类对象Foo,如果没有找到,Python会调用type方法创建类对象Foo。
当执行以下代码时,Python的编译过程如下:
class Foo(Bar):
pass
判断Foo中是否有__metaclass__属性?如果有,分配内存并使用__metaclass__属性创建以Foo为名的类对象;如果没有找到,Python会在当前模块里寻找该属性;如果还没有找到,那就会用父类Bar的元类(可能是默认的type)来创建类对象。这里需要注意,Foo从Bar处继承,但不会继承Bar的__metaclass__属性。如果Bar的使用type()来作为__metaclass__机制创建子类的话,那么子类不会继承这种创建机制。
而在Python3.x中,元类语法与2.x中不同。
class Foo(object, metaclass=something):
...
__metaclass__属性换成了关键字参数,传递给类对象,但元类的其他行为大体是相同的。3.x中新增的是可以按照关键字参数将属性传递给元类,如下:
class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
...
4、自定义元类
元类的主要作用是,在创建类时自动改变类的一些属性。当进行类似于调用APIs操作时需要创建对应匹配的类。
举一个例子,如果你想把模块中的所有类的属性名都使用大写,那其中一个可以实现的方式就是使用元类定义,设置__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'
现在我们换成另一个方式,定义一个元类:
# 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__方法总是把它定义的类作为第一个参数接受,类似于普通类定义中的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)
回到Python3.x中对元类的使用:
class Foo(object, metaclass=Thing, kwarg1=value1):
...
%可以转换成如下:
class Thing(type):
def __new__(class, clsname, bases, dct, kwargs1=default):
...
元类的介绍差不多就这些。
元类在实际中最主要的应用是生成API,典型应用是Django的ORM