May 29, 2021 Article blog
Python is Google's main dynamic language, and this style guide lists what you should and shouldn't do when programming with Python
To help you organize your code correctly, we wrote a Vim setup file. For Emacs, the default settings are sufficient.
Many teams use yapf automatic formatting tools to avoid formatting disputes
Use pylint for code
pylint is a tool for finding bugs and code style problems in Python code, and pylint looks for problems that are often captured in non-dynamic languages such as C or C. compilers. Because Python is a dynamic language, some warnings may not be correct, but error warnings should be very rare.
Be able to find errors that are easy to miss, such as spelling errors, calls earlier than declarations, and so on.
pylint is not perfect, and we sometimes need it in order to make better use of the tools
a. Write around it (fit context style)
b. Suppress some warnings
c. Optimization tools
Make sure that pylint is applied to the code
If some warnings are inappropriate, suppress them so that others are not hidden. To suppress warnings, you can set line-level comments:
dict = 'something awful' # Bad Idea... pylint: disable=redefined-builtin
The pylint warning contains the identification name (empty-docstring), and Google's proprietary warning begins with g-.
If the reason for suppressing the warning is not clearly stated in the identity name, add additional comments.
The advantage of suppressing warnings in this way is that we can simply find suppressed warnings and revisit them.
You can get a list of pylint warnings by:
pylint --list-msgs
Get more specific information about a particular message by:
pylint --help-msg=C6409
Priority is given to pylint:disable instead of the old method (pylint: disable-msg) If you want to suppress warnings that are unused due to parameters, you can start del at the beginning of the function and comment on why these unused parameters should be deleted, and it is not enough to simply say "unused":
def viking_cafe_order(spam, beans, eggs=None):
del beans, eggs # Unused by vikings.
return spam + spam + spa
Other ways you can suppress warnings include using ''' as the identification of unused parameters, adding 'unused_' before parameter names, or assigning these parameters to ''. The first two methods affect the way parameters are called, and the last one does not guarantee that the parameters are not actually used.
Use import only when import packages and modules, not in separate classes or functions. (This article has a special surprise for typing_module)
A mechanism for sharing code between one module and another
The namespace management convention is simple and the source of each identity is consistently indicated. For example, x.Obj means that Obj is defined in module x
Module names may conflict, and some module names may be long and inconvenient
Take sound.effects.echo for example:
from sound.effects import echo...echo.EchoFilter(input, output, delay=0.7, atten=4)
Do not use relative references and use the full package name import even within the same package, which helps to avoid inadvertently repeating the import package.
The above rules do not apply from typing module and six.moves module import
Each module is imported from the full path
The ability to avoid module name conflicts and misquotes due to the module search path not matching the author's expectations. Make it easier to find modules.
It's a bit difficult to deploy code because package architectures also need to be assigned, but that's not really a problem for today's deployment mechanism.
All new code comes from the full package name of the import module
The import example should look something like this:
Yes:
# Reference absl.flags in code with the complete name (verbose).
# 在代码中使用完整路径调用absl.flags
import absl.flagsfrom doctor.who import jodie
FLAGS = absl.flags.FLAGS
# Reference flags in code with just the module name (common).
# 在代码中只用包名来调用flags
from absl import flagsfrom doctor.who import jodie
FLAGS = flags.FLAGS
No: (Suppose the file is in doctor/who, jodie.py is also here)
# 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的是哪个包,
# 实际的import操作依赖于受到外部参数控制的sys.path
# 那么哪一个可能的jodie模块是作者希望import的呢?
import jodie
It should not be assumed that the path where the main code is located is included in sys.path, even if it is sometimes possible to work. In the previous example, we should assume that import jodie refers to a third-party package called jodie or jodie in a top-level directory, rather than a jodie.py of the current path
Exception handling is allowed, but use with caution
Exceptions are a means of jumping out of a normal snippet control flow to handle errors or other unusual conditions.
The control flow of normal code will not be affected by error handling code. Exception handling also allows the control flow to skip multiple pieces of code in some cases, such as returning results from N embedded functions at a step instead of forcing continuation of error codes.
It may make it difficult to understand the control flow and make it easier to miss the error of calling library functions.
Exceptions must follow specific conditions:
Do not use assert to fragment the common structure parameter value .assert is used to confirm the correctness of internal calculations and is not used to represent some unexpected events. If the exception is required for subsequent processing, it is handled with a raise statement, for example:
Yes:
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.
Raises:
ConnectionError: If no available port is found.
"""
if minimum < 1024:
# Note that this raising of ValueError is not mentioned in the doc
# string's "Raises:" section because it is not appropriate to
# guarantee this specific behavioral reaction to API misuse.
# 注意抛出ValueError这件事是不在docstring中的Raises中提及, 因为这样并适合保障对于API误用的特殊反馈
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
try:
raise Error()
except Error as error:
pass
Avoid global variables
Variables declared at the module level or as class properties
Sometimes it works
In the import process, it is possible to change the behavior of the module because the global variable has been declared during the first introduction of the module
Avoid global variables
As a technical variable, module - level constants are allowed and encouraged to be used. For example MAX_HOLY_HANDGRENADE_COUNT s 3, constants must consist of capital letters and underscores, see the naming rules below
If necessary, global variables need to be declared at the module level and privatized within the module by adding a preceding variable name. External access to module global variables must pass through the common module-level function, see the naming rules below
Inline local functions or classes are possible when turning off local variables. I nternal class awareness is available. My understanding here is that when an embedded local function or class is within the same closed scope as a local variable,)
Classes can be defined within methods, functions, and classes. F unctions can be defined within a method or function. Inline functions have read - only access to closed scope variables.
Allows you to define tool classes or tool functions that are only available within a very limited scope. V ery ADT-y(?? ???) that meets the requirements of abstract data types, and is typically used to implement decorators
Instances of inline or local classes cannot be pickled, and inline functions or classes cannot be tested directly. Nesting makes external functions longer and harder to read.
These inline/local/internal classes and functions are possible except for some special declarations. A void inline functions or classes except when you need to close a local value. ( The translator understands that this may be except in cases where local variables are enclosed in the same scope). Do not turn a function into an inline indication to avoid access. In this case, place the function at the module level and add the function name to ensure that the test is accessible to the function.
Available in simple cases
List, Dict, and Set derived generators as well as generator expressions provide a concise and efficient way to generate containers and iterators without the need for traditional loops, map(), filter() or lambda expressions
Simply deriving expressions is more concise and clear than other dictionary, list or collection generation methods. Generator expressions can be efficient because building lists is completely avoided.
The derived or generator expressions of the load are difficult to read
When used in simple cases. E ach section (mapping expression, filter expression, etc.) should be done within a line. M ultiple for terms or filter expressions are not allowed. Use loops when the situation becomes complex and appropriate.
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 range(5)
for y in range(5)
if x != y
for z in range(5)
if y != z)
Use them for types that support default iterators and cloud algorithms, such as lists, dictionaries, files, and so on
Container types (such as dictionaries, lists, etc.) define the default iterator and member check operators.
The default iterators and operators are simple and effective and can express actions directly without the need for additional call methods. F unctions that use the default operator are generic. Can be used for any type that supports these operations.
Types cannot be distinguished by method names, for example, has_key () means dictionary, which is of course an advantage.
Use default iterators and operators for supported types such as lists, dictionaries, and files. T he built-in type also defines the iterator method. U se these methods first rather than those that return a list. U nless it is certain that the container will not change during traversal. Do not use Python 2 proprietary iterative methods unless necessary.
Yes:
for key in adict: ...
if key not in adict: ...
if obj in alist: ...
for line in afile: ...
for k, v in adict.items(): ...
for k, v in six.iteritems(adict): ...
No:
for key in adict.keys(): ...
if not adict.has_key(key): ...
for line in afile.readlines(): ...
for k, v in dict.iteritems(): ...
Use the generator when needed
The generator function returns an iterator that generates a value each time the yield statement is executed. After generating a value, the generator function's run is suspended until the next value is needed.
Simplify the code because local variables and control flows are preserved every time they are called, and the generator saves more memory than generating an entire list value at once.
not
It is recommended to use. Use "Yields:" instead of "Returns" in the document string of the generator function:
It's okay when you have a single line of code
Lambda defines anonymous functions within an expression, not in statements. Lambda expressions are often used to define callback functions or operators used by higher-order functions such as map() and filter().
convenient
Harder to read and debug than local functions, anonymity means stack tracking is harder to understand. Expression is limited because the lambda function contains only one expression
Lambda expressions can be used for single lines of code. If the code within the lambda expression exceeds 60-80 characters, it is best to define it as a regular inline function.
For general operations such as multiplication, use the operator module's built-in functions instead of redefining anonymous functions, such as using operator.mul instead of lambda x, y: x s y
Simple cases can be used.
Conditional expressions (also known as ternary operators) are a shorter mechanism for replacing if statements. For example, x is 1 if cond else 2
It is shorter and more convenient than an if statement
Conditions can be harder to read than if statements, and conditional parts can be difficult to locate when expressions are long.
Simple situations can be used. E ach section (true expression, if expression, else expression) must be completed within a row. Use a complete if statement if you are rich with conditional expressions.
Yes:
one_line = 'yes' if predicate(value) else 'no'
slightly_split = ('yes' if predicate(value)
else 'no, nein, nyet')
the_longest_ternary_style_that_can_be_done = (
'yes, true, affirmative, confirmed, correct'
if predicate(value)
else 'no, false, negative, nay')
No:
bad_line_breaking = ('yes' if predicate(value) else
'no')portion_too_long = ('yes'
if some_long_module.some_long_predicate_function(
really_long_variable_name)
else 'no, false, negative, nay')
OK in most cases
At the end of the list of function arguments you can set values for variables, such as def foo (a, b=0): If foo passes in only one argument at the time of the call, then the b variable is set to 0, and if two arguments are passed in at the time of the call, b is given a second argument value.
Usually a function may have a large number of default values, but there are few times when it is necessary to modify them. T he default values provide a simple way to meet the above situations without having to redefine many functions for these rare situations. Because Python does not support overloading methods or functions, the default parameter is a very simple way to "fake overload" behavior.
The default parameters are copied when the module is loaded. T his causes problems when parameters are variable objects, such as lists or dictionaries. If the function modifies these variable objects (such as adding elements to the end of the list), the default value is changed.
When using, be aware of the following warning---- do not use variable objects as default values when defining functions or methods.
Yes:
def foo(a, b=None):
if b is None:
b = []
def foo(a, b: Optional[Sequence] = None):
if b is None:
b = []
def foo(a, b: Sequence = ()): # Empty tuple OK since tuples are immutable 空元组是也不可变的
...
No:
def foo(a, b=[]):
...
def foo(a, b=time.time()): # The time the module was loaded??? 模块被加载的时间???
...
def foo(a, b=FLAGS.my_thing): # sys.argv has not yet been parsed... sys.argv还未被解析
...
def foo(a, b: Mapping = {}): # Could still get passed to unchecked code 仍可传入未检查的代码(此处翻译可能有误)
...
Use properties to access or set data through simple and lightweight visitor and setter methods.
A decorator called to obtain and set a property as standard property access when calculating a lighter weight
For simple property access, reducing explicit get and set methods can improve readability. L azy calculations are allowed. I t is considered a Python way to maintain class interfaces. In terms of performance, properties are allowed to bypass the trivial access methods required when direct access to variables is more reasonable.
Side effects such as operator overloading may be hidden in Python2 that must be inherited from object. Properties can be confusing for subclasses.
Use properties to access or set data in new code that typically has simple and lightweight access and setup methods. The property is decorated @property at the time of creation and participates in the decorator
If the property itself is not rewritten, inheritance with the property may not be clear enough, so it is important to ensure that the access method is accessed indirectly to ensure that the method overload of the subclass is called by the property (using Template Template DP, translator: should be a template method design pattern).
Yes:
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):
"""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
Use the implicit False if statement whenever possible
In the Boolean environment, Python is judged to be False for certain values, and a quick rule of thumb is that all "empty" values are considered False, so the Boolean values for 0, None, and '' are False
Conditional statements that use the Python Boolean type are more readable and more error-prone, and in most cases faster.
This may be a bit strange for C/C? developers
If possible, use implicit False. For example, use if foo: instead of if foo! Here are some of the warnings you should keep in mind:
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 []
Use string methods as much as possible instead of string modules. Use the function call syntax instead of apply. Use list derivation expressions and for loops instead of filter and map when the function argument is an anonymous function within a row
The current Python version provides a generally more tendentious way to build.
We do not use any Python versions that do not support these features and there is no reason not to use the new method.
Yes:
words = foo.split(':')
[x[1] for x in my_list if x[2] == 5]
map(math.sqrt, data) # Ok. No inlined lambda expression. 可以,没有行内的lambda表达式
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)
You can use it
An embedded Python function can refer to variables defined within a closed namespace, but it cannot be copied. V ariable binding is parsed to the use of lexical scopes, i.e. based on static program text. A ny assignment to a name within a block causes Python to use a reference to the name as a local variable, even when preceding assignment. If there is a global declaration, the name is considered a global variable.
An example of using this feature is:
def get_adder(summand1):
"""Returns a function that adds numbers to a given number."""
def adder(summand2):
return summand1 + summand2
return adder
You can often make your code simpler and more elegant, especially with experienced Lisp and Scheme (as well as Haskell and ML and other) programs.
This may lead to confusing bugs such as this PEP-0227- based example.
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 i对于foo来说是局部变量,所以在这里就是bar函数所获取的值
print(i, end='')
bar()
So foo (1, 2, 3) prints 1 2 3 3 instead of 1 2 3 4.
You can use it
Use with caution and wisdom when there are obvious benefits, avoid @staticmethod, and control @classmethod use
A common decorator for functions and method decorators (i.e. markers). A common decorator is a @property used to convert ordinary methods into dynamically calculated properties. However, decorator syntax also allows users to define decorators, especially for some functions my_decorator as follows:
class C(object):
@my_decorator
def method(self):
# method body ...
is equivalent to
class C(object):
def method(self):
# method body ...
method = my_decorator(method)
Be able to gracefully make some kind of conversion of the method, which may reduce some duplicate code and remain constant, etc.
The decorator can operate arbitrarily on the parameters and return values of the function, resulting in very invisible operating behavior. In addition, decorators are executed at the time of import, and the effectiveness of the decorator code can be very difficult to recover.
Use decorators sparingly where there are obvious benefits. D ecorators should follow the same import and naming guidelines as functions. T he documentation for the decorator should clearly declare the function as a decorator function. And to write unit tests for decorator functions.
Avoid the decorator's own external dependencies (e.g. do not rely on files, sockets, database connections, etc.), as these external dependencies may not be available while the decorator is running (in import, possibly from pydoc or other tools). A decorator that is passed in valid parameters and called should (as far as possible) guarantee that it is available under any circumstances.
The decorator is a special "top-level code", see main
Never use @staticmethod unless you have to integrate an API into an existing library and should write a module-level function.
Use @classmethod only when writing a named constructor or a class-specific process that modifies the necessary global state (such as process cache, etc.).
Do not rely on the atomicity of the built-in type
Although Python's built-in data types, such as dictionaries, appear to have atomic operations, there are rare cases where they are non-atomic (for example, if __hash__ or __eq__ implemented as Python methods), they should not depend on the atomicity of these types. Nor should atomic variable assignments be relied upon (because this depends on dictionaries)
Priority is given to the Queue class of the Queue module as a way to communicate data between threads. I n addition, if you use the threading module and its locking primitives, understand the reasonable use of conditional variables to facilitate the use of threading. Condition instead of using lower-level locks.
Try to avoid using it
Python is a very flexible language and offers many novel features such as custom metaclasses, access bytecodes, dynamic compilation, dynamic inheritance, object parent class redefinition, import hacks, reflection (e.g. some applications for getatr(), system built-in modifications, and so on.
These are very powerful language features that make the program more compact
It is tempting to use these new features. B ut it's not absolutely necessary, they're hard to read and hard to understand. I t's also hard to debug code that uses unusual features at the bottom. This may not be the case for the original author, but looking at the code again may be more difficult than longer but more direct code.
Avoid using these features in your code.
Standard libraries and classes that use these features internally are available (for example, abc. ABCMeta, collections.namedtuple, and enum)
Python3 is now available (translator: Python2 is no longer supported), although not every project is ready to use Python3, all code should be Python3 compatible and, where possible, tested in Python3 environments.
Python 3 is a major change for Python, and although existing code is usually written by Python 2.7, there are simple things you can do to make the code more explicit about its intent, allowing the code to run better under Python3 without adjustment.
In considering the code written by Python3 is clearer and clearer, once all dependencies are in place, it can be easier to run in a Python3 environment.
Some people will think that the default model is ugly, import actually does not need features into the module is not common.
from future imports
Encourage the use of from __future__ import statements. All new code should contain the following code, and existing code should be updated to be as compatible as possible:
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
If you're not familiar with these, read these in detail: absolute import, new/divide behavior, and print functions
Do not omit or remove these imports even if they are not in use in the module unless the code is only used for Python 3. It is best to always have import from future in all documents to ensure that it is not forgotten when someone uses it in subsequent edits.
There are other from __future__import statements to look like to use. O ur recommendations do not include unicode_literals because it has no obvious advantage because implicit default encoding conversions have led to their introduction in many places within Python 2.7, and if necessary, most code is best expressed using b'' and ''btyes and unicode strings. (Translator: This translation may not be accurate)
The six, future, or past libraries
When the project needs to support Python 2 and 3, use six, future and paste as needed.
Python3 code can be type commented according to PEP-484 and type checked with type checking tools such as pytype when build.
Type comments can be in the source code or in stub pyi file. C omments should be written in the source code whenever possible. Use pyi files for third parties or extension modules.
Type comments (also known as "type hints") are used for function or method arguments and return values:
def func(a: int) -> List[int]:
You can also declare the type of variable to be declared with a separate comment:
a = SomeFunc() # type: SomeType
Type comments improve code readability and maintainability, type checking turns many operational errors into build errors, and reduces the ability to use overly powerful features.
The type declaration needs to be constantly updated, and for code that is considered valid may report type errors, using type checking may reduce the ability to use too powerful features.
Python type analysis is strongly encouraged when updating code. W hen supplementing and modifying public APIs, include python type declarations and checks by pytype in the build system. S tatic type checking is relatively new for Python, and we acknowledge that some unintended side effects (such as misinterpretation of types) may deny use to some items. In this case, authors are encouraged to appropriately add a link to the BUILD file or within the code with a TODO or type comment described to the bug that is not currently searched.
Don't add a semicolon at the end of a line, and don't combine two lines of statements into one line with a semicolon
The maximum line length is 80 characters
Explicit exceptions beyond 80 characters:
Do not use backslash connections unless you want a context manager with statement that requires three layers or more
Using Python's implicit line joining here parentheses, brackets and braces (implicit line connection methods -- parenthese connections, including (), s, s) can also be added to the outside of the expression if necessary.
Yes:
foo_bar(self, width, height, color='black', design=None, x='foo',
emphasis=None, highlight=0)
if (width == 0 and height == 0 and
color == 'red' and emphasis == 'strong'):
When a string cannot be completed within a line, use parentheses to implicitly connect rows:
x = ('This will build a very long long '
'long long long long long long string')
In the comments, if necessary, place the long URL in its line:
Yes:
# See details at
# http://www.example.com/us/developer/documentation/api/content/v2.0/csv_file_name_extension_full_specification.html
No:
# See details at
# http://www.example.com/us/developer/documentation/api/content/\
# v2.0/csv_file_name_extension_full_specification.html
You can use a backslash to branch when you define a wish statement that has more than three lines or more of an expression. For two-line expressions, use nested with statements:
Yes:
with very_long_first_expression_function() as spam, \
very_long_second_expression_function() as beans, \
third_thing() as eggs:
place_order(eggs, beans, spam, beans)
with very_long_first_expression_function() as spam:
with very_long_second_expression_function() as beans:
place_order(beans, spam)
No:
with VeryLongFirstExpressionFunction() as spam, \
VeryLongSecondExpressionFunction() as beans:
PlaceOrder(eggs, beans, spam, beans)
Note the indentation in the example above, see indentation
In other cases where the line exceeds 80 characters and the yapf auto format tool cannot make the branch meet the requirements when allowed to exceed the 80 character limit.
Parenthesis is used fairly
Although unnecessary, parentheses can be added to tuples. Do not use parentheses in return statements or conditional statements unless they are used for implicit connection lines or to indicate tuples.
Yes:
if foo:
bar()
while x:
x = bar()
if x and y:
bar()
if not x:
bar()
# For a 1 item tuple the ()s are more visually obvious than the comma.
onesie = (foo,)
return foo
return spam, beans
return (spam, beans)
for (x, y) in dict.items(): ...
No:
if (x):
bar()
if not(x):
bar()
return (foo)
Indentation uses 4 spaces
Indent snippets do not use tabs or mix tabs and spaces. If you connect multiple lines, the lines should be aligned vertically, or again 4 space indentations (in this case the first line of brackets should not contain code).
Yes:
# Aligned with opening delimiter
# 和opening delimiter对齐(译者理解是分隔符的入口,例如三种括号,字符串引号等)
foo = long_function_name(var_one, var_two,
var_three, var_four)
meal = (spam,
beans)
# Aligned with opening delimiter in a dictionary
foo = {
long_dictionary_key: value1 +
value2,
...
}
# 4-space hanging indent; nothing on first line
# 缩进4个空格,首行括号后无内容
foo = long_function_name(
var_one, var_two, var_three,
var_four)
meal = (
spam,
beans)
# 4-space hanging indent in a dictionary
foo = {
long_dictionary_key:
long_dictionary_value,
...
}
No:
# Stuff on first line forbidden
# 首行不允许有内容
foo = long_function_name(var_one, var_two,
var_three, var_four)
meal = (spam,
beans)
# 2-space hanging indent forbidden
foo = long_function_name(
var_one, var_two, var_three,
var_four)
# No hanging indent in a dictionary
foo = {
long_dictionary_key:
long_dictionary_value,
...
}
For tail commas in a sequence of elements, it is only recommended to use when the container ends the symbol, or when the last element is not on the same line. The presence of a rear comma is also used as a hint to our Python code auto format tool yapf to automatically adjust container elements to one element per line when the last element appears later.
Yes:
golomb3 = [0, 1, 3]
golomb4 = [
0,
1,
4,
6,
]
No:
golomb4 = [
0,
1,
4,
6
]
There are two rows between top-level definitions (functions or classes). Between the method definition and between the row where the class is located and the first method, there is no empty line after the def line, and a single empty line can be used where you think it is appropriate within the function or method.
Follow standard space and punctuation typography rules.
Don't have extra spaces inside the brackets (), .
Yes:
spam(ham[1], {eggs: 2}, [])
No:
spam( ham[ 1 ], { eggs: 2 }, [ ] )
Don't have spaces before commas, semicolons, colons, but spaces at the back unless they are at the end of the line.
Yes:
if x == 4:
print(x, y)
x, y = y, x
No:
if x == 4 :
print(x , y)
x , y = y , x
There are no spaces before the function calls parentheses.
Yes:
spam(1)
dict['key'] = list[index]
No:
spam (1)
dict ['key'] = list [index]
Don't add spaces at the end of the line.
Spaces are added before and after the assignment, comparison (,, <, >,!,, <>, <, >, in, not in, is, is not), boolean symbol (and, or, not). As appropriate, spaces are added before and after the arithmetic operators (,-,, /,/,, %, , .
Yes:
x == 1
No:
x<1
Don't add spaces before and after the keyword name parameter passes or defines the default parameter value, with one exception: when a type comment exists, when the default parameter value is defined, spaces are added before and after
Yes:
def complex(real, imag=0.0): return Magic(r=real, i=imag)
def complex(real, imag: float = 0.0): return Magic(r=real, i=imag)
No:
def complex(real, imag = 0.0): return Magic(r = real, i = imag)
def complex(real, imag: float=0.0): return Magic(r = real, i = imag)
Do not use spaces for unnecessary alignment, as this can impose unnecessary burdens on maintenance (for: . . . .
Yes:
foo = 1000 # comment
long_name = 2 # comment that should not be aligned
dictionary = {
'foo': 1,
'long_name': 2,
}
No:
foo = 1000 # comment
long_name = 2 # comment that should not be aligned
dictionary = {
'foo' : 1,
'long_name': 2,
}
Most .py files don't need to start with the line. According to PEP-394, the main file of the program should start with s!/usr/bin/python2 or s!/usr/bin/python3
This line is used to help the kernel find the Python interpreter, but is ignored by Python when importing modules/only necessary to write in files that will be run directly.
Make sure to use the correct modules, functions, methods of document strings and in-line comments.
Python uses document strings to generate documents for code. A document string is the first statement of a package, module, class, or function. T hese strings can be automatically extracted by __doc__ member methods and used by pydoc. ( Try running pydoc on your module to see what it is). The document string uses triple double quotes "" (according to PEP-257). The document string should be organized as follows: a line summary (or the entire document string has only one line) and end with a period, a hello or an exclamation point. T his is followed by an empty line, followed by a document string, which corresponds to the first quotation marks on the first line. More specific format specifications are as follows.
Each file should contain a license template. Select the appropriate license template for the project (e.g. Apache 2.0, BSD, LGPL, GPL)
The document should begin with a document string and describe the contents of the module and how to use it.
"""A one line summary of the module or program, terminated by a period.
Leave one blank line. The rest of this docstring should contain an
overall description of the module or program. Optionally, it may also
contain a brief description of exported classes and functions and/or usage
examples.
Typical usage example:
foo = ClassFoo()
bar = foo.FunctionBar()
"""
In this section, "function" refers to a method, function, or generator.
The function should have a document string unless all of the following conditions are met:
The document string should contain enough information to call the function without having to read the function code. T he document string should be a narrative ("Fetches rows from a Bigtable.") i nstead of imperative ("Fetch rows from a Bigtable.")," except for @property (which should use the same style as attribute), document strings should describe the call syntax of the function and its meaning, not the implementation. Where there is more skill, it is more appropriate to use comments in code.
Methods that overwrite the base class can have simple document strings that indicate to the reader that the method is overwritten, such as ""See base class."" T his is because there is no need to repeat documents that already exist in the document string of the base class in many places. However, if the overwriting method behavior is actually inconsistent with the overwritten method, or if details need to be provided (such as additional side effects shown in the document), the document string of the overwriting method should provide at least these differences.
Different aspects of a function should be written to the document in specific corresponding sections, which are as follows. Each section starts with a line ending in a colon, and each section, with the exception of the first line, should be indented with two or four spaces and consistent throughout the document (the translator recommends four spaces to maintain overall consistency). If the function name and signature are sufficient to give enough information and can be described by just one line of document strings, these sections can be ignored.
Args:
List the names of each parameter. T he name should be followed by a colon and a space followed by a description. I f the description is too long to complete within a single line of 80 characters. Then the branch is indented into 2 or 4 spaces and is consistent with the full document (the translator also recommends 4 spaces)
The description should contain the type required by the argument, if the code does not contain type comments. If the function allows for a list of variable-length arguments or a bar (any keyword argument), it should be listed as .foo and s bar.
Returns: (or Yeelds for generators:)
Describes the type and meaning of the return value. I f the function returns at least None, this section does not need to be. I f the document string starts with Returns or Yeelds (for example, "Returns row from Bigtable as a tuple of of strings.") Or if the first sentence is sufficient to describe the return value, this section can be ignored.
Raises:
List all interface - related exceptions. E xceptions thrown in violation of document requirements should not be listed. (Because this paradoxically makes violations of interface requirements part of the interface)
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
"""Fetches rows from a Bigtable.
Retrieves rows pertaining to the given keys from the Table instance
represented by big_table. Silly things may happen if
other_silly_variable is not None.
Args:
big_table: An open Bigtable Table instance.
keys: A sequence of strings representing the key of each table row
to fetch.
other_silly_variable: Another optional variable, that has a much
longer name than the other args, and which does nothing.
Returns:
A dict mapping keys to the corresponding table row data
fetched. Each row is represented as a tuple of strings. For
example:
{'Serak': ('Rigel VII', 'Preparer'),
'Zim': ('Irk', 'Invader'),
'Lrrr': ('Omicron Persei 8', 'Emperor')}
If a key from the keys argument is missing from the dictionary,
then that row was not found in the table.
Raises:
IOError: An error occurred accessing the bigtable.Table object.
"""
The next line of the class definition should be the document string that describes the class. If the class has public properties, it should be noted in the Attributes section of the document string and is consistent with the Args section of the function.
class SampleClass(object):
"""Summary of class here.
Longer class information....
Longer class information....
Attributes:
likes_spam: A boolean indicating if we like SPAM or not.
eggs: An integer count of the eggs we have laid.
"""
def __init__(self, likes_spam=False):
"""Inits SampleClass with blah."""
self.likes_spam = likes_spam
self.eggs = 0
def public_method(self):
"""Performs operation blah."""
The last place to comment in code is the technical part of the code. I f you're going to reveal the code in the next code review. C omments should be added now. C omment a few lines before the complex operation begins. Comment at the end of the line for code that is not clear enough.
# We use a weighted dictionary search to find out where i is in
# the array. We extrapolate position based on the largest num
# in the array and the array size and then do binary search to
# get the exact number.
if i & (i-1) == 0: # True if i is 0 or a power of 2.
To improve readability, line comments should be at least 2 spaces after the code and start the comment section with at least 1 space after .
Also, don't describe the code, assuming that the person reading the code is more proficient in Python than you are (he just doesn't know what you're trying to do).
Pay attention to punctuation, spelling and grammar, and well written notes are better read than poorly written.
Comments should be as readable as narrative text and have appropriate case and punctuation. I n many cases, complete sentences are more readable than broken sentences. Shorter comments, such as comments at the end of a line, can sometimes be less formal, but should be styled throughout.
Although code reviewers point out that using commas where semicolons should be used is frustrating, it is important to maintain the source code at a highly readable level. Proper punctuation, spelling and grammar can help achieve this goal.
If the class does not inherit from other base classes, it is clear that it is inherited from the object, even if the inline class is.
Yes:
class SampleClass(object):
pass
class OuterClass(object):
class InnerClass(object):
pass
class ChildClass(ParentClass):
"""Explicitly inherits from another class already."""
No:
class SampleClass:
pass
class OuterClass:
class InnerClass:
pass
Inheriting from the object class ensures that the property runs correctly in Python2 and protects the code from potential incompatibility under Python3. This also defines the implementation of default special methods such as __new__, __init__, __delattr__, __getattribute__, __setattr__, __hash__, __repr__, and __str__.
Format strings with format or %, even if the parameters are string objects, consider using plus or % and format.
Yes:
x = a + b
x = '%s, %s!' % (imperative, expletive)
x = '{}, {}'.format(first, second)
x = 'name: %s; score: %d' % (name, n)
x = 'name: {}; score: {}'.format(name, n)
x = f'name: {name}; score: {n}' # Python 3.6+
No:
employee_table = '<table>'
for last_name, first_name in employee_list:
employee_table += '<tr><td>%s, %s</td></tr>' % (last_name, first_name)
employee_table += '</table>'
Avoid adding strings within a loop using the plus and minus operators because strings are immutable objects. T his causes unnecessary temporary variables to cause run times to grow in quads rather than linearly. E ach string should be recorded in a list and ''.join should be used to connect the list at the end of the loop (or to write each substring to io. BytesIO cache)
Yes:
items = ['<table>']
for last_name, first_name in employee_list:
items.append('<tr><td>%s, %s</td></tr>' % (last_name, first_name))
items.append('</table>')
employee_table = ''.join(items)
No:
employee_table = '<table>'
for last_name, first_name in employee_list:
employee_table += '<tr><td>%s, %s</td></tr>' % (last_name, first_name)
employee_table += '</table>'
In the same file, string quotation marks should be consistent, select '' or 'and do not change. You can change when you need to avoid escapes.
Yes:
Python('Why are you hiding your eyes?')
Gollum("I'm scared of lint errors.")
Narrator('"Good!" thought a happy Python reviewer.')
No:
Python("Why are you hiding your eyes?")
Gollum('The lint. It burns. It burns us.')
Gollum("Always the great lint. Watching. Watching.")
Multi-line string Multi-line strings take precedence over "" instead of '', and three single quotes .docstring must be used no matter how much '' is used for all non-document strings and for normal strings
Multiple lines of strings are indented in a different way than the rest of the code. If you need to avoid inserting extra spaces into the string, either use a single-line string connection or a multi-line string with textwarp.dedent() to remove the starting space for each line.
No:
long_string = """This is pretty ugly.
Don't do this.
"""
Yes:
long_string = """This is fine if your use case can accept
extraneous leading spaces."""
long_string = ("And this is fine if you can not accept\n" +
"extraneous leading spaces.")
long_string = ("And this too is fine if you can not accept\n"
"extraneous leading spaces.")
import textwrap
long_string = textwrap.dedent("""\
This is also fine, because textwrap.dedent()
will collapse common leading spaces in each line.""")