Google Python Style Guide

本文详细介绍了Google的Python编程风格指南,包括代码结构、命名规范、注释与文档、字符串处理、文件与套接字操作等核心内容,旨在帮助开发者编写高质量、可维护的Python代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Google Python Style Guide

Imports

使用 from x import y as z

  • 当有两个 modules 的名字为 y 时
  • 如果 y 是一个很不方便的长名字

使用 import y as z

  • 仅当 z 为 y 的标准缩写时(e.g, np for numpy)

module sound.effects.echo

from sound.effects import echo

Full package name

  • !!#0000ff Do not use relative names in imports. Even if the module is in the same package, use the full package name. This helps prevent unintentionally importing a package twice.!!

not got this problem yet

Global variables

避免使用全局变量

  • !!#0000ff 有潜在可能在导入期间更改模块行为, 因为在首次导入模块时会对全局变量进行分配!!

not got this problem yet

鼓励使用全局常量

module-level

不得不用全局变量

  • 定义一个 module-level 的全局变量, 同时定义一个 module-level 的函数来对这个变量进行操作, 不直接对这个变量进行操作, 将其视为模块的内部变量

Comprehensions & Generator Expressions

可用于简单的 cases

  • Simple comprehensions 比字典, 列表以及集合的创建技术更清晰简单
  • 生成器表达式是很高效的, 因为它们可以避免创建一整个列表

每一部分占一行

# No:
# For `complicated_transform`, it used two lines
result = [complicated_transform(
		      x, some_argument=x+1)
	      for x in iterable if predicate(x)]

不允许使用多重的 for 以及 expressions

# No:
# Not permitted for multiple `for`, even though they are in different lines.
result = [(x, y) for x in range(10) for y in range(5) if x * y > 10]

Default Iterators and Operators

  • Prefer these methods to methods that return lists
  • except that you should not mutate a container while iterating over it

!!#0000ff Why not?!!

# Yes:
for k, v in dict.iteritems(): ...
# No:
for line in afile.readlines(): ...

Lambda Functions

  • Okay for use them for one-lines
  • 对于普遍的操作, 如同乘法, 使用 operate 模块而不是 lambda 表达式
# prefer:
operator.mul
# to:
lambda x, y: x * y

!!#0000ff Have not used operator!!

Conditional Expressions

  • Okay for one-liners
# e.g.
x = 1 if cond else 2

Default Argument Values

在模型加载时默认参数也会同时被 evaluated

  • This may cause problems if the argument is a mutable object such as a list or a dictionary. If the function modifies the object (e.g., by appending an item to a list), the default value is modified
No:
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...
         ...

Properties

Main for class, I have not used it.

True/False evaluations

如果可能的话使用隐式 false

  • 所有"空"值都被视为 false
0, None, [], {}, ''

caveats:

  • use if foo: rather than if foo != []:

!!#0000ff Why? - Use the “implicit” false if at all possible!!

  • Never use == or != to compare singletons like None. Use is or is not

!!#0000ff Why?!!

  • Beware of writing if x: when you really mean if x is not None:

  • Never compare a boolean variable to False using ==. Use if not x:

    • If you need to distinguish False from None then chain the expressions, such as if not x and x is not None:
  • if seq: and if not seq: are preferable to if len(seq): and if not len(seq):

  • You may compare a value which is known to be an integer (and is not the result of len()) against the integer 0

    • Not accidentally handling None as 0
  • 对于数值比较, 使用 == 而不是 if not

Deprecated Language Features

  • 使用字符串方法而不是字符串模块

Lexical Scoping

  • 嵌套的Python函数可以引用封闭函数中定义的变量, 但不能赋值给它们
  • 如果引用了全局变量, 则它可见全局变量, 如果局部变量对其更改, 则它引用局部变量
  • !!#0000ff Example:!!
i = 4 
def foo(x):
    def bar(i=i):
        print(i, end=' ')

    
    #i = 5
    for j in x:
        print(j, end=' ')
    bar()


foo([1, 2, 3])
# results:
1 2 3 4 
i = 4 
def foo(x):
    def bar(i=i):
        print(i, end=' ')

    
    i = 5
    for j in x:
        print(j, end=' ')
    bar()


foo([1, 2, 3])
# Error:
UnboundLocalError: local variable 'i' referenced before assignment

!!#0000ff Why?!!

Type Annotated Code

  • 类型注释(或"类型提示")用于函数或方法参数和返回值
def func(a: int) -> List[int]:
  • 可以使用特殊注释声明变量的类型
a = SomeFunc()  # type: SomeType

Give it a try


Semicolons

  • 不要使用分号终止一行
  • 不要使用分号将两个语句放在同一行

Line length

最大行长度为 80

例外

  • Long import statements
  • URLs, pathnames, or long flags in comments
  • 不含空格的长字符串, 不便于分割跨行
  • Pylint disable comments. (e.g.: # pylint: disable=invalid-name)

Attention

  • 不要使用反斜杠继续写行, 除非 with statements requiring three or more context managers

skill

  • 善于使用 python 中括号的隐式连接
  • 在必要的情况下可以为表达式添加一个额外的括号
  • 当文字字符串不适合单行时, 使用括号进行隐式行连接
x = ('This will build a very long long '
     'long long long long long long string')
  • 反斜杠用于 with 语句

Parentheses

保守使用括弧

  • 尽管不需要, 最好在元组周围加上括弧
  • 不要在返回语句或者条件语句里使用括弧
    • 除非需要隐含的 line continuation 或者特指 tuple

Indentation

用 4 个空格缩进代码块

implied line continuation

  • 垂直对齐包裹元素
  • 使用 4 个空格的缩进, 此时, 第一行的左括号应该没有内容
# Yes:
foo = long_function_name(var_one, var_two,
                                           var_three, var_four)

foo = {
           long_dictionary_key: value1 +
                                value2,
           ...
}

foo = long_function_name(
           var_one, var_two, var_three,
           var_four)

foo = {
           long_dictionary_key:
               long_dictionary_value,
           ...
}

Trailing commas in sequences of items

# Yes:
# , after 6 is important
golomb4 = [
    0,
    1,
    4,
    6,
]

Blank Lines

  • 在函数或者类定义之前空两行
  • 在方法定义以及类和第一条方法之间空一行
  • def 之后不要空行
  • 在方法和函数中合适的地方空一行

Whitespace

标点符号

  • 在标定符号之间使用空格
  • 小中大括号周围没有空格
# Yes: 
spam(ham[1], {eggs: 2}, [])
# No:
No:  spam( ham[ 1 ], { eggs: 2 }, [ ] )
  • 逗号, 分号或冒号之前不要使用空格
  • 除了行尾, 在逗号, 分号或冒号之后使用空格

参数

  • 在参数和切片的括号前不要使用空格
# Yes:
spam(1)
dict['key'] = list[index]
# No:
spam (1)
dict ['key'] = list [index]

二进制运算符

  • 在二进制运算符两侧加上空格
=, ==, <, >, !=, <>, <=, >=, in, not in, is, is not, and, or, not

参数

  • 传递关键字参数时,切勿在=符号周围使用空格
  • 仅在类型注释的默认参数的=周围加上空格
# 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: float=0.0): 
	return Magic(r = real, i = imag)

对齐

  • 不要使用空格来垂直对齐
# Yes:
foo = 1000  # comment
long_name = 2  # comment that should not be aligned
# No:
foo       = 1000  # comment
long_name = 2     # comment that should not be aligned

Comments and Docstrings

确保编写正确的模块, 函数和方法的文档和行内注释

docstrings

  • docstring 是一个字符串, 它是包, 模块, 类或函数中的第一个语句
  • docstrings 使用 """ 的格式
  • 文档字符串应组织为一个汇总行(一个物理行),以句点,问号或感叹号结尾,后跟一个空行,后跟文档字符串的其余部分,从与第一个引号相同的光标位置开始 第一行

modules

  • 每个文件都应包含许可证样板。 为项目使用的许可选择合适的样板

!!#0000ff What’s this!!

Functions and Methods

  • 每一个函数都必须有一个文档, 除非它符合以下所有标准

    • 外部不可见

    !!#0000ff What’s this!!

    • 非常短
    • 显而易见
  • docstring 应该是描述性的而不是命令式的

  • docstring 应该描述函数的调用语法及其语义, 而不是它的实现

  • 对于棘手的代码, 代码旁带有注释比使用 docstring 更合适

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.
    """

Classes

  • 类应该在描述类的类定义下面有一个 docstring. 如果类具有公共属性, 则应在 Attributes 部分中记录它们, 并遵循与函数的 Args 部分相同的格式
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."""

Block and Inline Comments

  • 在棘手的代码处应该添加注释
  • 在棘手的代码开始之前应该有几行注释
  • 如果代码表达的内容不够明显会在行尾得到注释
  • 为了提高易读性, 这些注释应该距离代码至少 2 个空格
# 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.
  • 注意, 不要描述代码干了什么什么, 我们要假设看代码的人比你更懂 Python
# BAD COMMENT: Now go through the b array and make sure whenever i occurs
# the next element is i+1

Punctuation, Spelling and Grammar

  • 注意标点符号, 拼写和语法; 阅读写得好的评论比写得不好的评论更容易
  • 在很多情况下, 完整的句子比句子片段更具可读性
  • 较短的注释比如行尾的注释可以不用那么正式

!!#ff0000 you should be consistent with your style!!

Classes

  • 如果一个类不从其它基类继承, 明确地从 object 中继承. This also applies to nested classes

!!#0000ff nested classes?!!

  • 类可以从 object 继承 __new__, __init__, __delattr__, __getattribute__, __setattr__, __hash__, __repr__, and __str__.
# 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

Strings

  • 使用 format 方法或者 % 操作符来格式化字符串, 即使参数都是 string
  • 使用你的最佳判断来决定使用 + 或者 %(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: x = '%s%s' % (a, b)  # use + in this case
    x = '{}{}'.format(a, b)  # use + in this case
    x = first + ', ' + second
    x = 'name: ' + name + '; score: ' + str(n)
  • 避免使用 + 或者 += 操作在循环中累加一个字符串. 因为字符串是不可变的, 这会导致产生没有必要的临时对象以及非线性的执行时间. 将每一个 substring 添加到一个 list 中, 然后在循环结束时 ''.join() 这个 list. 或者也可以将每一个 substring 写入 io.BytesIO buffer
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>'
  • 在文件中使用 " 或者 ' 要保持一直, 为了避免使用转义字符 \ 可以使用其它引用字符
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.")
  • 对于多行的字符串首选 """ 而不是 '''

  • 如果使用 ' 在常规 string 上, 也可以选用 ''' 于多行的字符串上

  • Docstrings 必须使用 """

  • 使用隐式的行连接更加清晰, 使用 """ 不能很好地对应行缩进

  Yes:
  print("This is much nicer.\n"
        "Do it this way.\n")
  No:
    print("""This is pretty ugly.
Don't do this.
""")

Files and Sockets

当 files 或者 sockets 使用完成时, 显示地关闭它们

不关闭的缺点

  • 它们可能消耗有限的系统资源,例如文件描述符
  • 保持文件打开可能会阻止其它操作, 例如移动或删除
  • 如果不关闭文件或者 sockets, 可能会错误地往里面写入东西或者读东西

文件对象

当文件对象被破坏时文件和套接字自动关闭, 但是将文件对象的生命周期与文件状态联系起来是不好的做法

  • 无法保证运行时何时实际运行文件的析构函数。 不同的Python实现使用不同的内存管理技术,例如延迟垃圾收集,这可能会随意且无限地增加对象的生命周期
  • Unexpected references to the file, e.g. in globals or exception tracebacks, may keep it around longer than intended

!!#0000ff What’s this?!!

with 很好用

with open("hello.txt") as hello_file:
    for line in hello_file:
        print(line)
  • 对于不支持 with 的 file-like 的对象, 使用 contextlib.closing()
import contextlib

with contextlib.closing(urllib.urlopen("http://www.python.org/")) as front_page:
    for line in front_page:
        print(line)

TODO Comments

  • 当代码是暂时的, 短期的解决方案, 或者足够好但不是最完美时, 加上 TODO
# TODO(kl@gmail.com): Use a "*" here for string repetition.
# TODO(Zeke) Change this to use relations.
  • have a consistent TODO format

Imports formatting

  • Imports 应该在不同行
import os
import sys
  • Imports 通常在文件顶, 在模块注释以及文件字符串之后以及模块全局变量和常量之前

  • Imports 应该从最通用到不通用进行分组

  • Code repository sub-package imports

from otherproject.ai import mind

!!#0000ff ?!!

  • 在每个分组中, imports 应该按字典顺序排序, 忽略大小写, 根据每一个模块的完整路径
  • 可以选择性地在每 Imports 部分之间加入一个空行
import collections
import queue
import sys

from absl import app
from absl import flags
import bs4
import cryptography
import tensorflow as tf

from book.genres import scifi
from myproject.backend.hgwells import time_machine
from myproject.backend.state_machine import main_loop
from otherproject.ai import body
from otherproject.ai import mind
from otherproject.ai import soul

Statements

通常每行只有一个语句

  • 在没有 else 的情况下可以尝试 if 语句放在一行
if foo: bar(foo)

Access Control

  • 如果访问器函数很简单, 则应使用公共变量而不是访问器函数来避免Python中函数调用的额外成本

  • 当添加更多功能时,您可以使用 property 来保持语法一致

  • 另一方面, 如果访问更复杂, 或访问变量的成本很, 则应使用函数调用, 例如 get_foo()set_foo()

  • If the past behavior allowed access through a property, do not bind the new accessor functions to the property. Any code still attempting to access the variable by the old method should break visibly so they are made aware of the change in complexity

!!#0000ff what?!!

Naming

module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_CONSTANT_NAME, global_var_name, instance_var_name, function_parameter_name, local_var_name
  • 函数名, 变量名和文件名应该是描述性的, 避免缩写
  • 不要使用项目之外的读者不明确或不熟悉的缩写, 也不要通过删除单词中的字母来缩写
  • 始终使用.py 拓展名, 不要使用破折号

避免使用的名字

  • 除计数器或迭代器之外的单个字符名称, 可以再 try/except 语句中使用 e 作为异常标识符
  • 不能在任何模块或包名中使用dashes (-)
  • 不能使用头尾上下划线的名字 (Python 的保留符号)
# No use:
__double_leading_and_trailing_underscore__

命名惯例

  • “Internal” 表示模块内部, 或类中 protected or private

  • 在名字前加上 _ 能保护模块中的变量或者函数 (from module import * 不会引用到前置 _ 的变量)

  • 在名字前加上 __ 双下划线能够表示这是类中的私有变量或方法. 但是 Google 不建议使用它, 因为它影响可读性和可测试性, 并且不是真正私有的

!!#0000ff What shell I do?!!

  • 将相关类和顶级函数放在一个模块中. 与 Java 不同, 不需要将每个模块限制为一个类

!!#0000ff what is top-level function?!!

  • Underscores may appear in unit test method names starting with test to separate logical components of the name, even if those components use CapWords

!!#0000ff …!!
- One possible pattern is test<MethodUnderTest>_<state>. e.g. testPop_EmptyStack

文件名

  • Python 的文件名必须以 .py 结尾并且不能有 - 破折号, 这允许了它们被导入并且进行单元测试.

  • 如果您希望在没有扩展名的情况下访问可执行文件, 请使用符号链接 (symbolic link) 或包含 exec "$0.py" "$@" 的简单bash包装器

!!#0000ff ???!!

来自 Guido 的建议书

|| Type || Public || Internal ||
|| Packages || lower_with_under || ||
|| Modules || lower_with_under || _lower_with_under ||
|| Classes || CapWords || _CapWords ||
|| Exceptions || CapWords || ||
|| Functions || lower_with_under() || _lower_with_under() ||
|| Global/Class Constants || CAPS_WITH_UNDER || _CAPS_WITH_UNDER ||
|| Global/Class Variables || lower_with_under || _lower_with_under ||
|| Instance Variables || lower_with_under || _lower_with_under (protected) ||
|| Method Names || lower_with_under() || _lower_with_under() (protected) ||
|| Function/Method Parameters || lower_with_under || ||
|| Local Variables || lower_with_under || ||

  • 尽管 Python 支持前置双下划线来将 things 变得私有, 但不建议这样做. 尽量使用前置单下划线, They are easier to type, read, and to access from small unit tests. Lint warnings take care of invalid access to protected members

Main

  • 即使是用作可执行文件的文件也应该是可导入的, 但是导入时不应该执行程序的 main functionality
  • main functionality 应该在 main() 函数中
def main():
    ...

if __name__ == '__main__':
    main()
  • 导入模块时, 将执行所有 top level 的代码

!!#0000ff top level ?!!

  • 当这个文件被 !!#0000ff pydoc!! 时, 注意不要调用函数, 创建对象或执行不应执行的其他操作

!!#0000ff …!!

Function length

小而专注

  • 我们认识到长函数有时是合适的, 因此对函数长度没有硬性限制

  • 如果函数超过大约 !!#ff0000 40!! 行, 请考虑是否可以在不损害程序结构的情况下将其分解

  • 保持您的功能简洁易用, 使其他人更容易阅读和修改您的代码

  • 不要被修改现有的长代码所吓倒

    • 如果利用这种长代码工作是困难的
    • 你发现错误很难 debug
    • 或者你想在不同的 contexts 中只是用它的某部分
    • 考虑将它分解为小而易于管理的几部分

Type Annotations

Line Breaking

  • 加上类别注释后, 许多函数会变成一行一个参数
def my_method(self,
              first_var: int,
              second_var: Foo,
              third_var: Optional[Bar]) -> int:
  ...
def my_method(self, first_var: int) -> int:
  ...
  • 如果最后一个参数或者返回类型过长, 可以换行
def my_method(
    self, first_var: int) -> Tuple[MyLongType1, MyLongType1]:
  ...
  • 如果最后一个参数和返回值不能放在同一行
def my_method(
    self, **kw_args: Optional[MyLongType]
) -> Dict[OtherLongType, MyLongType]:
  ...
  • try to keep sub-types unbroken
def my_method(
    self,
    first_var: Tuple[List[MyLongType1],
                     List[MyLongType2]],
    second_var: List[Dict[
        MyLongType3, MyLongType4]]) -> None:
  ...
def my_function(
    long_variable_name:
        long_module_name.LongTypeName,
) -> None:
  ...

Forward Declarations

  • 如果您需要使用尚未定义的同一模块中的类名 - 例如,如果您需要类声明中的类,或者您使用下面定义的类 - 请为类使用字符串 名称。
class MyClass(object):

  def __init__(self,
               stack: List["MyClass"]) -> None:

Default Values

  • = 周围使用空格
def func(a: int = 0) -> int:
  ...

NoneType

  • 使用显示的 Optional 而不是隐式的 Optional
  • You can use Union, but if there is only one other type, use Optional
# Yes:
def func(a: Optional[Text], b: Optional[Text] = None) -> Text:
  ...
def multiple_nullable_union(a: Union[None, Text, int]) -> Text
  ...
# No:
def nullable_union(a: Union[None, Text]) -> Text:
  ...
def implicit_optional(a: Text = None) -> Text:
  ...

!!#0000ff ?!!

Type Aliases

  • 您可以声明复杂类型的别名。 别名的名称应为CapWorded。 如果别名仅在此模块中使用,则它应为_Private。
_ShortName = module_with_long_name.TypeWithLongName
ComplexMap = Mapping[Text, List[Tuple[int, int]]]
  • 其他示例是复杂的嵌套类型和函数的多个返回变量(作为元组)。

Typing internal variables

  • 如果内部变量的类型很难或无法推断,您可以将其作为特殊注释提供
a = SomeUndecoratedFunction()  # type: Foo

Tuples vs Lists

a = [1, 2, 3]  # type: List[int]
b = (1, 2, 3)  # type: Tuple[int, ...]
c = (1, "2", 3.5)  # type: Tuple[int, Text, float]

and so on

Parting Words

  • BE CONSISTENT
  • – We present global style rules here so people know the vocabulary, but local style is also important
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值