# Others

### 描述符

```python
"""
某日，假设你需要一个类，来记录数学考试的分数。但是稍后你就发现了问题：分数为负值是没有意义的。

# 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
```

```python
# 上面关于赋值检查的 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

```python
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

```python
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

```python
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'
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zeliang-yao.gitbook.io/my-note-zeliang-yao/useful/python-oop/others.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
