蟒蛇军火库

this

Python 之禅:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> import this
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!

decimal

假如你的程序需要精确的浮点数计算,请考虑使用 decimal.Decimal 对象来代替普通浮点数:

1
2
3
>>> from decimal import Decimal
>>> Decimal('0.1') + Decimal('0.2')
Decimal('0.3')

locale

如果不指定 encoding,Python 将通过 locale 模块获取系统偏好的编码格式:

1
2
3
>>> import locale
>>> locale.getpreferredencoding()
'UTF-8'

dis

dis 的全称是 Disassembler for Python Bytecode,在解释器真正运行 Python 代码前,其实仍然有一个类似编译的加速过程:将代码编译为二进制的字节码。我们没法直接读取字节码,但利用内置的 dis 模块,可以将它们反汇编成人类可读的内容:

1
2
3
def do_something(delta_seconds):
if delta_seconds < 11 * 24 * 3600:
return
1
2
3
4
5
6
7
8
9
10
11
>>> import dis
>>> dis.dis(do_something)
2 0 LOAD_FAST 0 (delta_seconds)
2 LOAD_CONST 1 (950400)
4 COMPARE_OP 0 (<)
6 POP_JUMP_IF_FALSE 12

3 8 LOAD_CONST 0 (None)
10 RETURN_VALUE
>> 12 LOAD_CONST 0 (None)
14 RETURN_VALUE

第 4 行表示 Python 解释器在将源码编译成字节码时,会主动计算 11 * 24 * 3600 表达式的结果,并用 950400 替换它。也就是说,无论你调用 do_something 多少次,其中的算式都只会在编译期被执行 1 次。

解释器除了会预计算数值表达式外,还会对字符串、列表执行类似的操作。

textwrap

在已经有缩进层级的代码里,插入多行字符串字面量,为了让字符串不要包含当前缩进里的空格,我们必须把代码写成这样:

1
2
3
4
5
6
def main():
if user.is_active:
message = """Welcome, today's movie list:
- Jaw (1975)
- The Shining (1980)
- Saw (2004)"""

可以用标准库 textwrap 来解决这个问题,dedent 方法会删除字符串左侧的空白缩进。使用它来处理多行字符串以后,整段代码的缩进视觉效果就能保持正常了:

1
2
3
4
5
6
7
8
9
from textwrap import dedent

def main():
if user.is_active:
message = dedent("""\
Welcome, today's movie list:
- Jaw (1975)
- The Shining (1980)
- Saw (2004)""")

timeit

利用 Python 的内置模块 timeit,我们可以非常方便地测试代码的执行效率:

1
2
3
4
5
6
7
8
9
10
11
12
13
WORDS = ['Hello', 'string', 'performance', 'test'] * 25

def str_cat():
s = ''
for word in WORDS:
s += word
return s

def str_join():
l = []
for word in WORDS:
l.append(words)
return ''.join(l)

然后,导入 timeit 模块,定义性能测试:

1
2
3
4
5
6
7
8
import timeit

# 默认执行 100 万次
cat_spent = timeit.timeit(setup='from __main__ import str_cat', smt='str_cat()')
print('cat_spent: ', cat_spent)

join_spent = timeit.timeit(setup='from __main__ import str_join', smt='str_join()')
print('join_spent:', join_spent)
1
2
cat_spent:  7.844882188
join_spent: 7.310863505

contextlib

如果你在真实项目中要忽略某类异常,可以直接使用标准库模块 contextlib 里的 suppress 函数:

1
2
3
4
from contextlib import suppress

with suppress(FileNotFoundError):
os.remove('somefile.tmp')

pydantic

在数据校验方面,pydantic 模块是一个不错的选择:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def input_a_number():
while True:
number = input('Please input a number (0-100):')

if not number:
print('Input can not be empty!')
continue
if not number.isdigit():
print('Your input is not a valid number!')
continue
if not (0 <= int(number) <= 100):
print('Please input a number between 0 and 100!')
continue

number = int(number)
break

print(f'Your number is {number}')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pydantic import BaseModel, conint, ValidationError

class NumberInput(BaseModel):
number: conint(ge=0, le=100)

def input_a_number():
while True:
number = input('Please input a number (0-100):')

try:
number_input = NumberInput(number=number)
except ValidationError as e:
print(e)
continue

number = number_input.number
break

print(f'Your number is {number}')

radon

在 Python 中,你可以通过 radon 工具计算一个函数的圈复杂度:

1
2
3
4
5
6
7
8
9
10
11
12
def rank(rating):
rating_num = float(rating)
if rating_num >= 8.5:
return 'S'
elif rating_num >= 8:
return 'A'
elif rating_num >= 7:
return 'B'
elif rating_num >= 6:
return 'C'
else:
return 'D'
1
2
3
$ radon cc complex_func.py -s
complex_func.py
F 1:0 rank - A (5)