• 9.21 避免重复的属性方法
    • 问题
    • 解决方案
    • 讨论

    9.21 避免重复的属性方法

    问题

    你在类中需要重复的定义一些执行相同逻辑的属性方法,比如进行类型检查,怎样去简化这些重复代码呢?

    解决方案

    考虑下一个简单的类,它的属性由属性方法包装:

    1. class Person:
    2. def __init__(self, name ,age):
    3. self.name = name
    4. self.age = age
    5.  
    6. @property
    7. def name(self):
    8. return self._name
    9.  
    10. @name.setter
    11. def name(self, value):
    12. if not isinstance(value, str):
    13. raise TypeError('name must be a string')
    14. self._name = value
    15.  
    16. @property
    17. def age(self):
    18. return self._age
    19.  
    20. @age.setter
    21. def age(self, value):
    22. if not isinstance(value, int):
    23. raise TypeError('age must be an int')
    24. self._age = value

    可以看到,为了实现属性值的类型检查我们写了很多的重复代码。只要你以后看到类似这样的代码,你都应该想办法去简化它。一个可行的方法是创建一个函数用来定义属性并返回它。例如:

    1. def typed_property(name, expected_type):
    2. storage_name = '_' + name
    3.  
    4. @property
    5. def prop(self):
    6. return getattr(self, storage_name)
    7.  
    8. @prop.setter
    9. def prop(self, value):
    10. if not isinstance(value, expected_type):
    11. raise TypeError('{} must be a {}'.format(name, expected_type))
    12. setattr(self, storage_name, value)
    13.  
    14. return prop
    15.  
    16. # Example use
    17. class Person:
    18. name = typed_property('name', str)
    19. age = typed_property('age', int)
    20.  
    21. def __init__(self, name, age):
    22. self.name = name
    23. self.age = age

    讨论

    本节我们演示内部函数或者闭包的一个重要特性,它们很像一个宏。例子中的函数 typed_property()看上去有点难理解,其实它所做的仅仅就是为你生成属性并返回这个属性对象。因此,当在一个类中使用它的时候,效果跟将它里面的代码放到类定义中去是一样的。尽管属性的 gettersetter 方法访问了本地变量如 name , expected_type以及 storate_name ,这个很正常,这些变量的值会保存在闭包当中。

    我们还可以使用 functools.partial() 来稍稍改变下这个例子,很有趣。例如,你可以像下面这样:

    1. from functools import partial
    2.  
    3. String = partial(typed_property, expected_type=str)
    4. Integer = partial(typed_property, expected_type=int)
    5.  
    6. # Example:
    7. class Person:
    8. name = String('name')
    9. age = Integer('age')
    10.  
    11. def __init__(self, name, age):
    12. self.name = name
    13. self.age = age

    其实你可以发现,这里的代码跟8.13小节中的类型系统描述器代码有些相似。

    原文:

    http://python3-cookbook.readthedocs.io/zh_CN/latest/c09/p21_avoid_repetitive_property_methods.html