Better Code: 使用property
introduction
@property
是Python的一个内置函数, 通过property, 我们可以获得更好的可读性/可维护性
- 屏蔽细节, 对于外部使用者, 不需要知道更多的细节!
- 可读性更好, 调用方只有简单直接的逻辑, 而对象实体中包含了具体处理逻辑
- 可维护性更好, 细节不会被
扩散
到其他地方, 后续变更修改更容易
refactor
代码坏味道:
- 当你发现调用处, 每个地方获取到返回值后, 都做了一模一样的处理;
- 变更属性的格式/分隔符等细节, 发现需要修改多个地方代码
- 调用者需要感知到属性的当前值
[1, 2, 3]
(type: list) 以及原始值1,2,3
(type: str)
使用property之后:
- 所有调用处, 没有重复代码
- 变更属性的格式/分隔符等细节, 只需要修改一个地方
- 调用者只知道属性的当前值
[1, 2, 3]
usage
思路: 将属性通用的处理内化, fat model
对外屏蔽细节, 避免了使用者的冗余代码以及过多的认知负担;
- side effect: 可以在
set/get/delete
操作时, 额外进行一些处理, 例如校验, 做一些额外变更等(存在副作用) - adapter: 也可以作为类似adapter, 组合或者拼装出对调用者更为友好的做法
- 封装/facade: 对于类/对象实例, 更多的是起到
facade
模式的作用, 将更多的细节封装, 对外屏蔽, 只暴露有限的内容
1. 属性校验
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
2. 限制只读
class Student(object):
@property
def birth(self):
return self._birth
3. 拼装组合出额外的属性
class Staff:
@property
def email(self):
return f'{self.username}@{self.domain}'
4. 类型/格式转换
下面示例是django 中的一种用法, 在db中字段名为maintainers
, 是一个字符串, 但是对于模型使用者, maintainers
是一个列表, 通过property, 屏蔽了字段类型/分隔符等细节
class API(models.Model):
_maintainers = models.CharField(db_column='maintainers', max_length=1024)
@property
def maintainers(self) -> List[str]:
if not self._maintainers:
return []
return self._maintainers.split(";")
@maintainers.setter
def maintainers(self, data: List[str]):
self._maintainers = ";".join(data)
或者:
class A():
@property
def data(self):
return json.loads(self._data)
@data.setter
def data(self, d):
self._data = json.dumps(d)