#!/usr/bin/env Python# -- coding: utf-8 --import weakrefimport numbersclassField:passclassIntField(Field):# 数据描述符def__init__(self,db_column,min_value=None,max_value=None): self.min_value = min_value self.max_value = max_value self.db_column = db_columnif min_value isnotNone:ifnotisinstance(min_value, numbers.Integral):raiseValueError("min_value must be int")elif min_value <0:raiseValueError("min_value must be positive int")if max_value isnotNone:ifnotisinstance(max_value, numbers.Integral):raiseValueError("max_value must be int")elif max_value <0:raiseValueError("max_value must be positive int")if min_value isnotNoneand max_value isnotNone:if min_value > max_value:raiseValueError("min_value must be smaller than max_value") self._value = weakref.WeakKeyDictionary()def__get__(self,instance,owner):if instance isNone:return selfreturn self._value.get(instance, 0)def__set__(self,instance,value):ifnotisinstance(value, numbers.Integral):raiseValueError("int value need")if value < self.min_value or value > self.max_value:raiseValueError("value must between min_value and max_value") self._value[instance]= valueclassCharField(Field):def__init__(self,db_column,max_length=None):# self._value = None self.db_column = db_columnif max_length isNone:raiseValueError("you must spcify max_lenth for charfiled") self.max_length = max_length self._value = weakref.WeakKeyDictionary()def__get__(self,instance,owner):if instance isNone:return selfreturn self._value.get(instance, '0')def__set__(self,instance,value):ifnotisinstance(value, str):raiseValueError("string value need")iflen(value)> self.max_length:raiseValueError("value len excess len of max_length") self._value[instance]= value
代码-元类
classModelMetaClass(type):def__new__(cls,name,bases,attrs,**kwargs):if name =="BaseModel":returnsuper().__new__(cls, name, bases, attrs, **kwargs) fields ={}for key, value in attrs.items():ifisinstance(value, Field): fields[key]= value attrs_meta = attrs.get("Meta", None) _meta ={} db_table = name.lower()if attrs_meta isnotNone: table =getattr(attrs_meta, "db_table", None)if table isnotNone: db_table = table _meta["db_table"]= db_table attrs["_meta"]= _meta attrs["fields"]= fieldsdel attrs["Meta"]returnsuper().__new__(cls, name, bases, attrs, **kwargs)classBaseModel(metaclass=ModelMetaClass):def__init__(self,*args,**kwargs):for key, value in kwargs.items():setattr(self, key, value)returnsuper(BaseModel, self).__init__()defsave(self): fields = [] values = []for key, value in self.fields.items(): db_column = value.db_columnif db_column isNone: 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))print(sql)
代码-测试
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__':
first_user = User(name="bobby", age=28)
first_user.name = "bobby"
first_user.age = 28
second_user = User(name="bobby", age=23)
print(first_user.name is second_user.name)
second_user.name = 'okay'
print(first_user.name is second_user.name)
second_user.name = 'sec_boddy'
print(first_user.name)
print(second_user.name)
print(first_user.name is second_user.name)
输出
TrueFalsebobbysec_boddyFalse
成绩管理
代码
#!/usr/bin/env Python# -- coding: utf-8 --import weakrefclassGrade:def__init__(self): self._values = weakref.WeakKeyDictionary()def__get__(self,instance,owner):if instance isNone:return selfreturn self._values.get(instance, 0)def__set__(self,instance,value):ifnot (0<= value <=100):raiseValueError('Grade must be between 0 and 100') self._values[instance]= valueclassExam:# https://lingxiankong.github.io/2014-03-28-python-descriptor.html# 为了让描述符能够正常工作,它们必须定义在类的层次上。如果你不这么做,那么Python无法自动为你调用__get__和__set__方法。# 确保实例的数据只属于实例本身 math_grade =Grade() writing_grade =Grade() science_grade =Grade()if__name__=='__main__': first_exam =Exam() first_exam.writing_grade =82 second_exam =Exam() second_exam.writing_grade =75 logger.info(f'first {first_exam.writing_grade}') logger.info(f'second {second_exam.writing_grade}')
输出
2020-06-2520:18:23 lazy_db INFO: first 822020-06-2520:18:23 lazy_db INFO: second75
成绩管理2.0
代码
classGrade:def__init__(self,name): self.name = name self.internal_name ='_'+ self.namedef__get__(self,instance,owner):if instance isNone:return selfreturngetattr(instance, self.internal_name, '')def__set__(self,instance,value):ifnot (0<= value <=100):raiseValueError('Grade must be between 0 and 100')setattr(instance, self.internal_name, value)classExam:# https://lingxiankong.github.io/2014-03-28-python-descriptor.html# 为了让描述符能够正常工作,它们必须定义在类的层次上。如果你不这么做,那么Python无法自动为你调用__get__和__set__方法。# 确保实例的数据只属于实例本身 math_grade =Grade('math_grade') writing_grade =Grade('writing_grade') science_grade =Grade('science_grade')if__name__=='__main__': first_exam =Exam() first_exam.writing_grade =82 second_exam =Exam() second_exam.writing_grade =75 logger.info(f'first {first_exam.writing_grade}') logger.info(f'second {second_exam.writing_grade}')
输出
2020-06-2520:18:23 lazy_db INFO: first 822020-06-2520:18:23 lazy_db INFO: second75
成绩管理2.1
代码
classGrade:def__init__(self): self.name =None self.internal_name =Nonedef__get__(self,instance,owner):if instance isNone:return selfreturngetattr(instance, self.internal_name, '')def__set__(self,instance,value):ifnot (0<= value <=100):raiseValueError('Grade must be between 0 and 100')setattr(instance, self.internal_name, value)classMeta(type):def__new__(cls,name,bases,class_dict):for key, value in class_dict.items():ifisinstance(value, Grade): value.name = key value.internal_name ='_'+ key cls =type.__new__(cls, name, bases, class_dict)return clsclassExam(object,metaclass=Meta):# https://lingxiankong.github.io/2014-03-28-python-descriptor.html# 为了让描述符能够正常工作,它们必须定义在类的层次上。如果你不这么做,那么Python无法自动为你调用__get__和__set__方法。# 确保实例的数据只属于实例本身 math_grade =Grade() writing_grade =Grade() science_grade =Grade()def__init__(self,writing_grade): self.writing_grade = writing_gradeif__name__=='__main__': first_exam =Exam(85) first_exam.writing_grade =82 second_exam =Exam(13) second_exam.writing_grade =75 logger.info(f'first {first_exam.writing_grade}') logger.info(f'second {second_exam.writing_grade}')
classCallbackProperty(object):"""A property that will alert observers when upon updates"""def__init__(self,default=None): self.data =WeakKeyDictionary() self.default = default self.callbacks =WeakKeyDictionary()def__get__(self,instance,owner):return self.data.get(instance, self.default)def__set__(self,instance,value): for callback in self.callbacks.get(instance, []):# alert callback function of new valuecallback(value) self.data[instance]= valuedefadd_callback(self,instance,callback):"""Add a new function to call everytime the descriptor updates"""#but how do we get here?!?!if instance notin self.callbacks: self.callbacks[instance]= [] self.callbacks[instance].append(callback)classBankAccount(object): balance =CallbackProperty(0)deflow_balance_warning(value):if value <100:print"You are poor"ba =BankAccount()# will not work -- try it#ba.balance.add_callback(ba, low_balance_warning)
classCallbackProperty(object):"""A property that will alert observers when upon updates"""def__init__(self,default=None): self.data =WeakKeyDictionary() self.default = default self.callbacks =WeakKeyDictionary()def__get__(self,instance,owner):if instance isNone:return self return self.data.get(instance, self.default)def__set__(self,instance,value):for callback in self.callbacks.get(instance, []):# alert callback function of new valuecallback(value) self.data[instance]= valuedefadd_callback(self,instance,callback):"""Add a new function to call everytime the descriptor within instance updates"""if instance notin self.callbacks: self.callbacks[instance]= [] self.callbacks[instance].append(callback)classBankAccount(object): balance =CallbackProperty(0)deflow_balance_warning(value):if value <100:print"You are now poor"ba =BankAccount()BankAccount.balance.add_callback(ba, low_balance_warning)ba.balance =5000print"Balance is %s"% ba.balanceba.balance =99print"Balance is %s"% ba.balanceBalance is5000You are now poorBalance is99
The implementation works through a precedence chain that gives data descriptors priority over instance variables, instance variables priority over non-data descriptors, and assigns lowest priority to __getattr__() if provided.
classA(object):def__init__(self,value): self.value = valuedef__getattr__(self,item):print"into __getattr__"return"can not find"a =A(10)print a.value# 10print a.name# into __getattr__# can not find
class A(object):
def __init__(self, value):
self.value = value
def __setattr__(self, name, value):
self.__dict__[name] = value
a = A(10)
print a.value
# 10
__delattr__
__delattr__是个删除属性的方法
class A(object):
def __init__(self, value):
self.value = value
def __delattr__(self, item):
object.__delattr__(self, item)
def __getattr__(self, item):
return "when can not find attribute into __getattr__"
a = A(10)
print a.value
# 10
del a.value
print a.value
# when can not find attribute into __getattr__
类元编程是指在运行时创建或定制类的技艺。在 Python 中,类是一等对象,因此任何时候都可以使用函数新建类,而无需使用 class 关键字。类装饰器也是函数,不过能够审查、修改,甚至把被装饰的类替换成其他类。最后,元类是类元编程最高级的工具:使用元类可以创建具有某种特质的全新类种,例如我们见过的抽象基类。
元类的定义
Python定义元类时,需要从 type 类中继承,然后重写 __new__ 方法,便可以实现意想不到的功能。
classBase(type):def__new__(cls,name,param,dicts):print(cls)print(name)print(param)print(dicts)returnsuper().__new__(cls, name, param, dicts)classMeta(metaclass=Base): name ='yang'defperson(self):passMeta()<class'__main__.Base'>Meta(){'__module__':'__main__','__qualname__':'Meta','name':'yang','person':<function Meta.person at 0x10c6492f0>}