元类
大约 7 分钟
@property动态属性
from datetime import date, datetime
class User:
def __init__(self, name, birthday):
self.name = name
self.birthday = birthday
# @property 在给属性赋值时可以通过函数加入自定义的逻辑
@property
def name(self):
# 前面增加下划线表示这个属性不想对外暴露,只能通过属性描述符进行访问
# 这仅仅是代码规范,仍然可以通过 user._age 的方式访问
return self._name + "·不良人"
# 可以通过赋值加入自动移的逻辑
@name.setter
def name(self, name):
self._name = name
# 也可以将自动计算的逻辑变成 User 的属性
@property
def age(self):
return datetime.now().year - self.birthday.year
# 给一个不存在的属性赋值
@age.setter
def age(self, age):
self._age = age
user = User('lixingyun', date(year=1990, month=1, day=1))
print(user.name) # lixingyun·不良人
print(user.age) # 34
user.name = 'wanglin' # wanglin·不良人
print(user.name)
# 直接赋值 _age
user._age = 22
print(user._age) # 22
__getattr__
和__getattribute__
from datetime import date
class User:
def __init__(self, name, birthday, info={}):
self.name = name
self.birthday = birthday
self.info = info
# 在查找不到属性时调用 __getattr__()
def __getattr__(self, item):
# 在访问属性时可以添加自己的逻辑
# print(f'not found {item}')
if item == 'address':
return self.info['address']
# 在查找属性时,首先会无条件地进入这个方法
def __getattribute__(self, item):
if item == 'age':
return date.today().year - self.birthday.year
return super().__getattribute__(item)
user = User('lixingyun', date(year=1990, month=1, day=1), info={'address': 'beijing'})
# 如果没有 __getattr__ ,访问一个不存在的属性时会报错
# print(user.age) # AttributeError: 'User' object has no attribute 'age'
# 如果有 __getattr__ ,访问一个不存在的属性会调用 __getattr__()
# print(user.age) # not found age
# None
# 在 __getattr__ 和 __getattribute__ 中,__getattribute__ 的优先级更高
print(user.age) # 34
print(user.name) # lixingyun
print(user.address) # beijing
__new__
和__init__
class User:
# __new__ 可以在创建对象实例时加入自定义逻辑,自定义类的生成过程,它传递的第一个参数是类
# __new__ 会在 __init__ 之前调用,而且调用之后并没有生成对象
# __new__ 是用来控制对象的生成过程,返回的对象实例,在对象生成之前执行
# 如果返回 None,那么就不会生成对象实例
def __new__(cls, *args, **kwargs):
print('__new__ *args begin')
for arg in args:
print(arg)
print('__new__ *args end')
print('__new__ **kwargs begin')
for kwarg in kwargs:
print(kwarg)
print('__new__ **kwargs end')
# 调用 __init__ 方法
return super().__new__(cls)
# __init__ 就时一个普通的构造函数,它传递的第一个参数是对象实例
# __init__ 是用来完善对象的属性的,是在对象生成之后在实例中执行的
# 如果 __new__ 方法不返回对象,就不会调用 __init__ 方法
def __init__(self, name):
print('__init__ begin')
self.name = name
print('__init__ end')
# 在 __new__ 中加入 return super().__new__(cls) 后才会输出 __init__ begin 和 __init__ end
user1 = User('lixingyun') # __new__ *args begin
# lixingyun
# __new__ *args end
# __new__ **kwargs begin
# __new__ **kwargs end
属性描述符
# 自定义属性描述符,只需要实现 __get__, __set__, __delete__ 方法即可
class AttrDesc:
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
print('__get__', instance, owner)
return instance.__dict__[self.name]
# 在设置值的时候,就可以实现自定义的各种逻辑
def __set__(self, instance, value):
print('__set__', instance, value)
# 判断是否是字符串
if not isinstance(value, str):
raise TypeError('name must be str')
instance.__dict__[self.name] = value
def __delete__(self, instance):
print('__delete__', instance)
del instance.__dict__[self.name]
class Entity:
name = AttrDesc('name')
entity = Entity()
# 当给属性描述符赋值时,会自动调用 __set__ 方法
entity.name = 'lixingyun' # __set__ <__main__.Entity object at 0x7fb68000fe80> lixingyun
# entity.name = 1 # TypeError: name must be str
# 当获取属性描述符的值时,会自动调用 __get__ 方法
print(entity.name) # __get__ <__main__.Entity object at 0x7ff1c000fe80> <class '__main__.Entity'>
# lixingyun
自定义元类
# 通过函数动态创建类
def create_class(name):
if name == 'Entity':
class Entity:
def __init__(self, name):
self.name = name
return Entity
elif name == 'Person':
class Person:
def __init__(self, name):
self.name = name
return Person
Entity = create_class('Entity')
print(Entity) # <class '__main__.create_class.<locals>.Entity'>
print(type(Entity)) # <class 'type'>
entity = Entity('lixingyun')
print(entity) # <__main__.create_class.<locals>.Entity object at 0x7febb81fee80>
print(type(entity)) # <class '__main__.create_class.<locals>.Entity'>
class BaseClass:
def show(self):
return 'hello from BaseClass'
def sayHello(self):
return 'hello, lixingyun'
# 也可以通过type实现动态创建类
# ()表示默认继承object
# User = type('User', (), {'name': 'lixingyun', 'sayHello': sayHello})
User = type('User', (BaseClass, ), {'name': 'lixingyun', 'sayHello': sayHello})
user = User()
print(user) # <__main__.User object at 0x7f82f0167f40>
print(type(user)) # <class '__main__.User'>
print(user.name) # lixingyun
print(user.sayHello) # <bound method sayHello of <__main__.User object at 0x7fe308010b20>>
print(user.sayHello()) # hello, lixingyun
print(user.show()) # hello from BaseClass
print()
# 因此,元类是用来创建类的类,type 就是元类
# 只要继承了 type 的类,就是一个元类
# python 在创建类的实例时,会首先寻找 metaclass 属性,如果有,就会通过 metaclass 去创建类,否则会使用 type 去创建类
class MetaClass(type):
# __new__ 是元类的构造方法,在实例化时会先进入并执行
def __new__(cls, *args, **kwargs):
print('MetaClass __new__')
return super().__new__(cls, *args, **kwargs)
class Person(metaclass=MetaClass):
def __new__(cls, *args, **kwargs):
print('Person __new__')
return super().__new__(cls, *args, **kwargs)
def __init__(self):
print('Person __init__')
def __str__(self):
return 'I am a Person'
person = Person() # MetaClass __new__
# Person __new__
# Person __init__
print(person) # I am a Person
自定义ORM
# 自定义orm
# 属性描述符公共父类
class Field:
pass
# 属性描述符
class StrField(Field):
def __init__(self, db_column, db_type, max_length):
self._value = None
# db_column
if db_column is not None:
if isinstance(db_column, str):
self.db_column = db_column
else:
raise TypeError('db_column must be str')
else:
ValueError('db_column is None')
if len(db_column) == 0:
raise ValueError('db_column is empty')
# 判断db_type
if db_type is not None:
if isinstance(db_type, str):
self.db_type = db_type
else:
raise TypeError('db_type must be str')
else:
ValueError('db_type is None')
if len(db_type) == 0:
raise ValueError('db_type is empty')
# 判断max_length
if max_length is not None:
if isinstance(max_length, int):
self.max_length = max_length
else:
raise TypeError('max_length must be int')
if self.max_length < 0:
raise ValueError('max_length must be greater than 0')
else:
raise ValueError('max_length must be setting')
def __get__(self, instance, owner):
return self._value
# return f" {self.db_column} {self.db_type}({self.max_length}) not null auto_increment primary key,"
def __set__(self, instance, value):
if not isinstance(value, str):
raise TypeError('%s must be str' % self.db_column)
self._value = value
class IntField(Field):
def __init__(self, db_column, db_type, max_length, min_value, max_value):
self._value = None
# db_column
if db_column is not None:
if isinstance(db_column, str):
self.db_column = db_column
else:
raise TypeError('db_column must be str')
else:
ValueError('db_column is None')
if len(db_column) == 0:
raise ValueError('db_column is empty')
# 判断db_type
if db_type is not None:
if isinstance(db_type, str):
self.db_type = db_type
else:
raise TypeError('db_type must be str')
else:
ValueError('db_type is None')
if len(db_type) == 0:
raise ValueError('db_type is empty')
# 判断max_length
if max_length is not None:
if isinstance(max_length, int):
self.max_length = max_length
else:
raise TypeError('max_length must be int')
if self.max_length < 0:
raise ValueError('max_length must be greater than 0')
else:
raise ValueError('max_length must be setting')
# 判断min_value
if min_value is not None:
if isinstance(min_value, int):
self.min_value = min_value
else:
raise TypeError('min_value must be int')
if self.min_value < 0:
raise ValueError('min_value must be greater than 0')
else:
raise ValueError('min_value must be setting')
# 判断max_value
if max_value is not None:
if isinstance(max_value, int):
self.max_value = max_value
else:
raise TypeError('max_value must be int')
if self.max_value < 0:
raise ValueError('max_value must be greater than 0')
else:
raise ValueError('max_value must be setting')
# 判断 min_value < max_value
if min_value is not None and max_value is not None:
if self.min_value > self.max_value:
raise ValueError('min_value must be less than max_value')
def __get__(self, instance, owner):
return self._value
# return f" {self.db_column} {self.db_type}({self.max_length}) not null default {self.min_value}"
def __set__(self, instance, value):
if not isinstance(value, int):
raise TypeError('%s must be int' % self.db_column)
self._value = value
# 定义元类
class Entity(type):
def __new__(cls, name, bases, attrs):
print('Entity __new__')
if name == 'BaseModel':
return super().__new__(cls, name, bases, attrs)
_mate = {}
# 获取表名
meta = attrs.get('Meta', None)
table_name = name.lower()
if meta is not None:
table = getattr(meta, 'table_name', None)
if table is not None:
table_name = table
else:
raise TypeError('Meta is None')
_mate['table_name'] = table_name
# 加入attrs
attrs["_mate"] = _mate
# 删除无用的属性
del attrs['Meta']
# 获取列名
columns = {}
for key, value in attrs.items():
if isinstance(value, Field):
columns[key] = value
# 加入attrs
attrs["columns"] = columns
return super().__new__(cls, name, bases, attrs)
class BaseModel(metaclass=Entity):
def __init__(self, *args, **kwargs):
print('BaseModel __init__')
for key, value in kwargs.items():
setattr(self, key, value)
super().__init__()
# 拼装orm字符串
def save(self):
print('BaseModel save')
# print(f"create table {self._mate['table_name']} (")
columns = []
values = []
for key, value in self.columns.items():
db_column = value.db_column
if db_column is None:
db_column = key.lower()
columns.append(db_column)
value = getattr(self, key)
if isinstance(value, str):
value = f"'{value}'"
values.append(str(value))
# print(") engine=MyISAM DEFAULT CHARSET=utf8mb4 COMMENT='用户表';")
sql = f"insert into {self._mate['table_name']} ({', '.join(columns)}) values ({', '.join(values)});"
print(sql)
class User(BaseModel):
# 表中的列
name = StrField(db_column='username', db_type='varchar', max_length=32)
age = IntField(db_column='age', db_type='int', max_length=11, min_value=19, max_value=100)
class Meta:
table_name = 't_user'
# 不继承元类可以手动拼装orm字符串
# user = User()
# user.name = 'lixingyun'
# user.age = 18
# meta = user.Meta()
# print(f"create table {meta.table_name} (") # t_user
# print(user.name) # lixingyun
# print(user.age) # 18
# print(") engine=MyISAM DEFAULT CHARSET=utf8mb4 COMMENT='用户表';")
# 使用元类创建orm
user = User(name="lixingyun", age=28)
user.save() # Entity __new__
# Entity __new__
# BaseModel __init__
# BaseModel save
# insert into t_user (username, age) values ('lixingyun', 28);
感谢支持
更多内容,请移步《超级个体》。