文章目录
描述符方法
一、概述
一般来说,描述符是带有“绑定行为”的对象属性,它的属性访问已经被描述符协议中的方法覆盖了.这些方法是__get__(),set(),和__delete__().
其中instance的参数表示实例化的对象,owner代表实例属于的类
描述符必须定义在类属性中,而不能定义在构造函数中。
注意,只有在新式类中描述符才会起作用(新式类继承type或者object class),python3中创建的类均为新式类。
class Foo:
def __get__(self, instance, owner):
print("-->get方法")
def __set__(self,instance,value):
print("-->set方法",instance,value) #例子中的self是x,instance是b1,value是10
instance.__dict__['x'] = value
# def __del__(self,instance):
# print("-->del方法")
class Bar:
x = Foo()
def __init__(self,n):
self.x = n
b1 = Bar(10)
#程序内部执行过程
#b1.x=10(x为foo类的实例) --> x.__set__(self,b1,10) --> b1.__dict__['x'] = 10
#b1实例生成的时候b1.x属性是foo生成的实例
#因此类似于当你访问b1.x这个属性的时候,实际上是在访问foo的实例
#self.x赋值的时候会触发__set__方法
print(b1.__dict__)
#
-->set方法 <__main__.Bar object at 0x102b07908> 10
{'x': 10}
二、访问优先级
- 类属性
- 数据描述符(数据描述符是指拥有__set__方法的描述符)
- 实例属性
- 非数据描述符
- 以上都找不到则触发__getattr__()
三、通过描述符来限制传入类型的实例
3.1 实现功能
class typecheck:
def __init__(self,key,type):
self.key = key
self.type = type
def __get__(self, instance, owner):
return instance.__dict__[self.key]
def __set__(self, instance, value):
if isinstance(value,self.type):
instance.__dict__[self.key] = value #f1.__dict__[name.'name'] = str
else:
raise TypeError("你输入的类型应该是",self.type)
def __del__(self):
pass
class Foo:
name = typecheck('name',str)
age = typecheck('age',int)
def __init__(self,name,age):
self.name = name
self.age = age
f1 = Foo(12,12) #name输入了整型的值会产生报错
print(f1.name)
####
TypeError: ('你输入的类型应该是', <class 'str'>)
3.2 利用装饰器简化代码
name = typecheck('name',str)
age = typecheck('age',int)
这两行描述符实例化的代码,重复程度高,如果再多几行,则显得很臃肿,我们此时可以使用装饰器来对代码进行简化
## 定义一个装饰器函数typed,传入k,v值
def typed(**kwargs):
def wrapper(obj):
for k,v in kwargs.items():
setattr(obj,k,typecheck(k,v))
# setattr(Foo,name,typecheck(('name',str)) 给描述符赋值
return obj
return wrapper
@typed(name=str,age=int)
#typed(name=str,age=int) --> @wrapper --> wrapper(Foo) --> Foo
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
f1 = Foo('hyj',12)
print(f1.name)
四、利用描述符实现自制的property方法
4.1简单实现property
property是将类中定义的方法封装成一个类属性来进行调用,现在我们可以用描述符加装饰器的组合类实现。
class Myproperty:
def __init__(self,func):
print("--> 装饰器生成描述符")
self.func = func
def __get__(self, instance, owner):
print("访问了描述符实例")
return self.func(instance)
class Hoom:
def __init__(self,length,width):
self.length = length
self.width = width
@Myproperty #area = Myproperty(area)
def area(self):
return self.length * self.width
h1 = Hoom(30,40)
print(h1.area)
#@Myproperty 语法糖的本质就是执行area = Myproperty(area)
#现在Myproperty是一个描述符类,因此area = Myproperty(area)为Hoom这个类添加了一个描述符实例
#这个描述符类初始化的时候将area这个方法传入了,所以当调用h1.area实际上是调用了area实例中的__get__方法。
4.2 property的进阶用法
class Phone:
def __init__(self,price):
self._price = price
@property
def Price(self):
return self._price
@Price.setter
def Price(self,value):
if isinstance(value,int) and value > 0:
self._price = value
else:
print("价格请输入整数")
@property
def Count(self):
return self._price * 0.8
h1 = Phone(20)
h1.Price = -1
print(h1.Price)
h1.Count = 30
print(h1.Count)
###
价格请输入整数
20
Traceback (most recent call last):
File "/Users/huyinjie/PycharmProjects/study/strex.py", line 38, in <module>
h1.Count = 30
AttributeError: can't set attribute
利用property里的setter方法来进行对属性的控制,而不设置setter方法的话,那个属性就只是可读属性,就像例子中的Count属性一样
def get_Price(self):
return self._price
def set_Price(self,value):
if isinstance(value,int) and value > 0:
self._price = value
else:
print("价格请输入整数")
Price = property(get_Price,set_Price)
# 这种装饰方法和上面那个例子是等价的