python 描述符 装饰器

装饰器:装饰器就是一个函数,它可以接受一个函数作为输入并返回一个新的函数作为输出。

描述符:就是以特殊方法__get__()、__set__()、__delete__()的形式实现了三个核心的属性访问操作(对应于get、set和delete)的类。这些方法通过接受类实例作为输入来工作。之后,底层的实例字典会根据需要适当的进行调整。

class Integer:
    def __init__(self, name):
        self.name = name
        print('Integer __init__')

    def __get__(self, instance, cls):
        print('Integer __get__')
        if instance is None:
            return self
        else:
            return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('Integer __set__')
        if not isinstance(value, int):
            raise TypeError('Expceted an int')
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('Integer __delete__')
        del instance.__dict__[self.name]

class Point:
    print('Point')
    x = Integer('x')
    y = Integer('y')

    def __init__(self, x, y):
        print('28')
        self.x = x
        self.y = y
        print('Point __init__')
        print(self.__dict__)

p = Point(2, 3)
print('34')
print(p.x)
p.y = 5
# p.x = 2.3

Point
Integer __init__
Integer __init__
28
Integer __set__
Integer __set__
Point __init__
{'x': 2, 'y': 3}
34
Integer __get__
2
Integer __set__
p.x = 2.3
Traceback (most recent call last):
Point
  File "C:/Users/***/PycharmProjects/study_test/cookbook_8.9.2.py", line 39, in <module>
    p.x = 2.3
  File "C:/Users/***/PycharmProjects/study_test/cookbook_8.9.2.py", line 16, in __set__
    raise TypeError('Expceted an int')
TypeError: Expceted an int

 每个描述符方法都会接受被操纵的实例作为输入。要执行所请求的操作,底层的实例字典(即__dict__属性)会根据需要适当地进行调整。描述符的self.name属性会保存字典的键,通过这些键可以找到存储在实例字典中的实际数据。

关于描述符,常容易困惑的地方就是它们只能再类的层次上定义,不能根据实例来产生。因此,下面的代码是无法正常工作的:

class Point:
    def __init__(self, x, y):
        self.x = Integer('x')
        self.y = Integer('y')
        self.x = x
        self.y = y

此外,在实现__get__()方法时比想象中的还要复杂一些: 

__get__()看起来复杂的原因在于实例变量(instance)和类变量(cls)之间是有区别的。如果是以类变量的形式返回描述符,参数instance应该设为None。在这种情况下,标准的做法就是简单的返回描述符实例本身(尽管此时做任何类型的自定义处理也是允许的)。实例如下:

print(Point.x)
<__main__.Integer object at 0x0000026BB2EAE7B8>

下面是一些更加高级的基于描述符的代码: 

class Type:
    def __init__(self, name, expected_type):
        self.name = name
        self.expected_type = expected_type
        print('Type __init__')

    def __get__(self, instance, cls):
        print('Type __get__')
        if instance is None:
            return self
        else:
            return instance._dict__[self.name]

    def __set__(self, instance, value):
        print('Type __set__')
        if not isinstance(value, self.expected_type):
            raise TypeError('Expected ' + str(self.expected_type))
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('Type __delete__')
        del instance.__dict__[self.name]


def typeassert(**kwargs):
    def decorate(cls):
        for name, expected_type in kwargs.items():
            print('typeassert decorate setattr')
            print(name, expected_type)
            print(cls)
            setattr(cls, name, Type(name, expected_type))
        return cls
    return decorate


@typeassert(name=str, shares=int, price=float)
class Stock:
    def __init__(self, name, shares, price):
        print('Stock __init__ before')
        self.name = name
        self.shares = shares
        self.price = price
        print('Stock __init__ later')


s = Stock('s', 2, 3.5)
print(s)
print(Stock)

输出:
typeassert decorate setattr
name <class 'str'>
<class '__main__.Stock'>
Type __init__
typeassert decorate setattr
shares <class 'int'>
<class '__main__.Stock'>
Type __init__
typeassert decorate setattr
price <class 'float'>
<class '__main__.Stock'>
Type __init__
Stock __init__ before
Type __set__
Type __set__
Type __set__
Stock __init__ later
<__main__.Stock object at 0x0000011DAD4782E8>
<class '__main__.Stock'>

Python setattr() 函数对应函数 getattr(),用于设置属性值,该属性不一定是存在的。

setattr(object, name, value)

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值