#!/usr/bin/env Python
# -- coding: utf-8 --
import weakref
import numbers
class Field:
pass
class IntField(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_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")
self._value = weakref.WeakKeyDictionary()
def __get__(self, instance, owner):
if instance is None:
return self
return self._value.get(instance, 0)
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[instance] = 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
self._value = weakref.WeakKeyDictionary()
def __get__(self, instance, owner):
if instance is None:
return self
return self._value.get(instance, '0')
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[instance] = 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(BaseModel, self).__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))
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)
输出
True
False
bobby
sec_boddy
False
成绩管理
代码
#!/usr/bin/env Python
# -- coding: utf-8 --
import weakref
class Grade:
def __init__(self):
self._values = weakref.WeakKeyDictionary()
def __get__(self, instance, owner):
if instance is None:
return self
return self._values.get(instance, 0)
def __set__(self, instance, value):
if not (0 <= value <= 100):
raise ValueError('Grade must be between 0 and 100')
self._values[instance] = value
class Exam:
# 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-25 20:18:23 lazy_db INFO: first 82
2020-06-25 20:18:23 lazy_db INFO: second 75
成绩管理2.0
代码
class Grade:
def __init__(self, name):
self.name = name
self.internal_name = '_' + self.name
def __get__(self, instance, owner):
if instance is None:
return self
return getattr(instance, self.internal_name, '')
def __set__(self, instance, value):
if not (0 <= value <= 100):
raise ValueError('Grade must be between 0 and 100')
setattr(instance, self.internal_name, value)
class Exam:
# 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-25 20:18:23 lazy_db INFO: first 82
2020-06-25 20:18:23 lazy_db INFO: second 75
成绩管理2.1
代码
class Grade:
def __init__(self):
self.name = None
self.internal_name = None
def __get__(self, instance, owner):
if instance is None:
return self
return getattr(instance, self.internal_name, '')
def __set__(self, instance, value):
if not (0 <= value <= 100):
raise ValueError('Grade must be between 0 and 100')
setattr(instance, self.internal_name, value)
class Meta(type):
def __new__(cls, name, bases, class_dict):
for key, value in class_dict.items():
if isinstance(value, Grade):
value.name = key
value.internal_name = '_' + key
cls = type.__new__(cls, name, bases, class_dict)
return cls
class Exam(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_grade
if __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}')
class CallbackProperty(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 value
callback(value)
self.data[instance] = value
def add_callback(self, instance, callback):
"""Add a new function to call everytime the descriptor updates"""
#but how do we get here?!?!
if instance not in self.callbacks:
self.callbacks[instance] = []
self.callbacks[instance].append(callback)
class BankAccount(object):
balance = CallbackProperty(0)
def low_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)
class CallbackProperty(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 is None:
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 value
callback(value)
self.data[instance] = value
def add_callback(self, instance, callback):
"""Add a new function to call everytime the descriptor within instance updates"""
if instance not in self.callbacks:
self.callbacks[instance] = []
self.callbacks[instance].append(callback)
class BankAccount(object):
balance = CallbackProperty(0)
def low_balance_warning(value):
if value < 100:
print "You are now poor"
ba = BankAccount()
BankAccount.balance.add_callback(ba, low_balance_warning)
ba.balance = 5000
print "Balance is %s" % ba.balance
ba.balance = 99
print "Balance is %s" % ba.balance
Balance is 5000
You are now poor
Balance is 99
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.
class A(object):
def __init__(self, value):
self.value = value
def __getattr__(self, item):
print "into __getattr__"
return "can not find"
a = A(10)
print a.value
# 10
print 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__ 方法,便可以实现意想不到的功能。