关于python元类的最全资料整理
python的基础知识
- python 中一切都是对象。(字符串 整数 元组 字典等是对象,函数、类也是对象)
- 类是对象,那么是谁的对象?即类对象(类)是谁的对象?
- type函数的作用:1.调用时给参数(对象),打印出对象的类;2.手动生成类,跟class关键字作用类似
- python中的两个链:1.类的继承链;2.类(也是对象)的实例化链
类定义的两种方式(等价)
- 自动定义(class 关键字)
>>> class Animal(object):
'''Animal类,继承顶级类object'''
def __init__(self, name):
self.name = name
def eat(self):
pass
def go_to_vet(self):
pass
>>> class Cat(Animal):
'''Cat类,继承Animal类'''
def meow(self):
pass
def purr(self):
pass
- 手动定义(type函数)
type函数的使用:
- 第一个参数是一个字符串’Animal’,发送该字符串将其作为类名称,但也将type调用的结果赋给变量Animal。class关键字可以为你完成上面的操作。由于这里直接调用type,因此必须手动将结果赋给变量,就像在为其他类的新实例所做的一样。
- 第二个参数是只包含一个成员的元组:(object,)。这表示Animal类继承自object,与之前的类一样。需要在元组尾部加逗号,让Python解释器将其作为元组。括号在Python中有其他用处,因此对于只包含一个元素的元组需要在结尾处加逗号。
- 第三个参数是一个字典,定义了类的属性,等价于class代码块内缩进的代码部分。
>>> def init(self, name):
self.name = name
>>> def eat(self):
pass
>>> def go_to_vet(self):
pass
>>> Animal = type('Animal', (object,), {
'__doc__': 'A class representing an arbitrary animal.',
'__init__': init,
'eat': eat,
'go_to_vet': go_to_vet,
})
>>> def meow(self):
pass
>>> def purr(self):
pass
>>> Cat = type('Cat', (Animal,), {
'meow': meow,
'purr': purr,
})
元类
类是对象,叫类对象。类对象的类叫元类。
元类的定义
- class定义(推荐方法)
>>> class Meta(type):
'''A custom metaclass that adds actual functionality.'''
def __new__(cls, name, bases, attrs):
return super(Meta, cls).__new__(cls, name, bases, attrs)
- type定义(或者用type的子类定义)
>>> C = Meta('C', (object,), {})
元类的继承原则
一个类只能有一个元类。冲突时,有以下规则:
- 如果一个类的多个子类有不同的元类,Python的解释器会通过检查元类的起源来解决该冲突,如果其元类关系为直接继承,则最终使用子类。
>>> class Z(C, N):
pass
>>> type(Z)
<class '__main__.Meta'>
- 如果两个元类中的一个并不是另一个的直接子类,Python解释器并不知道该如何解决,因此会拒绝尝试执行(报错)
>>> class OtherMeta(type):
def __new__(cls, name, bases, attrs):
return super(OtherMeta, cls).__new__(cls, name, bases, attrs)
>>> OtherC = OtherMeta('OtherC', (object,), {})
>>> class Invalid(C, OtherC):
pass
Traceback (most recent call last):
File "<pyshell#36>", line 1, in <module>
class Invalid(C, OtherC):
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
元类的使用语法
- Python3
>>> class C(metaclass = Meta):
pass
- Python2
>>> class C(object,metaclass = Meta):
pass
- 兼容Python2和Python3
import six
class C(six.with_metaclass(Meta)):
pass
import six
@six.add_metaclass(Meta)
class C(object):
pass
元类的用途
元类的主要用途是手动创建类,即在类产生前做一些工作。最常见的应用场景是ORM模型。ORM中,表是类,字段是属性,记录是对象。那么,不同表的字段不同,即不同类的属性是不同的;但是每个表都有CURD(增删改查)操作。思路来了,那就是在类产生的过程中做手脚,不同类产生不同的属性,所以手动创建类就派上用场了。代码如下:
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)
class StringField(Field):
'''字符串字段类'''
def __init__(self, name):
super(StringField, self).__init__(name, 'varchar(100)')
class IntegerField(Field):
'''整型字段类'''
def __init__(self, name):
super(IntegerField, self).__init__(name, 'bigint')
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()
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)
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))
class User(Model):
'''用户表'''
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
######## 模型的使用##########
#创建一个实例:
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
# 保存到数据库:
u.save()