面向优雅的 Python 编程

很早就想整理一篇关于如何让 Python 代码风格更加优雅的博客,以供查阅。Python 的设计理念是 “优雅”、“简单” 和 “明确”, 引用一下 Python 之禅:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.

Explicit is better than implicit.

Simple is better than complex.

Complex is better than complicated.

Flat is better than nested.

Sparse is better than dense.

Readability counts.

Special cases aren’t special enough to break the rules.

Although practicality beats purity.

Errors should never pass silently.

Unless explicitly silenced.

In the face of ambiguity, refuse the temptation to guess.

There should be one-- and preferably only one --obvious way to do it.

Although that way may not be obvious at first unless you’re Dutch.

Now is better than never.

Although never is often better than right now.

If the implementation is hard to explain, it’s a bad idea.

If the implementation is easy to explain, it may be a good idea.

Namespaces are one honking great idea – let’s do more of those!

编程规范

Python 编程风格指引
PEP 8: https://peps.python.org/pep-0008/

中文版: https://github.com/kernellmd/Knowledge/blob/master/Translation/PEP%208%20%E4%B8%AD%E6%96%87%E7%BF%BB%E8%AF%91.md

一些实践

1. 使用 with 关键字

在对文件等需要及时清理的对象进行操作时,使用 with 进行管理,能够在操作完成之后及时对对象进行关闭或清理,简化操作,例如:

1
2
with open('test.txt', 'w') as f:
f.write('hello world!\n')

2. 使用 zip 关键字

zip 让我们能够同时取出多个可迭代对象中对应的元素,返回由这些元素组成的元组,例如:

1
2
3
4
5
x = [1, 2, 3]
y = ['a', 'b', 'c']
z = ['x', 'y', 'z']
for one_x, one_y, one_z in zip(x, y, z):
print(one_x, one_y, one_z)

另外 zip 对于 numpy.array 和 list 对象的混合情况同样适用。

输出:

1 a x

2 b y

3 c z

3. 使用列表推导式

在代码中恰当地引入列表推导式可以大大简化和缩短代码,例如:

1
2
x = [[i, j] for i in range(10) for j in range(10)]
y = [[one_x[0]+10, one_x[1]-10] for one_x in x]

4. 使用 lambda 表达式

使用 lambda 可以简化函数定义,缩短代码,例如:

1
2
fun = lambda a, b: '{} + {} = {}'.format(a, b, a+b)
fun(1, 2)

5. 使用 mapfilter 函数

map 函数会根据传入的函数对指定可迭代对象(list, turple…)中的元素逐个进行操作,例如:

1
2
3
4
5
fun = lambda a, b: '{} + {} = {}'.format(a, b, a+b)
x = [i for i in range(5)]
y = [i+2 for i in x]
for i in map(fun, x, y):
print(i)

输出:

0 + 2 = 2

1 + 3 = 4

2 + 4 = 6

3 + 5 = 8

4 + 6 = 10

filtermap 功能类似,不同的是 filter 是对符合符合要求的元素进行过滤操作,例如:

1
2
3
4
fun = lambda x: x>0
x = [i for i in range(-3, 3)]
for i in filter(fun, x):
print(i)

输出:

1

2

6. 错误处理

错误处理在 Python 编程中也非常重要,主要用到的是 tryexceptfinallyraise 关键字,Python 中提供了非常多的标准异常:

C 名称

Python 名称

备注

PyExc_BaseException

BaseException

[1]

PyExc_Exception

Exception

[1]

PyExc_ArithmeticError

ArithmeticError

[1]

PyExc_AssertionError

AssertionError

PyExc_AttributeError

AttributeError

PyExc_BlockingIOError

BlockingIOError

PyExc_BrokenPipeError

BrokenPipeError

PyExc_BufferError

BufferError

PyExc_ChildProcessError

ChildProcessError

PyExc_ConnectionAbortedError

ConnectionAbortedError

PyExc_ConnectionError

ConnectionError

PyExc_ConnectionRefusedError

ConnectionRefusedError

PyExc_ConnectionResetError

ConnectionResetError

PyExc_EOFError

EOFError

PyExc_FileExistsError

FileExistsError

PyExc_FileNotFoundError

FileNotFoundError

PyExc_FloatingPointError

FloatingPointError

PyExc_GeneratorExit

GeneratorExit

PyExc_ImportError

ImportError

PyExc_IndentationError

IndentationError

PyExc_IndexError

IndexError

PyExc_InterruptedError

InterruptedError

PyExc_IsADirectoryError

IsADirectoryError

PyExc_KeyError

KeyError

PyExc_KeyboardInterrupt

KeyboardInterrupt

PyExc_LookupError

LookupError

[1]

PyExc_MemoryError

MemoryError

PyExc_ModuleNotFoundError

ModuleNotFoundError

PyExc_NameError

NameError

PyExc_NotADirectoryError

NotADirectoryError

PyExc_NotImplementedError

NotImplementedError

PyExc_OSError

OSError

[1]

PyExc_OverflowError

OverflowError

PyExc_PermissionError

PermissionError

PyExc_ProcessLookupError

ProcessLookupError

PyExc_RecursionError

RecursionError

PyExc_ReferenceError

ReferenceError

PyExc_RuntimeError

RuntimeError

PyExc_StopAsyncIteration

StopAsyncIteration

PyExc_StopIteration

StopIteration

PyExc_SyntaxError

SyntaxError

PyExc_SystemError

SystemError

PyExc_SystemExit

SystemExit

PyExc_TabError

TabError

PyExc_TimeoutError

TimeoutError

PyExc_TypeError

TypeError

PyExc_UnboundLocalError

UnboundLocalError

PyExc_UnicodeDecodeError

UnicodeDecodeError

PyExc_UnicodeEncodeError

UnicodeEncodeError

PyExc_UnicodeError

UnicodeError

PyExc_UnicodeTranslateError

UnicodeTranslateError

PyExc_ValueError

ValueError

PyExc_ZeroDivisionError

ZeroDivisionError

以上内容引用自官方文档

例如:

1
2
3
4
5
6
def x_greater_than_100(x):
if x > 100:
return x
else:
raise ValueError('x is not greater than 100: {}'.format(x))
x_greater_than_100(50)

输出:

ValueError: x is not greater than 100: 50

这里我们可以通过 try 来对错误进行处理,例如:

1
2
3
4
5
6
7
x = 3
while(x<100):
try:
x = x_greater_than_100(x)
except ValueError:
x = x + 10
print(x)

输出:

103

7. 构造函数时灵活使用可变参数和字典参数

在构造函数时,Python 提供了非常灵活的参数传入机制,其中包括可变参数和字典参数,恰当使用可以大大增加代码的灵活性,例如,

对于可变参数:

1
2
3
4
5
def enumerate_args(*args):
for i, arg in enumerate(args):
print('#{}: '.format(i), arg)
enumerate_args(*[1, 2, 3]) ### 传入可迭代对象
enumerate_args(1, 2, 3) ### 传入多个参数

对于字典参数:

1
2
3
4
5
def enumerate_kwargs(**kwargs):
for key, item in kwargs.items():
print('{}: {}'.format(key, item))
enumerate_kwargs(**{'dog': 1, 'cat': 2, 'fish': 3}) ### 传入可迭代对象
enumerate_kwargs(dog=1, cat=2, fish=3) ### 传入多个参数