函数式编程

Python最佳实践模式

   组合方法composed method

将程序功能切分到每个方法,每个方法执行一个单独的任务,保证方法中所有操作都是在同一个抽象层次上。

不好的代码:

class Boiler:
def safety_check(self):
# Convert fixed-point floating point
temperature = self.modbus.read_holding()
pressure_psi = self.abb_f100.register / F100_FACTOR
if psi_to_pascal(pressure_psi) > MAX_PRESSURE:
if temperature > MAX_TEMPERATURE:
# Shutdown!
self.pnoz.relay[15] &= MASK_POWER_COIL
self.pnoz.port.write('$PL,15\0')
sleep(RELAY_RESPONSE_DELAY)
# Successful shutdown?
if self.pnoz.relay[16] & MASK_POWER_OFF:
# Play alarm
with open(BUZZER_MP3_FILE) as f:
play_sound(f.read())

下面好的代码展示不同层次的抽象:bitmaps, 文件系统操作 播放声音:

# Better
class 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())
 
@property
def pressure(self):
pressure_psi = abb_f100.register / F100_FACTOR
return psi_to_pascal(pressure_psi)
...
  • safety_check处理温度和压力
  • alarm 处理文件和声音
  • pressure 处理字节bit并转换它们

 

构造器方法Constructor method

提供构造器方法用来创建美观的实例,将所有参数都要传给这个方法:

# At initiation `point` is not well-formed
point = Point()
point.x = 12
point.y = 5
 
# Better
point = Point(x=12, y=5)

最后一行是推荐的好的实现方法。

使用类方法@classmethod制造多个构造器:

class Point:
def __init__(self, x, y):
self.x, self.y = x, y
 
@classmethod
def polar(cls, r, theta):
return cls(r * cos(theta),
r * sin(theta))
 
point = Point.polar(r=13, theta=22.6)

 

围绕方法执行(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, port
 
def __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?)更有效。

if self.flags & 0b1000: # Am I visible?
...
 
# Better
...
@property
def is_visible(self):
return self.flags & 0b1000
 
if self.is_visible:
...

 

选择消息

# Bad
if type(entry) is Film:
responsible = entry.producer
else:
responsible = entry.author
 
# Shouldn't use type() or isinstance() in conditional --> smelly
 
# Better
class Film:
...
@property
def responsible(self):
return self.producer
 
entry.responsible

 

意图显露消息

如何在intent 意图之间简单实现联系?

class ParagraphEditor:
...
def highlight(self, rectangle):
self.reverse(rectangle)
 
# Better
class ParagraphEditor:
...
highlight = reverse # More readable, more composable

 

不变性常量方法

_DEFAULT_PORT = 1234
 
class 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!
 
# Better
class 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 properties
 
class Point:
def __init__(self, x, y):
self._x, self._y = x, y
 
@property
def x(self):
return self._x
 
@x.setter
def 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 set
class Department:
def __init__(self, *employees):
self.employees = employees
 
for employee in department.employees:
...
# Better
class Department:
def __init__(self, *employees):
self._employees = employees
 
def __iter__(self):
return iter(self._employees)
 
for employee in department: # More readable, more composable
...

临时变量

# Meh
class Rectangle:
def bottom_right(self):
return Point(self.left + self.width,
self.top + self.height)
 
# Better to use temporary variables for readability
class Rectangle:
...
def bottom_right(self):
right = self.left + self.width
bottom = self.top + self.height
return Point(right, bottom)

Sets

item in a_set
item not in a_set
 
# a_set <= other
a_set.is_subset(other)
 
# a_set | other
a_set.union(other)
 
# a_set & other
a_set.intersection(other)
 
# a_set - other
a_set.difference(other)

等同方法

obj == obj2
obj1 is obj2
 
class Book:
...
def __eq__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return (self.author == other.author and
self.title == other.title)

 

HASH哈希方法

class Book:
...
def __hash__(self):
return hash(self.author) ^ hash(self.other)

排序集合 collection

class Book:
...
def __lt__(self, other):
return (self.author, self.title) < (other.author, other.title)

级联Concatenation

class Config:
def __init__(self, **entries):
self.entries = entries
 
def __add__(self, other):
entries = (self.entries.items() +
other.entries.items())
return Config(**entries)
default_config = Config(color=False, port=8080)
config = default_config + Config(color=True)

简单枚举参数

# Awkward
for options_shortcut in self.options_shortcuts:
options_shortcut.this()
options_shortcut.that()
 
# Better
for each in self.options_shortcuts:
each.this()
each.that()

 

Cascades级联

不要让方法返回值,而是返回它们自己:

# Instead of this
self.release_water()
self.shutdown()
self.alarm()
 
class Reactor:
...
def release_water(self):
self.valve.open()
return self
 
self.release_water().shutdown().alarm()

显式好于隐式

# Meh
def match(self, items):
for each in items:
if each.match(self):
return each
# Is this supposed to reach the end? Is this a bug?
 
# Better
def match(self, items):
for each in items:
if each.match(self):
return each
return None

 

Python语言教程手册