函数式编程
Python最佳实践模式
组合方法composed method
将程序功能切分到每个方法,每个方法执行一个单独的任务,保证方法中所有操作都是在同一个抽象层次上。
不好的代码:
class Boiler:def safety_check(self):# Convert fixed-point floating pointtemperature = self.modbus.read_holding()pressure_psi = self.abb_f100.register / F100_FACTORif psi_to_pascal(pressure_psi) > MAX_PRESSURE:if temperature > MAX_TEMPERATURE:# Shutdown!self.pnoz.relay[15] &= MASK_POWER_COILself.pnoz.port.write('$PL,15\0')sleep(RELAY_RESPONSE_DELAY)# Successful shutdown?if self.pnoz.relay[16] & MASK_POWER_OFF:# Play alarmwith open(BUZZER_MP3_FILE) as f:play_sound(f.read())
下面好的代码展示不同层次的抽象:bitmaps, 文件系统操作 播放声音:
# Betterclass Boiler:def safety_check(self):if any([self.temperature > MAX_TEMPERATURE,self.pressure > MAX_PRESSURE]):if not self.shutdown():self.alarm()def alarm(self):with open(BUZZER_MP3_FILE) as f:play_sound(f.read())@propertydef pressure(self):pressure_psi = abb_f100.register / F100_FACTORreturn psi_to_pascal(pressure_psi)...
- safety_check处理温度和压力
- alarm 处理文件和声音
- pressure 处理字节bit并转换它们
构造器方法Constructor method
提供构造器方法用来创建美观的实例,将所有参数都要传给这个方法:
# At initiation `point` is not well-formedpoint = Point()point.x = 12point.y = 5# Betterpoint = Point(x=12, y=5)
最后一行是推荐的好的实现方法。
使用类方法@classmethod制造多个构造器:
|
围绕方法执行(in Python: Context manager)
f = open('file.txt', 'w')f.write('hi')f.close()# Better好的代码如下with open('file.txt', 'w') as f:f.write('hi')with pytest.raises(ValueError):int('hi')with SomeProtocol(host, port) as protocol:protocol.send(['get', signal])result = protocol.receive()class SomeProtocol:def __init__(self, host, port):self.host, self.port = host, portdef __enter__(self):self._client = socket()self._client.connect((self.host,self.port))def __exit__(self, exception, value, traceback):self._client.close()def send(self, payload): ...def receive(self): ...
方法注解
一些小方法比注解(如下面 # Am I visible?)更有效。
|
选择消息
# Badif type(entry) is Film:responsible = entry.producerelse:responsible = entry.author# Shouldn't use type() or isinstance() in conditional --> smelly# Betterclass Film:...@propertydef responsible(self):return self.producerentry.responsible
意图显露消息
如何在intent 意图之间简单实现联系?
|
不变性常量方法
_DEFAULT_PORT = 1234class SomeProtocol:...def __enter__(self):self._client = socket()self._client.connect((self.host,self.port or _DEFAULT_PORT))return self# If you want to subclass SomeProtocol, you would have to overwrite every method!# Betterclass SomeProtocol:_default_port = 1234...def __enter__(self):self._client = socket()self._client.connect((self.host,self.port or self._default_port))
直接或间接的变量访问
直接是不需要getters和 setters
class Point:def __init__(self, x, y):self.x, self.y = x, y# Sometimes need more flexibility --> use propertiesclass Point:def __init__(self, x, y):self._x, self._y = x, y@propertydef x(self):return self._x@x.setterdef x(self, value):self._x = value
Enumeration (iteration) 枚举方法
# Bad# Can't change type of collection# e.g. can't change employees from a list to a setclass Department:def __init__(self, *employees):self.employees = employeesfor employee in department.employees:...# Betterclass Department:def __init__(self, *employees):self._employees = employeesdef __iter__(self):return iter(self._employees)for employee in department: # More readable, more composable...
临时变量
# Mehclass Rectangle:def bottom_right(self):return Point(self.left + self.width,self.top + self.height)# Better to use temporary variables for readabilityclass Rectangle:...def bottom_right(self):right = self.left + self.widthbottom = self.top + self.heightreturn Point(right, bottom)
Sets
|
等同方法
|
HASH哈希方法
|
排序集合 collection
|
级联Concatenation
|
简单枚举参数
# Awkwardfor options_shortcut in self.options_shortcuts:options_shortcut.this()options_shortcut.that()# Betterfor each in self.options_shortcuts:each.this()each.that()
Cascades级联
不要让方法返回值,而是返回它们自己:
|
显式好于隐式
# Mehdef match(self, items):for each in items:if each.match(self):return each# Is this supposed to reach the end? Is this a bug?# Betterdef match(self, items):for each in items:if each.match(self):return eachreturn None