# 第八章:元类编程
# 8.1 property 动态属性
from datetime import date
class User:
def __init__(self, name, birthday):
self.name = name
self.birthday = birthday
self._age = 0 # _ 一种编程规范
@property
def age(self):
return date.today().year - self.birthday.year
@age.setter
def age(self, value):
self._age = value
def get_age(self):
return self._age
if __name__ == '__main__':
user = User('linda', date(1987, 11, 14))
print(user.age) # @property 用变量的方式去封装逻辑
user.age = 100 # @age.setter 接收参数
print(user.get_age()) # self._age 实例内部有存储的变量 _age
- 对外展示 user.age; 内部存储 self._age
- 动态属性 property 内部有更多的逻辑操作空间
- user.age = 100 仔细体会内部处理过程
# 8.2 getattr、__getattribute__魔法函数
# getattr
class User:
def __init__(self, name):
self.name = name
def __getattr__(self, item):
return 'Not found attribute %s' % item
if __name__ == '__main__':
user = User('linda')
print(user.age) # Not found attribute age
- getattr, 在查找不到属性的时候调用
- 类似 else 机制
class User:
def __init__(self, info=None):
if not info:
info = {}
self.info = info
def __getattr__(self, item):
return self.info[item]
if __name__ == '__main__':
user = User({'name': 'linda', 'age': 18})
print(user.name)
print(user.age)
- 神奇的代理操作
# getattribute
class User:
def __init__(self, name):
self.name = name
def __getattribute__(self, item):
return 'get_attribute'
if __name__ == '__main__':
user = User('linda')
print(user.name) # get_attribute
print(user.test) # get_attribute
print(user.other) # get_attribute
- 只要调用属性,就会触发 getattribute
- 把持了整个属性调用入口,尽量不要重写这个方法
- 写框架时会涉及到
# 8.3 属性描述符和属性查找过程
property 实现在数据获取和设置时增加额外逻辑处理,并对外提供简单接口
在批量属性操作,如验证,则需要每个属性都要写一遍,代码重复
- 数据属性描述符:实现 get 和 set 方法
- 非数据属性描述符: 实现 get 方法
import numbers
class IntField:
def __init__(self):
self._data = None
def __get__(self, instance, owner):
print(instance) # <__main__.User object at 0x000002B88B270288>
print(owner) # <class '__main__.User'>
print(type(instance) is owner) # True
print(instance.__class__ is owner) # True
return self._data
def __set__(self, instance, value):
if not isinstance(value, numbers.Integral):
raise ValueError('Need int value')
# 重点来了,如何保存 value 呢,instance or self
# 如果 instance.attribute 又会触发 __set__ 描述符
self._data = value
def __delete__(self, instance):
pass
class User:
age = IntField()
num = IntField()
if __name__ == '__main__':
user = User()
user.age = 18
print(user.__dict__) # {} "age" 并没有进入到 __dict__
print(user.age)
转变原先简单的属性获取顺序
user 某个类实例,user.age 等价于 getattr(user, 'age')
首先调用 __getattribute__
如果定义了 __getattr__ 方法,调用 __getattribute__ 抛出异常 AttributeError 触发__getattr__
而对于描述符(__get__)的调用,则是发生在 __getattribute__内部
user = User(), 调用 user.age 顺序如下:
(1) 如果 'age' 是出现在 User 或基类的 __dict__ 中,且 age 是data descriptor,那么调用其 __get__(instance, owner) 方法,否则
(2) 如果 'age' 出现在 user 的 __dict__ 中,那么直接返回 user.__dict__['age'],否则
(3) 如果 'age' 出现在 User 或基类的 __dict__ 中
(3.1) 如果 age 是 non-data descriptor, 那么调用其 __get__ 方法,否则
(3.2) 返回 User.__dict__['age']
(4) 如果 User 有 __getattr__ 方法,调用 __getattr__ 方法,否则
(5) 抛出异常 AttributeError
- 属性描述符优先级最高
class NonDataIntFiled:
def __get__(self, instance, owner):
print(instance)
print(owner)
return 100
class User:
age = NonDataIntFiled()
if __name__ == '__main__':
user = User()
# user.__dict__['age'] = 18
# user.age = 18
# print(user.__dict__)
print(user.age)
# 8.4 new 和 init 的区别
- 自定义类中 new: 用来控制对象的生成过程,返回 self 对象,如果没有返回值,则不会调用 init
- 自定义类中 init: 用来完善对象,如初始化
- new 在 init 之前调用
class User(object):
# 新式类才有,生成对象 user 之前加逻辑
def __new__(cls, *args, **kwargs):
# args = ('linda', )
# kwargs = {'age': 20}
# 与自定义 metaclass 中的 __new__ 有区别
print('from __new__')
self = super().__new__(cls)
return self
def __init__(self, name, age=18):
self.name = name
self.age = age
print('from __init__')
if __name__ == '__main__':
user = User('linda', age=20)
PS: 统一描述
- 元类 -> 类对象
- 类 -> 实例
# 8.5 自定义元类
- class 关键字 可以字面创建类
def create_class(name):
if name == 'user':
class User:
def __str__(self):
return 'User'
return User
elif name == 'company':
class Company:
def __str__(self):
return 'Company'
return Company
MyClass = create_class('user')
obj = MyClass()
print(obj)
print(type(obj)) # <class '__main__.create_class.<locals>.User'>
- type 可以动态创建类,动态添加属性和方法
def func(self):
return 'I am from func.'
class Base:
def answer(self):
return 'I am from Base.answer.'
# type 动态创建类
User = type('User', (Base, ), {'name': 'user', 'func': func})
user = User()
print(user.name)
print(user.func())
print(user.answer())
print(type(user))
元类创建类的类 metaclass(type) -> class -> instance
class MetaClass(type):
# 用来控制 User 的创建过程 与 User 中的 __new__ 有区别
def __new__(cls, name, bases, attrs, **kw):
return super().__new__(cls, name, bases, attrs, **kw)
class User(object, metaclass=MetaClass):
def __init__(self, name):
self.name = name
def bar(self):
print('from bar.')
python 在实例化的过程 user = User()
(1) 首先寻找 metaclass,来创建 User,否则 (2) 再次寻找基类 BaseUser 的 metaclass,来创建 User,否则 (3) 接着寻找模块 metaclass,来创建 User,否则 (4) 最后默认 type 为 metaclass 来创建 User
# 8.6 通过元素实现ORM
首先明确需求
# 简单定义
class User:
name = CharFiled(db_column="", max_length=32)
age = IntFiled(db_column="", min_value=0, max_value=100)
class Meta:
db_table = 'user'
# ORM
user = User()
user.name = 'linda'
user.age = 18
user.save()
迷你版 ORM
from collections import OrderedDict
class Field:
pass
class IntField(Field):
def __init__(self, db_column, min_value=0, max_value=100):
self.db_column = db_column
self.min_value = min_value
self.max_value = max_value
self._value = None
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value, int):
raise TypeError('need int value')
if value < self.min_value or value > self.max_value:
raise ValueError('need [%s, %s] value' % (self.min_value, self.max_value))
self._value = value
class CharField(Field):
def __init__(self, db_column, max_length=32):
self.db_column = db_column
self.max_length = max_length
self._value = None
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value, str):
raise TypeError('need str value')
if len(value) > self.max_length:
raise ValueError('len need lower than %s' % self.max_length)
self._value = value
# 元类注入一系列属性
class MetaClass(type):
def __new__(cls, name, bases, attrs, **kw):
# BaseModel 也会调用 Metaclass,但没有定义 name,age 等属性,可特殊判断
if name == 'BaseModel':
return super().__new__(cls, name, bases, attrs, **kw)
fields = {}
for key, value in attrs.items():
if isinstance(value, Field):
fields[key] = value
attrs_meta = attrs.get('Meta', None)
_meta = {}
db_table = name.lower()
if attrs_meta is not None:
table = getattr(attrs_meta, 'db_table', None)
if not table:
db_table = table
_meta['db_table'] = db_table
attrs['_meta'] = _meta
attrs['fields'] = fields
if attrs.get('Meta'):
del attrs['Meta']
return super().__new__(cls, name, bases, attrs, **kw)
class BaseModel(metaclass=MetaClass):
def __init__(self, **kw):
for key, value in kw.items():
setattr(self, key, value)
super().__init__()
def save(self):
fields = OrderedDict(self.fields)
fields_str = ", ".join([value.db_column for value in fields.values()])
values_str = ', '.join([str(getattr(self, field)) if not isinstance(value, CharField)
else "'%s'" % str(getattr(self, field))
for field, value in fields.items()])
sql = "insert into %s (%s) values (%s)" % (self._meta['db_table'], fields_str, values_str)
print(sql)
# insert into user (name1, age) values ('linda', 20)
# 自定义类时写少量属性,元类帮助我们注入很多通用属性
class User(BaseModel):
name = CharField('name1', max_length=16)
age = IntField('age', min_value=0, max_value=100)
class Meta:
db_table = 'user'
if __name__ == '__main__':
user = User(name='linda')
user.age = 20
user.save()
ORM 设计思想
- 数据属性描述符(set, get) 实现验证操作
- 自定义元类(MetaClass(type)) 实现参数注入
- 自定义 ORM 类(BaseModel) 获取元类注入的参数 进行额外操作
- 自定义元类 注入 objects
- 需特别注意调用层级顺序,new 在 init 之前,所以 init 中可以使用元类注册测参数