python类之描述符方法

描述符方法

一、概述

一般来说,描述符是带有“绑定行为”的对象属性,它的属性访问已经被描述符协议中的方法覆盖了.这些方法是__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)
# 这种装饰方法和上面那个例子是等价的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值