07 python元类编程
property动态属性
根据生日获取年龄案例
@property 把函数编程一个属性,获取值
@类属性函数名.setter 设置一个属性
使用property属性可以获取一个值加入自己的逻辑
from datetime import date, datetime class User: def __init__(self, name, birthday): self.name = name self.birthday = birthday self._age = 0 @property def age(self): return datetime.now().year - self.birthday.year @age.setter def age(self, value): self._age = value if __name__ == "__main__": user = User("bobby", date(year=1987, month=1, day=1)) user.age = 30 print (user._age) print(user.age)
输出结果如下
__getattr__、__getattribute__魔法函数
__getattr__ 在查找不到属性的时候,python就会调用这个魔法函数,可避免出现报错信息
from datetime import date class User: def __init__(self,info={}): self.info = info def __getattr__(self, item): return self.info[item] if __name__ == "__main__": user = User(info={"company_name":"imooc", "name":"bobby"}) print(user.name)
打印结果如下
__getattribute__ 对象只要调用属性,无论能否找到这个属性,都会先调用这个魔法函数,比__getattr__的优先级高
from datetime import date class User: def __init__(self,info={}): self.info = info # def __getattr__(self, item): # return self.info[item] def __getattribute__(self, item): return "bobby" if __name__ == "__main__": user = User(info={"company_name":"imooc", "name":"bobby"}) print(user.name) print(user.company_name)
打印结果如下
属性描述符和属性查找过程
在一个类中实现__get__,__set__,__delete__三个之中的任一个方法,那我们就成为这个类为属性描述符
案例:验证属性类型,如果类型正确,保存起来;否则,报自定义错误信息
使用属性描述符后赋值会把值给属性描述符对象,取值也从属性描述符中获取
import numbers class IntField: # 整形的属性描述符 # 数据描述符可按照自定义的逻辑来检查对象 """ instance 在这里指的是 user对象 """ def __get__(self, instance, owner): return self.value def __set__(self, instance, value): if not isinstance(value, numbers.Integral): raise ValueError("int value need") if value < 0: raise ValueError("positive value need") self.value = value def __delete__(self, instance): pass class User: age = IntField() # 这里的age是一个属性描述符的对象 if __name__ == "__main__": user = User() user.age = 30 # 调用上面的__set__函数 ,把值保存在 InField.value= value print(user.age) # 调用上面的__get__函数 ,从IntField中取值
输出结果如下 (如果不是整形会报错)
数据描述符
class IntField: #数据描述符 def __get__(self, instance, owner): return self.value def __set__(self, instance, value): if not isinstance(value, numbers.Integral): raise ValueError("int value need") if value < 0: raise ValueError("positive value need") self.value = value def __delete__(self, instance): pass
非数据描述符
class NonDataIntField: #非数据属性描述符 def __get__(self, instance, owner): return self.value
对象查找一个属性的顺序
user = User(), 那么user.age 顺序如下:
(1)如果“age”是出现在User或其基类的__dict__中, 且age是data descriptor, 那么调用其__get__方法, 否则
(2)如果“age”出现在user的__dict__中, 那么直接返回 obj.__dict__[‘age’], 否则
(3)如果“age”出现在User或其基类的__dict__中
(3.1)如果age是non-data descriptor,那么调用其__get__方法, 否则
(3.2)返回 __dict__[‘age’]
(4)如果User有__getattr__方法,调用__getattr__方法,否则
(5)抛出AttributeError
__new__和__init__的区别
__new__允许在生成类的对象之前添加逻辑,它传进来的参数cls表示类本身,可自定义类的生成过程,返回一个对象
__init__传进来的参数self表示类的对象本身,是用来完善对象的初始化
调用__new__函数生成对象之后,并且__new__方法中要返回对象,才会调用__init__函数
利用__new__生成一个单例
class User: _instance = False def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super().__new__(cls) return cls._instance def __init__(self, name): self.name = name if __name__ == "__main__": user1 = User(name="bobby1") user2 = User(name="bobby2") print(id(user1),id(user2))
打印结果如下
自定义元类
type也可以动态创建类,type('类名',(继承的类),{属性和方法})
def say(self): return "i am user" class BaseClass: def answer(self): return "i am baseclass" USER = type("User", (BaseClass,), {"name": "jack", "age": "14", "say": say}) my_obj = USER() print(type(my_obj)) print(my_obj.age) print(my_obj.say()) print(my_obj.answer())
输出结果如下
什么是元类?
元类是创建类的类,比如type,常见的用法是定义一个类来继承type,那么这个新定义的类就是元类python中类的实例化过程
首先找自定义的metaclass, 通过metaclass来创建类对象,如果没有metaclass, 则会使用type来创建类对象
class MetaClass(type): def __new__(cls, *args, **kwargs): return super().__new__(cls, *args, **kwargs) class User(metaclass=MetaClass): def __init__(self, name): self.name = name def __str__(self): return "user" if __name__ == "__main__": my_obj = User(name="jack") print(my_obj)
调试结果如下
通过元类实现orm
使用元类可以实现类型的检查一些值的设定
用元类模仿django的orm
# 需求 import numbers class Field: pass class IntField(Field): # 数据描述符 def __init__(self, db_column, min_value=None, max_value=None): self._value = None self.min_value = min_value self.max_value = max_value self.db_column = db_column if min_value is not None: if not isinstance(min_value, numbers.Integral): raise ValueError("min_value must be int") elif min_value < 0: raise ValueError("min_value must be positive int") if max_value is not None: if not isinstance(max_value, numbers.Integral): raise ValueError("max_value must be int") elif max_value < 0: raise ValueError("max_value must be positive int") if min_value is not None and max_value is not None: if min_value > max_value: raise ValueError("min_value must be smaller than max_value") def __get__(self, instance, owner): return self._value def __set__(self, instance, value): if not isinstance(value, numbers.Integral): raise ValueError("int value need") if value < self.min_value or value > self.max_value: raise ValueError("value must between min_value and max_value") self._value = value class CharField(Field): def __init__(self, db_column, max_length=None): self._value = None self.db_column = db_column if max_length is None: raise ValueError("you must spcify max_lenth for charfiled") self.max_length = max_length def __get__(self, instance, owner): return self._value def __set__(self, instance, value): if not isinstance(value, str): raise ValueError("string value need") if len(value) > self.max_length: raise ValueError("value len excess len of max_length") self._value = value class ModelMetaClass(type): def __new__(cls, name, bases, attrs, **kwargs): if name == "BaseModel": return super().__new__(cls, name, bases, attrs, **kwargs) 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 table is not None: db_table = table _meta["db_table"] = db_table attrs["_meta"] = _meta attrs["fields"] = fields del attrs["Meta"] return super().__new__(cls, name, bases, attrs, **kwargs) class BaseModel(metaclass=ModelMetaClass): def __init__(self, *args, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) return super().__init__() def save(self): fields = [] values = [] for key, value in self.fields.items(): db_column = value.db_column if db_column is None: db_column = key.lower() fields.append(db_column) value = getattr(self, key) values.append(str(value)) sql = "insert {db_table}({fields}) value({values})".format(db_table=self._meta["db_table"], fields=",".join(fields), values=",".join(values)) pass class User(BaseModel): name = CharField(db_column="name", max_length=10) age = IntField(db_column="age", min_value=1, max_value=100) class Meta: db_table = "user" if __name__ == "__main__": user = User(name="bobby", age=28) # user.name = "bobby" # user.age = 28 user.save()
调试结果如下