【Python】使用31条规则编写高质量且美丽的Python代码
Raymond Hettinger在pycon US 2013 视频,幻灯片上的讲话。
代码示例和直接引用都来自Raymond的演讲。我在这里复制它们是为了我自己的启发和希望别人会发现它们像我一样方便!
原文:https://github.com/JeffPaine/beautiful_idiomatic_python
基础
1、循环一系列数字
for i in [0, 1, 2, 3, 4, 5]:
print i**2
for i in range(6):
print i**2
更好
for i in xrange(6):
print i**2
xrange
在范围内创建一个迭代器,一次生成一个值。这种方法比内存效率更高range
。在python 3中xrange
重命名为range
。
2、循环收集
colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)):
print colors[i]
更好
for color in colors:
print color
3、向后循环
colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)-1, -1, -1):
print colors[i]
更好
for color in reversed(colors):
print color
4、循环收集和索引
colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)):
print i, '--->', colors[i]
更好
for i, color in enumerate(colors):
print i, '--->', color
它快速而美观,可以帮助您跟踪各个索引并增加它们。
每当你发现自己在[集合]中操纵索引时,你可能做错了。
5、循环两个集合
names = ['raymond', 'rachel', 'matthew'
colors = ['red', 'green', 'blue', 'yellow']
n = min(len(names), len(colors))
for i in range(n):
print names[i], '--->', colors[i]
for name, color in zip(names, colors):
print name, '--->', color
更好
for name, color in izip(names, colors):
print name, '--->', color
zip
在内存中创建一个新列表并占用更多内存。izip
效率比zip
。注意:在python 3 izip
中重命名zip
并提升为内置替换旧的zip
。
6、按排序顺序循环
colors = ['red', 'green', 'blue', 'yellow']
# Forward sorted order
for color in sorted(colors):
print colors
# Backwards sorted order
for color in sorted(colors, reverse=True):
print colors
7、自定义排序顺序
colors = ['red', 'green', 'blue', 'yellow']
def compare_length(c1, c2):
if len(c1) < len(c2): return -1
if len(c1) > len(c2): return 1
return 0
print sorted(colors, cmp=compare_length)
更好
print sorted(colors, key=len)
原作很慢,写起来很不愉快。此外,python 3中不再提供比较功能。
8、调用函数直到sentinel值
blocks = []
while True:
block = f.read(32)
if block == '':
break
blocks.append(block)
更好
blocks = []
for block in iter(partial(f.read, 32), ''):
blocks.append(block)
iter
有两个论点。第一个你一遍又一遍地打电话,第二个是哨兵价值。
9、区分循环中的多个退出点
def find(seq, target):
found = False
for i, value in enumerate(seq):
if value
found = True
break
if not found:
return -1
return i
更好
def find(seq, target):
for i, value in enumerate(seq):
if value == target:
break
else:
return -1
return i
每个for
循环内部都是一个else
。
10、循环字典键
d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
for k in d:
print k
for k in d.keys():
if k.startswith('r'):
del d[k]
什么时候应该使用第二个而不是第一个?当你改变字典。
如果你在迭代它时改变某些东西,那么你就生活在一种罪恶的状态中,并且应该发生在你身上的事情。
d.keys()
制作所有**的副本并将其存储在列表中。然后你可以修改字典。注意:在python 3中迭代一个字典你必须显式写:list(d.keys())
因为d.keys()
返回一个“字典视图”(一个可以在字典键上提供动态视图的迭代)。见文档。
11、循环字典键和值
# Not very fast, has to re-hash every key and do a lookup
for k in d:
print k, '--->', d[k]
# Makes a big huge list
for k, v in d.items():
print k, '--->', v
更好
for k, v in d.iteritems():
print k, '--->', v
iteritems()
更好,因为它返回一个迭代器。注意:在python 3中没有iteritems()
,items()
行为接近于什么iteritems()
。见文档。
12、从对构造字典
names = ['raymond', 'rachel', 'matthew'
colors = ['red', 'green', 'blue']
d = dict(izip(names, colors))
# {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
对于python 3: d = dict(zip(names, colors))
13、用字典统计
colors = ['red', 'green', 'red', 'blue', 'green', 'red']
# Simple, basic way to count. A good start for beginners.
d = {}
for color in colors:
if color not in d:
d[color] = 0
d[color] += 1
# {'blue': 1, 'green': 2, 'red': 3}
更好
d = {}
for color in colors:
d[color] = d.get(color, 0) + 1
# Slightly more modern but has several caveats, better for advanced users
# who understand the intricacies
d = collections.defaultdict(int)
for color in colors:
d[color] += 1
14、用词典分组 - 第一部分和第二部分
names = ['raymond', 'rachel', 'matthew', 'roger',
'betty', 'melissa', 'judith', 'charlie']
# In this example, we're grouping by name length
d = {}
for name in names:
key = len(name)
if key not in d:
d[key] = []
d[key].append(name)
# {5: ['roger', 'betty'], 6: ['rachel', 'judith'], 7: ['raymond', 'matthew', 'melissa', 'charlie']}
d = {}
for name in names:
key = len(name)
d.setdefault(key, []).append(name)
更好
d = collections.defaultdict(list)
for name in names:
key = len(name)
d[key].append(name)
15、是一个字典popitem()原子?
d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
while
key, value = d.popitem()
print key, '-->', value
popitem
是原子的,所以你不必在它周围放置锁以在线程中使用它。
16、链接词典
defaults = {'color': 'red', 'user': 'guest'}
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--user')
parser.add_argument('-c', '--color')
namespace = parser.parse_args([])
command_line_args = {k:v for k, v in vars(namespace).items() if v}
# The common approach below allows you to use defaults at first, then override them
# with environment variables and then finally override them with command line arguments.
# It copies data like crazy, unfortunately.
d =
d.update(os.environ)
d.update(command_line_args)
更好
d = ChainMap(command_line_args, os.environ, defaults)
ChainMap
已经被引入python 3.快速而美丽。
提高清晰度
- 位置论证和指标很好
- 关键字和名称更好
- 第一种方式是方便计算机
- 第二个对应于人类的思考方式
18、使用关键字参数澄清函数调用
twitter_search('@obama', False, 20, True)
更好
twitter_search('@obama', retweets=False, numtweets=20, popular=True)
略微(微秒)慢,但值得为代码清晰度和开发人员节省时间。
19、使用命名元组澄清多个返回值
# Old testmod return value
doctest.testmod()
# (0, 4)
# Is this good or bad? You don't know because it's not clear.
更好
# New testmod return value, a namedTuple
doctest.testmod()
# TestResults(failed=0, attempted=4)
namedTuple是元组的子类,因此它们仍像普通元组一样工作,但更友好。
要创建一个namedTuple:
TestResults = namedTuple('TestResults', ['failed', 'attempted'])
20、解包序列
p = 'Raymond', 'Hettinger', 0x30, '[email protected]'
# A common approach / habit from other languages
fname = p[0]
lname = p[1]
age = p[2]
email = p[3]
更好
fname, lname, age, email = p
第二种方法使用元组解包,更快,更易读。
21、更新多个状态变量
def fibonacci(n):
x = 0
y = 1
for i in range(n):
t =
y = x +
x = t
更好
def fibonacci(n
x, y = 0, 1
for i in range(n):
print x
x, y = y, x + y
第一种方法的问题
- x和y是状态,状态应该一次全部更新,或者在状态不匹配的行和问题的常见来源之间更新
- 订购事宜
- 它的水平太低了
第二种方法是更高级别,不会冒错误的订单并且速度快。
22、同步状态更新
tmp_x = x + dx *
tmp_y = y + dy * t
# NOTE: The "influence" function here is just an example function, what it does
# is not important. The important part is how to manage updating multiple
# variables at once.
tmp_dx = influence(m, x, y, dx, dy, partial='x')
tmp_dy = influence(m, x, y, dx, dy, partial='y')
x = tmp_x
y = tmp_y
dx = tmp_dx
dy = tmp_dy
更好
# NOTE: The "influence" function here is just an example function, what it does
# is not important. The important part is how to manage updating multiple
# variables at once.
x, y, dx, dy = (x + dx * t,
y + dy * t,
influence(m, x, y, dx, dy, partial='x'),
influence(m, x, y, dx, dy, partial='y'))
效率
- 优化基本规则
- 不要让数据不必要地移动
- 只需要小心谨慎就可以避免O(n ** 2)行为而不是线性行为
基本上,只是不要不必要地移动数据。
23、连接字符串
names = ['raymond', 'rachel', 'matthew', 'roger',
'betty', 'melissa', 'judith', 'charlie']
s = names[0]
for name in names[1:]:
s += ', ' + name
print s
更好
print ', '.join(names)
24、更新序列
names = ['raymond', 'rachel', 'matthew', 'roger',
'betty', 'melissa', 'judith', 'charlie']
del names[0]
# The below are signs you're using the wrong data structure
names.pop(0)
names.insert(0, 'mark')
更好
names = collections.deque(['raymond', 'rachel', 'matthew', 'roger',
'betty', 'melissa', 'judith', 'charlie'])
# More efficient with collections.deque
del names[0]
names.popleft()
names.appendleft('mark')
装饰器和上下文管理器
- 帮助将业务逻辑与管理逻辑分开
- 用于分解代码和改进代码重用的干净,漂亮的工具
- 良好的命名至关重要。
- 记住蜘蛛侠规则:强大的力量,责任重大!
25、使用装饰器来分解管理逻辑
# Mixes business / administrative logic and is not reusable
def web_lookup(url, saved={}):
if url in saved:
return saved[url]
page = urllib.urlopen(url).read()
saved[url] = page
return page
更好
@cache
def web_lookup(url):
return urllib.urlopen(url).read()
注意:因为python 3.2在标准库中有一个装饰器:functools.lru_cache
。
26、因子分解临时背景
# Saving the old, restoring the new
old_context = getcontext().copy()
getcontext().prec = 50
print Decimal(355) / Decimal(113)
setcontext(old_context)
更好
27、如何打开和关闭文件
f = open('data.txt')
try:
data = f.read()
finally:
f.close()
更好
with open('data.txt') as f:
data = f.read()
28、如何使用锁
# Make a lock
lock = threading.Lock()
# Old-way to use a lock
lock.acquire()
try:
print 'Critical section 1'
print 'Critical section 2'
finally:
lock.release()
更好
# New-way to use a lock
with lock:
print 'Critical section 1'
print 'Critical section 2'
29、因子分解临时背景
try:
os.remove('somefile.tmp')
except OSError:
pass
更好
with ignored(OSError):
os.remove('somefile.tmp')
ignored
是python 3.4中的新功能,文档。注意:ignored
实际上是suppress
在标准库中调用的。
ignored
在此期间制作自己的上下文管理器:
@contextmanager
def ignored(*exceptions):
try:
yield
except exceptions:
pass
坚持在您的utils目录中,你也可以忽略异常
30、因子分解临时背景
# Temporarily redirect standard out to a file and then return it to normal
with open('help.txt', 'w') as f:
oldstdout = sys.stdout
sys.stdout = f
try:
help(pow)
finally:
sys.stdout = oldstdout
更好
with open('help.txt', 'w') as f:
with redirect_stdout(f):
help(pow)
redirect_stdout
建议用于python 3.4,bug报告。
滚动自己的redirect_stdout
上下文管理器
@contextmanager
def redirect_stdout(fileobj):
oldstdout = sys.stdout
sys.stdout = fileobj
try:
yield fileobj
finally
sys.stdout = oldstdout
简洁富有表现力的单行
两条相互矛盾的规则:
- 不要在一条线上放太多
- 不要将思想原子分解为亚原子粒子
雷蒙德的规则:
- 一行逻辑代码等于英语中的一个句子
31、列表理解和生成器表达式
result = []
for i in range(10):
s = i ** 2
result.append(s)
print sum(result)
更好
print sum(i**2 for i in xrange(10))
第一种方式告诉你该做什么,第二种方式告诉你你想要什么。
下一篇: AVPlayer代码解析