编写漂亮的,可读的Python代码的最佳实践
参考: Raymond Hettinger在PyCon US 2015上的演讲 Beyond PEP 8 – Best practices for beautiful intelligible code
通常的一些方式如list comprehension在此就不列举了,主要补充一些其他方式
将非pythonic的代码做一层Adapter变成pythonic的
先看一段的代码:
import jnettool.tools.elements.NetworkElement
import jnettool.tools.Routing
import jnettool.tools.RouteInspector
ne = jnettool.tools.elements.NetworkElement('x.x.x.x')
try:
routing_table = ne.getRoutingTable()
except jnettoll.tools.elements.MissingVar:
logging.exception('No routing table found')
ne.cleanup('rollback')
else:
num_routes = routing_table.getSize()
for RToffset in range(num_routes):
route = routing_table.getRouteByIndex(RToffset)
name = route.getName()
ipaddr = route.getIpAddr()
print "%15s => %s" % (name, ipaddr)
finally:
ne.cleanup('commit')
ne.disconnect()这段代码初看起来并没有什么问题,也的确没有什么问题,肯定能够正常运行,而且完全符合PEP 8标准。然而这看上去更像是一段Java的代码,带有浓烈的Java色彩,如getter, setter,getXXXByIndex,还有try…except…finally这一系列处理异常的方式(当然大多数语言也都是这样子),但这和Python的简约风格相去甚远。我们先来看一下理想的pythonic的版本长的样子:
from nettools import NetworkElement
#使用with去除了冗长的try...except...finally
with NetworkElement('x.x.x.x') as ne:
#使用python中sequence类型遍历的特性,代替原来的类似下标形式
for route in routing_table:
#直接使用变量名替代getter函数
print "%15s => %s" % (route.name, route.ipaddr) 就这样,4行代码搞定! 那如何才能将之前的冗长代码变身为这样呢?你需要做一层pythonic的封装(Adapter):
import jnettool.tools.elements.NetworkElement
import jnettool.tools.Routing
class NetworkElementError(Exception):
pass
class NetworkElement(object):
def __init__(self, ipaddr):
self.ipaddr = ipaddr
self.oldne = jnettool.tools.elements.NetworkElement(ipaddr)
@property
def routing_table(self):
try:
return RoutingTable(self.oldne.getRoutingTable())
except jnettool.tools.elements.MissingVar:
raise NetworkElementError('No routing table found')
def __enter__(self):
return self
def __exit__(self, exctype, excinst, exctb):
if exctype == NetworkElementError:
logging.exception('No routing table found')
self.oldne.cleanup('rollback')
else:
self.oldne.cleanup('commit')
self.oldne.disconnect()
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.ipaddr)
class RoutingTable(object):
def __init__(self, oldrt):
self.oldrt = oldrt
def __len__(self):
return self.oldrt.getSize()
def __getitem__(self, index):
if index >= len(self):
raise IndexError
return Route(self.oldrt.getRouteByIndex(index))
class Route(object):
def __init__(self, old_route):
self.old_route = old_route
@property
def name(self):
return self.old_route.getName()
@property
def ipaddr(self):
return self.old_route.getIpAddr()简要描述下这个Adapter都做了什么
- 使用@property decorator来完成getter函数的功能,使用上将函数变成了变量。如果需要setter则需再加上如@name.setter,对赋值的参数进行验证等操作
- 实现了__enter__, __exit__函数用于支持with,隐藏了try…except…finally
- 实现了__len__, __getitem__使得对象具有了sequence类型的遍历特性
通过以上这一连串的修改,成功地将原本Java味如此重的代码pythonic化了
未完待续。。。