My Note / Zeliang YAO
  • Zeliang's Note
  • Dremio
    • Custom Class
  • 💕Python
    • Design Pattern
      • Creational
        • Abstract Factory
        • Factory Method
        • Singleton
        • Builder / Director
      • Structural
        • Adapter
    • Boto3
    • Typing
    • String
    • Requests
    • Iterator & Iterable
      • Consuming iterator manually
      • Lazy Iterable
    • Genrators
    • itertools
    • Collections
    • Customization
      • Customize built-in
      • Logging
      • Hdf5
      • Sqlite3 & Df
    • Pandas
      • Basic
      • Data cleaning
      • Merge, Join, Concat
      • Useful tricks
      • Simple model
      • Pandas acceleration
    • Pandas time series
      • Date Range
      • Datetime Index
      • Holidays
      • Function_to_date_time
      • Period
      • Time zone
    • *args and**kwargs
    • Context Manager
    • Lambda
    • SHA
    • Multithreading
      • Threading
      • Speed Up
    • Email
    • Improvement
    • Useful functions
    • Python OOP
      • Basic
      • @static / @class method
      • attrs module
      • Dataclasses
      • Dataclasses example
      • Others
    • Design patterns
      • Creational Patterns
      • Structural Patterns
      • Behavioral Patterns
  • 🐣Git/Github
    • Commands
  • K8s
    • Useful commands
  • Linux
    • Chmod
Powered by GitBook
On this page
  • 描述符
  • Cache
  • Validator

Was this helpful?

  1. Python
  2. Python OOP

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'
PreviousDataclasses exampleNextDesign patterns

Last updated 3 years ago

Was this helpful?

💕