一、property
先看下面例子:
依赖于birthday设置User对象的属性
class User:
def __init__(self,name,birthday):
self.name = name
self.birthday = birthday
#age依赖于当前时间,需要做一番计算
self.age = datetime.today().year - self.birthday.year
user = User("ming",date(year=1992,month=8,day=12))
print(user.age)
但是这样好吗?如果是更加复杂的计算就必须以函数的方式返回。
from datetime import date,datetime
class User:
def __init__(self,name,birthday):
self.name = name
self.birthday = birthday
def get_age(self):
return datetime.today().year - self.birthday.year
user = User("ming",date(year=1992,month=8,day=12))
print(user.get_age())
虽然可以实现功能,但是明明是属性值却还要调用方法?有没有解决方案了?
通过property关键字就可以实现。
from datetime import date,datetime
class User:
def __init__(self,name,birthday):
self.name = name
self.birthday = birthday
@property
def age(self):
return datetime.today().year - self.birthday.year
user = User("ming",date(year=1992,month=8,day=12))
print(user.age)
@property可以把一个函数当作一个属性来供用户操作
既然是属性操作,还可以进行set和del操作。
from datetime import date,datetime
class User:
def __init__(self,name,birthday):
self.name = name
self.birthday = birthday
self._age = None
@property
def age(self):
return datetime.today().year - self.birthday.year
@age.setter
def age(self,value):
self._age = value
@age.deleter
def age(self):
del self._age
user = User("ming",date(year=1992,month=8,day=12))
print(user.age) #27
print(user._age) #None
#需要注意的是属性名不能和@property的方法名相同
user._age = 28
print(user.age) #27
print(user._age) #28
二、__getattr__和__getattribute__
__getattr__:当找不到相应的属性的时候所作的逻辑操作。
如果没有定义__getattr__方法,对于找不到的属性会直接报错。
from datetime import date,datetime
class User:
def __init__(self,name,birthday):
self.name = name
self.birthday = birthday
user = User("ming",date(year=1992,month=8,day=12))
print(user.age) #AttributeError: 'User' object has no attribute 'age'
如果设置了相应的方法找不到相应的属性会执行相应的逻辑。
from datetime import date,datetime
class User:
def __init__(self,name,birthday):
self.name = name
self.birthday = birthday
def __getattr__(self, item):
return "no find attr"
user = User("ming",date(year=1992,month=8,day=12))
print(user.age) #no find attr
当然不单单只是返回报错。
from datetime import date,datetime
class User:
def __init__(self,name,birthday,info):
self.name = name
self.birthday = birthday
self.info = info
def __getattr__(self, item):
return self.info.get(item)
info = {"company_name":"正名",
"position":"Director",
"family_information":{"father":"farmer","monther":'farmer'},
"Working company":["百度","阿里","苹果"]}
user = User("ming",date(year=1992,month=8,day=12),info)
print(user.position) #Director
__getattribute__:会比__getattr__更加霸道,所有的属性访问都会走下面的逻辑,不论是否存在。
from datetime import date,datetime
class User:
def __init__(self,name,birthday):
self.name = name
self.birthday = birthday
def __getattr__(self, item):
return self.info.get(item)
def __getattribute__(self, item):
return "test"
user = User("ming",date(year=1992,month=8,day=12))
print(user.position) #test
#不存在的也可以
__getattribute__在某些时候想要控制属性访问还是特别有用的。
三、属性描述符和属性的查找范围
user.age看似简单,但是内部实现的原理并不简单,通过下面内容应该可以清楚user.age的整个生命周期。
1.数据属性描述符
我们在自定义一个属性的时候该如何对属性本身做一些限制了?
我们要知道,类里面的所有方法都是针对于对象本身的,而不是针对这个属性的。
因此,我们需要将属性也变成一个自定义的对象,这样我们就能对属性进行操作
在ORM中,我们使用的是如下方式创建以及定义表:
class BusinessUnitType(models.Model):
"""
业务单元分类
"""
name = models.CharField(max_length=64)
下面我们可以模拟上述操作。
class CharField:
def __init__(self,max_length=32):
self.max_length = max_length
class User:
name= CharField(max_length=6)
user = User()
user.name = "str"
print(user.name)
但是现在还没有添加限制,比如必须是字符串,比如最大长度为6.
class CharField:
def __init__(self,max_length):
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("name必须是字符串")
if len(value) > self.max_length:
raise ValueError("超过规定长度")
if not isinstance(self.max_length,int):
raise ValueError("max_length 必须是整数")
self.value = value
def __delete__(self, instance):
del self.value
class User:
name= CharField(max_length=6)
打印测试:
user = User() user.name = 1 #ValueError: name必须是字符串 user.name = "uuuuuuuu" #ValueError: 超过规定长度 user.name = "yannc" #ValueError: max_length 必须是整数 print(user.name)
现在我们回过头来看看instance和value是什么

instance是一个user对象,竟然将外层内当参数传进来了,现在还没用过。
value就是name对象,或者说是值。当我们不知道怎么操作这些方法的时候,可以使用debug打印下到底做了什么。
现在回过头来想,我们就必须这样吗?
我们写一个使用常用的方式:
class NewPerson:
def __init__(self,name=None):
self.name = name
def __set__(self, instance, value):
print("123")
newperson = NewPerson()
newperson.name = "kebi" #这个操作根本就不会经过__set__魔法方法,__set__属于对象操作。
我们可以定义一个set_name方法,对name的赋值进行限制,估且不说name.set_name("kebi")这种形式好不好,
但是每一个属性的设置方法我都要进行定义,你觉得麻烦不?就不能newperson.name=""这种形式吗?
对于属性的操作难道我们就毫无办法吗?
其实可以使用@property,这个可以实现。
class Person:
@property
def name(self):
return self._name
@name.setter
def name(self,value):
self._name = value
person = Person()
person.name = "kibi"
print(person.name)
虽然可以实现,但是如果一个类的属性非常多,那么这个类就会显得非常臃肿。
对于属性的操作我们在类里面随便定义一个方法都可以操作,但是你把属性的值改变了之后,还是当前属性吗?
现在再来看看:
class CharField:
def __init__(self,max_length):
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("name必须是字符串")
if len(value) > self.max_length:
raise ValueError("超过规定长度")
if not isinstance(self.max_length,int):
raise ValueError("max_length 必须是整数")
self.value = value
def __delete__(self, instance):
del self.value
class User:
name= CharField(max_length=6)
name对于User来说是一个属性,但是同时也是一个对象,给一个对象赋值name="kebi",name还是CharField对象吗?
这就要说到属性操作符,CharField既是一个类,也是一个属性描述符,这个一个有点特别的类。
为什么这样说了?name可以说是CharField的对象,但是又str的对象,CharField更像是一个辅助类。
什么是属性描述符?只要是__get__、__set__、__delete__三个方法中的任意一个就是一个属性描述符。、
2.非数据属性描述符
当你只使用__get__方法来构造描述符的时候,你所构造的描述符就是一个非数据描述符。
class CharField:
def __init__(self,max_length):
self.max_length = max_length
def __get__(self, instance, owner):
return self.value
class User:
name = CharField()
3.属性查找过程
class CharField:
pass
class User:
name = CharField()
对于数据属性描述符、非数据属性描述符、类属性和对象属性的优先级说明:
如果user = User(),那么user.age的调用顺序如下:
(1)如果age在User类或者基类中,并且是数据属性描述符,那么调用其__get__方法。
关于属性描述符的定义:
class CharField:
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass
class User:
name= CharField()
user = User()
user.name = “kebi”
(2)如果age在user的__dict__中,那么以return self.age的方式调用。
出现在user的__dict__中,可能有三种情况:
class User:
def __init__(self,name):
#方法1
self.name = name
user = User('ming')
#方法2
user.age = 28
# 方法3:回溯机制
user.__dict__['addr'] = "罗田"
(3)如果age出现在User类或者基类__dict__中,并且是非数据属性描述符,那么调用其__get__方法。
class CharField:
def __get__(self, instance, owner):
return self.value
(4)如果age出现在User类或者基类__dict__中,不是描述符,以return self.age调用。
(5)如果User有__getattr__方法,调用__getattr__
(6)抛出AttributeError异常
class User:
pass
user = User()
print(user.name) #AttributeError: 'User' object has no attribute 'name'
优先级:
数据属性描述符 > 对象属性 > 非数据属性描述符 > 常规类属性 > __getattr__
下面是一个示例应该可以更好的阐述。
from datetime import date,datetime
class Name1():
"""数据属性描述符"""
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass
class Name3():
"""非数据属性描述符"""
def __get__(self, instance, owner):
pass
class User:
name1 = Name1()
name3 = Name3()
"""普通类对象"""
name4 = "kebi"
def __init__(self,name,birthday):
self.name2 = name
self.birthday = birthday
def __getattr__(self, item):
return "56789"
def __getattribute__(self, item):
#第一步:调用数据属性描述符name1
#第二步:调用self.name2
#第三步:调用非数据属性描述符name3
#第四步:调用类属性name4
#第五步:调用__getattr__方法
#第六步:抛出AttributeError
return "123"
user = User("ming",date(year=1992,month=8,day=12))
从上述示例中,关于user.age获取操作都是在__getattribute__方法中实现。
下面示例会证明上述过程:
示例1:
class User:
def __getattr__(self, item):
return "__getattr__"
user = User()
print(user.name)
示例2:类属性 > __getattr__
class User:
name = "class_attr"
def __getattr__(self, item):
return "__getattr__"
user = User()
print(user.name)
示例3:非数据描述符 > 类属性
class Name:
def __get__(self, instance, owner):
return "Non-data attribute descriptor"
class User:
name = Name()
def __getattr__(self, item):
return "__getattr__"
user = User()
print(user.name)
示例4:对象属性 > 非数据属性描述符
class Name:
def __get__(self, instance, owner):
return "Non-data attribute descriptor"
class User:
def __init__(self):
self.name = "object_attr"
user = User()
print(user.name)
示例5:数据属性描述符 > 对象属性
class Name:
def __get__(self, instance, owner):
return "data attribute descriptor"
def __set__(self, instance, value):
pass
class User:
name = Name()
def __init__(self):
self.name = "object_attr"
user = User()
print(user.name)
最后来看一个示例:
class Name:
def __get__(self, instance, owner):
return "Non-data attribute descriptor"
def __set__(self, instance, value):
pass
class User:
name = Name()
def __init__(self):
self.name = "object_attr"
def __getattr__(self, item):
return "__getattr__"
def __getattribute__(self, item):
return "123"
user = User()
print(user.name) #"123"
你可能会纳闷,为啥是这个?因为__getattribute__屏蔽了所有,
整个属性调用本身就在__getattribute__中进行,你现在重写,不就是覆盖了原有操作了吗。
还有一点需要注意的是,当你使用属性描述符设置值的时候,属性并不会出现在__dict__中。
class Name:
def __get__(self, instance, owner):
return self.name
def __set__(self, instance, value):
self.name = value
class User:
name = Name()
user = User()
user.name = "maoxian"
print(user.name) #maoxian
print(user.__dict__) #{}
当你使用__dict__来添加属性的时候,尤其是混合数据操作符的时候,可能会有以下异常:
class Name:
def __get__(self, instance, owner):
return self.name
def __set__(self, instance, value):
self.name = value
class User:
name = Name()
user = User()
user.__dict__["name"] = "maoxian"
print(user.__dict__) #{'name': 'maoxian'}
#当你在调用的user.nage的时候,优先还是先找数据属性描述符
print(user.name) #AttributeError: 'Name' object has no attribute 'name'
#当然下面方法还是可用:
user.__dict__['name']
四、__init__和__new__的区别
__init__:构造器,用来初始化对象的信息
__new__:在对象创建之前被创建。如果__new__没有返回值,那么也不会执行__init__函数。
class User:
def __new__(cls, *args, **kwargs): #这个cls代表当前类User
print("new")
def __init__(self): #self代表当前对象
print("init")
user = User()
#new 这里根本不会执行__init__函数
给__new__一个返回值就可以。
class User:
def __new__(cls, *args, **kwargs):
return super().__new__(cls)
def __init__(self):
print("init")
user = User()
__new__只有在新式类中实现
五、自定义元类
1.动态创建类的方式
(1)通过函数创建
def create_class(name):
if name == "User":
class User:
def __str__(self):
return "User"
return User
else:
class Person:
def __str__(self):
return "User"
return Person
User = create_class("User")
print(User)
user = User()
print(user)
(2)通过type来动态创建类
type中提供了三种创建方法:
type(object_or_name, bases, dict) type(object) -> the object's type type(name, bases, dict) -> a new type
下面是实现示例:
创建一个类:
User = type("User",(),{})
print(User)
定义一个属性:
User = type("User",(),{"name":"mao"})
user = User()
print(user.name)
定义一个方法:
def see(self): #这里必须要传递一个self参数
print("I see a dog")
User = type("User",(),{"name":"mao","see":see})
user = User()
user.see()
定义一个父类:
class Anlu:
def go_to_anlu(self):
print("go to anlu")
User = type("User",(Anlu,),{"name":"mao","see":see})
user = User()
user.go_to_anlu()
2.元类
元类就是创建类的类。比如type就是一个元类
a = 1 print(type(a)) #<class 'int'> print(type(int)) #<class 'type'> b = [1,2,3] print(type(b)) #<class 'list'> print(type(list)) #<class 'type'>
python中的int、str、list、dict、class都是type创建。
六、通过元类实现一个简单的ORM
在使用元类编程的时候,必须继承type类,一般在元类里面都是重写__new__方法,
用于在对象初始化之前做一些操作,比如数据的整理。
class IntegerField():
def __init__(self,db_column,max_length=0):
self._value = None
self.max_length = max_length
self.db_column = db_column
if max_length: #0 == Flase
if not isinstance(max_length,numbers.Integral):
raise ValueError("max_length must be int")
elif max_length < 0:
raise ValueError("max_length > 0")
else:
raise ValueError("max_length must bi have")
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value): #这个value = name
if not isinstance(value,numbers.Integral):
raise ValueError("value must be int")
if value < 0:
raise ValueError("value must greater than 0")
elif value < pow(10,self.max_length) -1:
raise ValueError("value should not be greater than max")
def __delete__(self, instance):
del self._value
class CharField():
def __init__(self,db_column,min_length=0,max_length=0):
self._value = None #_value是name的值,这个一般都会预留
self.db_column = db_column
self.min_length = min_length
self.max_length = max_length
if max_length:
if not isinstance(max_length,numbers.Integral):
raise ValueError("max_length must be int")
elif max_length < 0:
raise ValueError("max_length > 0")
else:
raise ValueError("max_length must bi have")
if min_length:
if not isinstance(min_length,numbers.Integral):
raise ValueError("min_length must be int")
elif min_length < 0:
raise ValueError("min_length > 0")
elif min_length > max_length:
raise ValueError("min_length less than max_length")
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value,str):
raise ValueError("value must be str")
if len(value) > self.max_length or len(value) < self.min_length:
raise ValueError("Value length is error")
self._value = value
def __delete__(self, instance):
del self._value
class Student():
name = CharField(db_column="",min_length=1,max_length=32)
age = IntegerField(db_column="",max_length=32)
class Meta:
db_table = "student"
上方代码通过属性描述符实现了对数据的检测功能。
虽然上面有了ORM的雏形,但是还需要对一些初始信息进行封装。比如字段信息
创建元类:ModelMetaClass
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()
#如果student中有Meta配置
if attrs_meta:
#取出db_table这个属性
table = getattr(attrs_meta,"db_table",None)
if table:
db_table = table #存在就会重新赋值
_meta["db_table"] = db_table
#重新整理attrs中的属性
attrs["_meta"] = _meta
attrs["fields"] = fields
del attrs["Meta"]
return super().__new__(cls, name, bases, attrs, **kwargs)
这就玩了吗?还需要一些初始化的改动,将初始化信息单独封装起来成BaseModel
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 not db_column:
db_column = key.lower()
fields.append(db_column)
value = getattr(self,key)
values.append(str(value))
sql = "insert into {db_table} ({fields}) value({values})".format(db_table=self._meta["db_table"],
fields=",".join(fields),
values=",".join(values))
pass


下面是完整代码:
import numbers
class Field:
pass
class IntegerField(Field):
# 属性描述符做字段验证
def __init__(self,db_column,max_length=0):
self._value = None
self.max_length = max_length
self.db_column = db_column
if max_length: #0 == Flase
if not isinstance(max_length,numbers.Integral):
raise ValueError("max_length must be int")
elif max_length < 0:
raise ValueError("max_length > 0")
else:
raise ValueError("max_length must bi have")
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value): #这个value = name
if not isinstance(value,numbers.Integral):
raise ValueError("value must be int")
if value < 0:
raise ValueError("value must greater than 0")
elif value < pow(10,self.max_length) -1:
raise ValueError("value should not be greater than max")
def __delete__(self, instance):
del self._value
class CharField(Field):
#属性描述符做字段验证
def __init__(self,db_column,min_length=0,max_length=0):
self._value = None #_value是name的值,这个一般都会预留
self.db_column = db_column
self.min_length = min_length
self.max_length = max_length
if max_length:
if not isinstance(max_length,numbers.Integral):
raise ValueError("max_length must be int")
elif max_length < 0:
raise ValueError("max_length > 0")
else:
raise ValueError("max_length must bi have")
if min_length:
if not isinstance(min_length,numbers.Integral):
raise ValueError("min_length must be int")
elif min_length < 0:
raise ValueError("min_length > 0")
elif min_length > max_length:
raise ValueError("min_length less than max_length")
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value,str):
raise ValueError("value must be str")
if len(value) > self.max_length or len(value) < self.min_length:
raise ValueError("Value length is error")
self._value = value
def __delete__(self, instance):
del self._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): #找出字段,为了方便验证重新定义Field类
fields[key] = value
attrs_meta = attrs.get("Meta",None)
_meta = {}
db_table = name.lower()
#如果student中有Meta配置
if attrs_meta:
#取出db_table这个属性
table = getattr(attrs_meta,"db_table",None)
if table:
db_table = table #存在就会重新赋值
_meta["db_table"] = db_table
#重新整理attrs中的属性
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): #支持Student(name=xxx)这个赋值操作
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 not db_column:
db_column = key.lower()
fields.append(db_column)
value = getattr(self,key)
values.append(str(value))
sql = "insert into {db_table} ({fields}) value({values})".format(db_table=self._meta["db_table"],
fields=",".join(fields),
values=",".join(values))
pass
class Student(BaseModel):
name = CharField(db_column="",min_length=1,max_length=32)
age = IntegerField(db_column="",max_length=32)
class Meta:
db_table = "student"
student = Student()
student.name = "kebi"
print(student.name)
student.save()
来源:https://www.cnblogs.com/yangmingxianshen/p/11288738.html