Python中元类的理解:
定义:
1,元类(metaclass)是创建类的类,可以理解为类是元类的实例,只有type及其派生类才能充当元类。正如我们要创建一个类,才能创建一个实例对象。要创建一个元类,才能创建一个类。
一句话就是:元类—>类—>实例2,元类是可以让你定义某些类是如何被创建出来的。从根本上说,就是赋予你如何创建类的控制权。元类也是一个类,是一个type类。
3, 元类一般用于创建类。在执行类定义时,python解释器必须要知道这个类的属性(_metaclass_)的元类。如果此属性没有定义,它会向上查找父类中的_metaclass_属性。如果都没有该类就通过普通的type创建类。
4,对于普通的类,它们的元类是types.ClassType。
从以下九个方面详细加以说明:
Num01–>类也是对象
在大多数的编程语言中,类就是一组用来描述如何生成对象的代码片段。在python中这一点仍然成立。代码如下:
#随便创建一个类
class ObjectCreator(object):
pass
my_object = ObjectCreator()
print(my_object)
# 结果如下:
# <__main__.ObjectCreator object at 0x000002331C60C358>
但是python中类还远不止如此。类同样也是一种对象。只要你使用关键字class,python解释器在执行的时候,就会创建一个对象。
上面的那段代码,将在内存中创建一个对象,名字就是ObjectCreator。这个对象(类对象)拥有创建对象(实例对象)的能力。但是,它的本质仍然是一个对象,既然是一个对象,就可以对它做如下操作:
1,可以将它(类对象)赋值给一个变量。
2,可以拷贝它(类对象)。
3,可以为它(类对象)增加属性。
4,可以把它(类对象)作为一个参数,传递给函数。
#随便创建一个类
class ObjectCreator(object):
pass
my_object = ObjectCreator()
print(my_object)
# 结果如下:打印一个实例对象, <__main__.ObjectCreator object at 0x000002331C60C358>
# 1,可以打印一个类,因为它其实也是一个对象
print(ObjectCreator)
# 结果为:<class '__main__.ObjectCreator'>
def demo(o):
print(o)
# 打印结果为:其实还是打印类,<class '__main__.ObjectCreator'>
# 2,你可以将类做为参数传给函数
demo(ObjectCreator)
# 3,你可以为类增加属性
ObjectCreator.attr2 = 'xiaoke'
# 判断类中是否有刚添加的属性
print(hasattr(ObjectCreator, 'attr2'))
# 结果为:True
print(ObjectCreator.attr2)
# 结果为:xiaoke
# 4,你可以将类赋值给一个变量
temp = ObjectCreator
print(temp())
# 结果为:其实就是打印一个实例对象,<__main__.ObjectCreator object at 0x0000018CE3647080>
Num02–>动态地创建类
因为类也是对象,你可以在运行时动态的创建类,就像其他任何对象一样。
下面的案例就是在函数中创建类,只要使用关键字class即可。
def choose_class(name):
if name == 'xiaoke':
class Foo(object):
pass
return Foo # 返回的是Foo类,不是类的实例
else:
class Bar(object):
pass
return Bar # 返回的是Bar类,不是类的实例
MyClass = choose_class('xiaoke')
# 函数返回的是类,不是类的实例
print(MyClass)
# 结果是:<class '__main__.choose_class.<locals>.Foo'>
# 你可以通过这个类创建类实例,也就是对象
print(MyClass())
# 结果是:<__main__.choose_class.<locals>.Foo object at 0x000001C3E7237128>
但这还不够动态,因为你仍然需要编写整个类的代码。由于类也是对象,所以它们必须通过什么来生成的才对。当你使用class关键字的时候,Python解释器自动创建这个对象。
内建函数type可以让你知道一个对象的类型是什么。就如下:
# 数值的类型
print(type(1))
# 结果是:<class 'int'>
# 字符串的类型
print(type("1"))
# 结果是:<class 'str'>
# 实例对象的类型
print(type(ObjectCreator()))
# 结果是:<class '__main__.ObjectCreator'>
# 类的类型
print(type(ObjectCreator))
# 结果是:<class 'type'>
仔细观察上面的运行结果,发现使用type对ObjectCreator查看类型时,答案为type。
Num03–>使用type创建简单的类
type还有一种完全不同的功能,就是动态的创建类。
type可以接收一个类的描述作为参数,然后返回一个类。
type的构成如下:type(类名,由父类名称组成的元组(针对集继承的情况,可以为空),包含属性的字典(名称和值,可以为空))
看下面的代码:
# 定义了一个ObjectCreate类
class ObjectCreate(object):
pass
# 创建了一个ObjectCreate类的实例对象
print(ObjectCreate())
# 结果是:<__main__.ObjectCreate object at 0x000002714EDAD358>
# 使用type动态的创建类
NewObjectCreate = type("NewObjectCreate", (), {})
# 创建了一个NewObjectCreate类的实例对象
print(NewObjectCreate())
# 结果是:<__main__.NewObjectCreate object at 0x000001B51DC8D358>
print(help(ObjectCreate))
# 结果是:
'''
Help on class ObjectCreate in module __main__:
class ObjectCreate(builtins.object)
| # 定义了一个ObjectCreate类
|
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
None
'''
print(help(NewObjectCreate))
# 结果是:
'''
Help on class NewObjectCreate in module __main__:
class NewObjectCreate(builtins.object)
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
None
'''
Num04–>使用type创建带有属性的类
type接收一个字典来为类定义属性,如下:
# 定义Foo类
Foo = type("Foo", (), {"name": "xiaoke"})
# 上面定义Foo类,也可以理解为如下:
class Foo(object):
name = 'xiaoke'
# 可以将类Foo当成一个普通的类使用
print(Foo)
# 结果是:打印类:<class '__main__.Foo'>
print(Foo.name)
# 结果是:xiaoke
f = Foo()
print(f)
# 结果是:打印一个实例对象:<__main__.Foo object at 0x00000203DBD5A0F0>
print(f.name)
# 结果是:xiaoke
print("======子类ChildFoo=========")
# 可以向这个类继承,所以,如下的代码:
class ChildFoo(Foo):
pass
# 使用type创建ChildFoo类,继承父类Foo
ChildFoo = type("ChildFoo", (Foo,), {})
print(ChildFoo)
# 结果:<class '__main__.ChildFoo'>
print(ChildFoo.name) # name属性是由子类ChildFoo继承父类Foo而来的
# 结果是:xiaoke
注意点:
1, type的第2个参数,元组中是父类的名字,而不是字符串。
2, 添加的属性是类属性,并不是实例属性。
Num05–>使用type创建带有方法的类
最终你会希望为你的类增加方法。只需要定义一个有着恰当签名的函数并将其作为属性赋值(放在type的字典中)就可以。
特别注意:使用type创建带有方法的类时,这些方法(静态方法,类方法,普通方法等)都是放在类的外面。或者可以理解为:把这些方法都添加新的类中,作为新类的方法而已。如果新类有父类,父类的方法子类是直接继承下来;如果子类继承父类的方法中,有名字相同的方法,子类就是重写父类的方法,对该方法加以修改。
# 先定义一个带有类属性的类
class A(object):
num = 100
# 在类的外部定义个普通的方法
def print_fun(self):
print("----我是普通方法----")
print("B类的属性值是:%d" % self.num)
# 在类的外部定义一个静态的方法,这仅在使用type创建类的时候,才把静态方法放在类外,其他都是在类中
@staticmethod
def print_static():
print("----我是静态方法----")
print("B类的属性值是:%d" % A.num)
# 在类的外部定义一个类方法,这仅在使用type创建类的时候,才把类方法放在类外,其他都是在类中
@classmethod
def print_class(cls):
print("----我是类方法----")
print("B类的属性值是:%d" % cls.num)
B = type("B", (A,), {"fun_B": print_fun, "static_B": print_static, "class_B": print_class})
b = B()
b.fun_B()
b.static_B()
b.class_B()
# 结果:
# ----我是普通方法----
# B类的属性值是:100
# ----我是静态方法----
# B类的属性值是:100
# ----我是类方法----
# B类的属性值是:100
Num06–>到底什么是元类
元类就是用来创建类的东西。你创建类基本就是为了创建实例对象。
Python中的类也是对象。元类就是用来创建这些类(对象)的。
总之:元类就是类的类。
# 使用元类创建出一个对象,这个对象称为“类”
MyClass = MetaClass()
# 使用“类”来创建出实例对象
MyObject = MyClass()
使用type也可以这样创建类:
MyClass = type('MyClass', (), {})
这是因为type实际上就是一个元类。type就是Python在背后用来创建所有类的元类。type之所以全部采用小写的原因,就是与str(字符串对象的类),int(整数对象的类)等类保持一致性的。type就是创建类对象的类,可以通过查看class这一属性来查看这一点。
Python中所有的东西都是对象。包括整数、字符串、函数和类等。它们全都是对象,而且它们都是从一个类创建而来,就是type类。
age = 35
print(age.__class__)
# 结果:<class 'int'>
name = 'bob'
print(name.__class__)
# 结果:<class 'str'>
def foo():
pass
print(foo.__class__)
# 结果:<class 'function'>
class Bar(object):
pass
b = Bar()
print(b.__class__)
# 结果:<class '__main__.Bar'>
对于任何一个class的class属性又是怎样呢?
age = 35
print(age.__class__.__class__)
# 结果:<class 'type'>
name = 'bob'
print(name.__class__.__class__)
# 结果:<class 'type'>
def foo():
pass
print(foo.__class__.__class__)
# 结果:<class 'type'>
class Bar(object):
pass
b = Bar()
print(b.__class__.__class__)
# 结果:<class 'type'>
因此,元类就是创建类这种对象的东西。type就是Python中内置元类,当然,你也可以自定义元类。
Num07–>_metaclass_属性
你可以在定义一个类的时候,为其添加_metaclass_属性。
class Foo(object):
__metaclass__=something...
...省略...
如果你这么做了,Python就会使用元类来创建类Foo。
首先,当你写下class Foo(object),但是类Foo还没有在内存中创建
其次,Python会在类的定义中寻找metaclass属性,如果找到了,Python就会用它来创建类Foo;如果没有找到,就用内建的type来创建这个类。
class ChildFoo(Foo):
pass
上面的代码,Python做了如下的操作:
1,ChildFoo中有_metaclass_这个属性吗?如果有,Python会通过_metaclass_创建一个名字为ChildFoo的类(对象)。
2,如果在ChildFoo中没有找到_metaclass_,它会继续在Foo(父类)中查找_metaclass_属性,并尝试做和前面同样的操作。
3,如果在任何父类中都找不到_metaclass_属性,它就会在模块层次中去寻找_metaclass_,并尝试做和前面同样的操作。
4,如果最终还是没有找到_metaclass_属性,Python就会用内置的type来创建这个类对象。特别注意:可以在_metaclass_中放置些什么代码呢?答案就是一个类的东西。那么什么可以用来创建一个类呢?那就是type,或者任何使用到type,或者子类化type的东西都行。
Num08–>自定义元类
元类的主要目的就是当创建类的时候能够自动改变类。通常,你会为API做这样的事,你希望可以创建符合当前上下文的类。
_metaclass_属性可以被任意的调用,可以是一个函数,可以是一个方法,可以是一个类。
使用一个函数,来创建元类
在python2中
def upper_attr(future_class_name, future_class_parents, future_class_attr):
# 遍历属性字典,把不是__开头的属性名字变为大写
newAttr = {}
for key, value in future_class_attr.items():
if not key.startswith("__"):
newAttr[key.upper()] = value
# 调用type来创建一个类
return type(future_class_name, future_class_parents, newAttr)
# 设置Foo类的元类为upper_attr
class Foo(object):
__metaclass__ = upper_attr
name = 'xiaoke'
print(hasattr(Foo, 'name'))
# 结果:False
print(hasattr(Foo, 'NAME'))
# 结果:True
f = Foo()
print(f.NAME)
# 结果:xiaoke
在Python3中:
def upper_attr(future_class_name, future_class_parents, future_class_attr):
# 遍历属性字典,把不是__开头的属性名字变为大写
newAttr = {}
for key, value in future_class_attr.items():
if not key.startswith("__"):
newAttr[key.upper()] = value
# 调用type来创建一个类
return type(future_class_name, future_class_parents, newAttr)
# 设置Foo类的元类为upper_attr
class Foo(object, metaclass=upper_attr):
name = 'xiaoke'
print(hasattr(Foo, 'name'))
# 结果:False
print(hasattr(Foo, 'NAME'))
# 结果:True
f = Foo()
print(f.NAME)
# 结果:xiaoke
使用一个真正的类,来创建元类
class UpperAttrMetaClass(type):
# __new__ 是在__init__之前被调用的特殊方法
# __new__是用来创建对象并返回之的方法
# 而__init__只是用来将传入的参数初始化给对象
# 你很少用到__new__,除非你希望能够控制对象的创建
# 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
# 如果你希望的话,你也可以在__init__中做些事情
# 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
def __new__(cls, future_class_name, future_class_parents, future_class_attr):
# 遍历属性字典,把不是__开头的属性名字变为大写
newAttr = {}
for name, value in future_class_attr.items():
if not name.startswith("__"):
newAttr[name.upper()] = value
# 方法1:通过'type'来做类对象的创建
# return type(future_class_name, future_class_parents, newAttr)
# 方法2:复用type.__new__方法
# 这就是基本的OOP编程,没什么魔法
# return type.__new__(cls, future_class_name, future_class_parents, newAttr)
# 方法3:在python3中使用super方法
#return super().__new__(cls, future_class_name, future_class_parents, newAttr)
# 方法4:在python2中使用super方法
return super(UpperAttrMetaClass, cls).__new__(cls, future_class_name, future_class_parents, newAttr)
# python2的用法
class Foo(object):
__metaclass__ = UpperAttrMetaClass
name = 'xiaoke'
# python3的用法
class Foo(object, metaclass=UpperAttrMetaClass):
name = 'xiaoke'
print(hasattr(Foo, 'name'))
# 输出: False
print(hasattr(Foo, 'NAME'))
# 输出:True
f = Foo()
print(f.NAME)
# 输出:xiaoke
Num09–>元类案例:简单ORM的实现
# 1,来定义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)
# 2,定义字符串类型的子类
class StringField(Field):
def __init__(self, name):
super(StringField, self).__init__(name, 'varchar(100)')
# 2.1,定义整数类型的子类
class IntegerField(Field):
def __init__(self, name):
super(IntegerField, self).__init__(name, 'bigint')
# 3,定义一个元类,用来修改创建的类
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
if name=='Model':
return type.__new__(cls, name, bases, attrs)
print('Found model: %s' % name)
# mappings = dict()
mappings = {}
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 # 保存属性和列的映射关系
attrs['__table__'] = name # 假设表名和类名一致
return type.__new__(cls, name, bases, attrs)
# 4,原始类
class Model(dict, metaclass=ModelMetaclass):
def __init__(self, **kw):
super(Model, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
def save(self):
fields = []
params = []
args = []
for k, v in self.__mappings__.items():
fields.append(v.name)
params.append('?')
args.append(getattr(self, k, None))
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
print('SQL: %s' % sql)
print('ARGS: %s' % str(args))
# 5,应用的类
class User(Model):
# 定义类的属性到列的映射:
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
# 创建一个实例:
u = User(id=666666, name='xiaoke', email='976249817@qq.com', password='521xiaoke')
# 保存到数据库:
u.save()
总结:
元类的作用:
1,拦截类的创建
2,修改类
3,返回修改之后的类
参考资料如下:
http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319106919344c4ef8b1e04c48778bb45796e0335839000
http://blog.jobbole.com/21351/