使用元类

Python元类详解

使用 type() 查看一个类或者变量的类型

>>> class Hello(object):
...     def hello(self, name='world'):
...             print('Hello, %s.' % name)
...
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))    #查看类的类型
<class 'type'>
>>> print(type(h))        #查看变量的类型
<class '__main__.Hello'>
复制代码

class 的定义是运行时动态创建的,而创建class 的方法就是使用 type() 函数 type() 函数既可以返回一个对象的类型,又可以创建出新的类型。

通过type() 动态创建 Hello类:

>>> def fn(self, name='world'):        #定义函数
...     print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello = fn))   #创建 Hello 类
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>
复制代码

通过 type() 函数创建类,需要传入的3个参数: 1、class 的名称 2、继承父类集合,注意 Python 支持多重继承,别忘了 tuple 的单元素写法 3、class 的方法名称与函数绑定,上面我们是通过 fn 绑定到 hello 上

通过 metaclass 可以给我们自定义的 MyList 增加一个 add 方法:

>>> class ListMetaclass(type):    #metaclass 是类的模板,所以必须从 ‘type’ 类型派生
...     def __new__(cls, name, bases, attrs):
...             attrs['add'] = lambda self, value: self.append(value)
...             return type.__new__(cls, name, bases, attrs)
...

复制代码

使用 ListMetaclass 来定制类,传入关键字参数 metaclass:

>>> class MyList(list, metaclass=ListMetaclass):     
...     pass
...

>>> L = MyList()   #测试 MyList 是否可以调用 add() 方法
>>> L.add(1)
>>> L
[1]
>>> L2 = list()    # 测试普通的 list 是否有add() 方法
>>> L2.add(1)
Traceback (most recent call last):               #报错了,说明没有add 方法
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'add'
复制代码

定义 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)
...
复制代码

进一步定义各种类型的 Field, 比如 StringField, IntegerField 等等:

>>> 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')
...
复制代码

编写复杂的 ModelMetaclass :

>>> 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)
...
复制代码

编写基类 Model:

>>> 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')
...
Found model:User
Found mapping: id ==> <IntegerField: id>
Found mapping: name ==> <StringField: username>
Found mapping: email ==> <StringField: email>
Found mapping: password ==> <StringField: password>
>>> u = User(id = 12345, name='Michael', email='test@orm.org', password='my-pwd')
>>> u.save()
SQL: insert into User (id,username,email,password) values(?,?,?,?)
ARGS: [12345, 'Michael', 'test@orm.org', 'my-pwd']
复制代码

以上我们通过 metaclass实现了一个精简的 ORM 框架,是不是很简单呢。

总结:

metaclass 是 Python 中非常具有魔术性的对象,它可以改变类创建时的行为。不过使用起来务必小心。
复制代码

转载于:https://juejin.im/post/5aef2312f265da0b8336a630

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值