Others
描述符
"""
某日,假设你需要一个类,来记录数学考试的分数。但是稍后你就发现了问题:分数为负值是没有意义的。
# class Score:
# def __init__(self, math):
# if math < 0:
# raise ValueError('math score must >= 0')
# self.math = math
但这样也没解决问题,因为分数虽然在初始化时不能为负,但后续修改时还是可以输入非法值:幸运的是,有内置装饰器 @property 可以解决此问题。
"""
class Score:
def __init__(self, math):
self.math = math
@property
def math(self):
# self.math 取值
return self._math
@math.setter
def math(self, value):
# self.math 赋值
if value < 0:
raise ValueError('math score must >= 0')
self._math = value
score = Score(90)
score.math =34
score._math # 34
# 上面关于赋值检查的 NonNegative 已经展示描述符的其中一种用途了:托管属性并复用代码,保持简洁。
class NonNegative:
# 注意这里
# __init__ 也没有了
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner=None):
return instance.__dict__.get(self.name)
def __set__(self, instance, value):
if value < 0:
raise ValueError(f'{self.name} score must >= 0')
instance.__dict__[self.name] = value
class Score:
# NonNegative() 不需要带参数以规定属性名了
math = NonNegative()
def __init__(self, math):
self.math = math
# score = Score(90)
# score.math =34
# score.math=-1
# ValueError: math score must >= 0
Cache
class Cache:
"""缓存描述符"""
def __init__(self, func):
self.func = func
self.name = func.__name__
def __get__(self, instance, owner=None):
instance.__dict__[self.name] = self.func(instance)
return instance.__dict__[self.name]
from time import sleep
class Foo:
@Cache
def bar(self):
sleep(5)
return 'Just sleep 5 sec...'
The normal built-in cache
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
Validator
from abc import ABC, abstractmethod
class Validator(ABC):
"""验证器抽象基类"""
def __set_name__(self, owner, name):
self.private_name = '_' + name
def __get__(self, instance, owner=None):
return getattr(instance, self.private_name)
def __set__(self, instance, value):
self.validate(value)
setattr(instance, self.private_name, value)
@abstractmethod
def validate(self, value):
pass
class OneOf(Validator):
"""字符串单选验证器"""
def __init__(self, *options):
self.options = set(options)
def validate(self, value):
if value not in self.options:
raise ValueError(f'Expected {value!r} to be one of {self.options!r}')
class Number(Validator):
"""数值类型验证器"""
def validate(self, value):
if not isinstance(value, (int, float)):
raise TypeError(f'Expected {value!r} to be an int or float')
class Component:
kind = OneOf('wood', 'metal', 'plastic')
quantity = Number()
def __init__(self, kind, quantity):
self.kind = kind
self.quantity = quantity
# Component('abc', 100) # ValueError
c = Component('wood', 100)
c.kind #'wood'
Last updated