Files
python3-cookbook/source/c09/p21_avoid_repetitive_property_methods.rst
2020-08-31 21:51:15 +08:00

102 lines
3.0 KiB
ReStructuredText
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

==============================
9.21 避免重复的属性方法
==============================
----------
问题
----------
你在类中需要重复的定义一些执行相同逻辑的属性方法,比如进行类型检查,怎样去简化这些重复代码呢?
----------
解决方案
----------
考虑下一个简单的类,它的属性由属性方法包装:
.. code-block:: python
class Person:
def __init__(self, name ,age):
self.name = name
self.age = age
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError('name must be a string')
self._name = value
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if not isinstance(value, int):
raise TypeError('age must be an int')
self._age = value
可以看到,为了实现属性值的类型检查我们写了很多的重复代码。
只要你以后看到类似这样的代码,你都应该想办法去简化它。
一个可行的方法是创建一个函数用来定义属性并返回它。例如:
.. code-block:: python
def typed_property(name, expected_type):
storage_name = '_' + name
@property
def prop(self):
return getattr(self, storage_name)
@prop.setter
def prop(self, value):
if not isinstance(value, expected_type):
raise TypeError('{} must be a {}'.format(name, expected_type))
setattr(self, storage_name, value)
return prop
# Example use
class Person:
name = typed_property('name', str)
age = typed_property('age', int)
def __init__(self, name, age):
self.name = name
self.age = age
----------
讨论
----------
本节我们演示内部函数或者闭包的一个重要特性,它们很像一个宏。例子中的函数 ``typed_property()``
看上去有点难理解,其实它所做的仅仅就是为你生成属性并返回这个属性对象。
因此,当在一个类中使用它的时候,效果跟将它里面的代码放到类定义中去是一样的。
尽管属性的 ``getter````setter`` 方法访问了本地变量如 ``name`` , ``expected_type``
以及 ``storage_name`` ,这个很正常,这些变量的值会保存在闭包当中。
我们还可以使用 ``functools.partial()`` 来稍稍改变下这个例子,很有趣。例如,你可以像下面这样:
.. code-block:: python
from functools import partial
String = partial(typed_property, expected_type=str)
Integer = partial(typed_property, expected_type=int)
# Example:
class Person:
name = String('name')
age = Integer('age')
def __init__(self, name, age):
self.name = name
self.age = age
其实你可以发现这里的代码跟8.13小节中的类型系统描述器代码有些相似。