Python Language Rules
参考:Google Python Style Guide
Imports:
在使用import时应该只针对包和模块, 这样可以区分x.Obj 中, x是模块而Obj是对象。
- Use import x for importing packages and modules.
- Use from x import y where x is the package prefix and y is the module name with no prefix.
- Use from x import y as z if two modules named y are to be imported or if y is an inconveniently long name.
- Use import y as z only when z is a standard abbreviation (e.g., np for numpy).
注意:不要使用相对路径的import,即使模块属于同一个包中,也要使用包的全名,这样可以避免无意的导入一个包两次。
Packages:
导入每一个模块都要用模块的全部路径名。
正确:
# Reference absl.flags in code with the complete name (verbose).
import absl.flags
from doctor.who import jodie
FLAGS = absl.flags.FLAGS
# Reference flags in code with just the module name (common).
from absl import flags
from doctor.who import jodie
FLAGS = flags.FLAGS
错误:
假定这个文件和jodie .py 都在doctor/who/目录下
# Unclear what module the author wanted and what will be imported. The actual
# import behavior depends on external factors controlling sys.path.
# Which possible jodie module did the author intend to import?
import jodie
Exceptions:
异常被允许但应该仔细使用。
- raise应该这样使用,
raise MyError('Error message')
orraise MyError()
,而不应该这样使用raise MyError, 'Error message'
- 使用内置的异常类,如
ValueError
, 如果要判断一个异常的发生要使用raise
, 不要使用assert
Yes:
def connect_to_next_port(self, minimum):
"""Connects to the next available port.
Args:
minimum: A port value greater or equal to 1024.
Raises:
ValueError: If the minimum port specified is less than 1024.
ConnectionError: If no available port is found.
Returns:
The new minimum port.
"""
if minimum < 1024:
raise ValueError('Minimum port must be at least 1024, not %d.' % (minimum,))
port = self._find_next_open_port(minimum)
if not port:
raise ConnectionError('Could not connect to service on %d or higher.' % (minimum,))
assert port >= minimum, 'Unexpected port %d when minimum was %d.' % (port, minimum)
return port
```
No:
def connect_to_next_port(self, minimum):
"""Connects to the next available port.
Args:
minimum: A port value greater or equal to 1024.
Returns:
The new minimum port.
"""
assert minimum >= 1024, 'Minimum port must be at least 1024.'
port = self._find_next_open_port(minimum)
assert port is not None
return port
- 当异常继承自库或包时,异常的名字应该以
Error
结尾,而不应该foo.FooError
- 最小化
try/except
块中的代码量。尝试的主体越大,异常被一行您不希望引发异常的代码引发的可能性就越大。在这些情况下,try/except块隐藏了一个真正的错误。 - 无论try块中是否有异常发生,都可以使用
finally
语句执行代码, 着经常是有用的对于cleanup 或者关闭文件 - 当捕捉一个异常时用
as
try:
raise Error()
except Error as error:
pass
Global variables:
避免使用全局变量,因为在模块import时可能会改变模块的行为。允许使用模块级的常量, 如MAX_HOLY_HANDGRENADE_COUNT = 3
。如果需要,应该在模块级别声明全局,并通过在名称前面加上一个_u
使其成为模块内部。外部访问必须通过公共模块级功能完成。
Comprehensions & Generator Expressions:
可以用于简单的案例。每个部分必须适合一行:mapping expression, for clause, filter expression。不允许使用多个for子句或ilter expression。当事情变得更复杂时,请使用循环。
Yes:
result = [mapping_expr for value in iterable if filter_expr]
result = [{'key': value} for value in iterable
if a_long_filter_expression(value)]
result = [complicated_transform(x)
for x in iterable if predicate(x)]
descriptive_name = [
transform({'key': key, 'value': value}, color='black')
for key, value in generate_iterable(some_input)
if complicated_condition_is_met(key, value)
]
result = []
for x in range(10):
for y in range(5):
if x * y > 10:
result.append((x, y))
return {x: complicated_transform(x)
for x in long_generator_function(parameter)
if x is not None}
squares_generator = (x**2 for x in range(10))
unique_names = {user.name for user in users if user is not None}
eat(jelly_bean for jelly_bean in jelly_beans
if jelly_bean.color == 'black')
No:
result = [complicated_transform(
x, some_argument=x+1)
for x in iterable if predicate(x)]
result = [(x, y) for x in range(10) for y in range(5) if x * y > 10]
return ((x, y, z)
for x in xrange(5)
for y in xrange(5)
if x != y
for z in xrange(5)
if y != z)
Default Iterators and Operators:
使用默认的iterators(如字典, 列表)或operators(如in或not in)\
Yes: for key in adict: ...
if key not in adict: ...
if obj in alist: ...
for line in afile: ...
for k, v in adict.items(): ...
No: for key in adict.keys(): ...
if not adict.has_key(key): ...
for line in afile.readlines(): ...
Lambda Functions:
只在一行时使用,在字符超过60-80时使用函数, 对于常用操作,如乘法, 使用operator
中定义的函数,如使用operator.mul
而不是lambda x, y: x * y
.
Conditional Expressions:
只在一行时使用,x = 1 if cond else 2
Default Argument Values:
默认值在多数情况下都可以,但默认值不能是可变对象,因为如果函数中对该元素进行了更改,那么默认值也会随之更改。
Yes: def foo(a, b=None):
if b is None:
b = []
Yes: def foo(a, b: Optional[Sequence] = None):
if b is None:
b = []
Yes: def foo(a, b: Sequence = ()): # Empty tuple OK since tuples are immutable
...
No: def foo(a, b=[]):
...
No: def foo(a, b=time.time()): # The time the module was loaded???
...
No: def foo(a, b=FLAGS.my_thing): # sys.argv has not yet been parsed...
...
Properties:
当对类中简单属性进行读或设置时,可以使用property
特性,增加可读性。
Yes: import math
class Square(object):
"""A square with two properties: a writable area and a read-only perimeter.
To use:
>>> sq = Square(3)
>>> sq.area
9
>>> sq.perimeter
12
>>> sq.area = 16
>>> sq.side
4
>>> sq.perimeter
16
"""
def __init__(self, side):
self.side = side
@property
def area(self):
"""Gets or sets the area of the square."""
return self._get_area()
@area.setter
def area(self, area):
return self._set_area(area)
def _get_area(self):
"""Indirect accessor to calculate the 'area' property."""
return self.side ** 2
def _set_area(self, area):
"""Indirect setter to set the 'area' property."""
self.side = math.sqrt(area)
@property
def perimeter(self):
return self.side * 4
True/False evaluations:
对于可能的应用场景,使用隐含的False, 因为Python中0, None, [], {}, ''
都为False
。
- 当比较
None
时,不要使用==, !=
, 而要使用is , not is
- 当使用
if x
, 意味着if x is not None
. - 不要使用
==
比较布尔量和False
, 应该使用if not x
, 如果要区分False
和None
使用if not x and x is not None
- 对于序列(string, list, tuple), 使用
if seq
if not seq
来判断序列是否为空,而不应该使用if len(seq)
if not len(seq)
'0'
字符的布尔量为True
- 已知结果为整数了,要跟0进行比较
Yes: if not users:
print('no users')
if foo == 0:
self.handle_zero()
if i % 10 == 0:
self.handle_multiple_of_ten()
def f(x=None):
if x is None:
x = []
No: if len(users) == 0:
print('no users')
if foo is not None and not foo:
self.handle_zero()
if not i % 10:
self.handle_multiple_of_ten()
def f(x=None):
x = x or []
Deprecated Language Features:
使用字符串方法而不是使用string
模块,使用函数调用语法而不是使用apply
, 使用list推导或for
循环而不是使用含有filter
map
的lambda
表达式,使用for
循环而不是使用reduce
Yes: words = foo.split(':')
[x[1] for x in my_list if x[2] == 5]
map(math.sqrt, data) # Ok. No inlined lambda expression.
fn(*args, **kwargs)
No: words = string.split(foo, ':')
map(lambda x: x[1], filter(lambda x: x[2] == 5, my_list))
apply(fn, args, kwargs)
Lexical Scoping(作用域):
嵌套的python函数可以引用封闭函数中定义的变量,但不能分配给它们。对块中某个名称的任何赋值都将导致python将对该名称的所有引用视为局部变量,即使在赋值之前使用。如果发生全局声明,则将名称视为全局变量。
i = 4
def foo(x):
def bar():
print(i, end='')
# ...
# A bunch of code here
# ...
for i in x: # Ah, i *is* local to foo, so this is what bar sees
print(i, end='')
bar()
Sofoo([1, 2, 3])
will print 1 2 3 3
, not 1 2 3 4
.
Function and Method Decorators:
在装饰器有明显优势时使用装饰器,避免使用staticmethod
有限制的使用classmethod
, 装饰器是在执行import
时执行的.
只有在编写命名构造函数或类特定的例程(修改必要的全局状态,如进程范围的缓存)时才使用@classmethod。
除非强制使用@staticmethod以便与现有库中定义的API集成,否则不要使用。改为编写模块级函数。
Power Features:
避免使用,如custom metaclasses, access to bytecode, on-the-fly compilation, dynamic inheritance, object reparenting, import hacks, reflection (e.g. some uses of getattr()), modification of system internals, etc.
标准库或类中使用这些特性是可以的,如abc.ABCMeta
, collections.namedtuple
, dataclasses
, and enum