2.11 条件表达式
适用于单行函数
2.11.1 定义
条件表达式(有时称为“三元运算符”)是为if语句提供较短语法的机制。例如:x = 1 if cond else 2。
2.11.2 优点
比if语句更短,更方便。
2.11.3缺点
可能比if语句难读。如果表达式很长,则可能很难找到条件。
2.11.4 结论
适用于单行函数. 在其他情况下,推荐使用完整的if语句.
2.12 参数值
2.12.1 定义
可以在函数的参数列表的末尾指定变量的值,例如def foo(a, b=0):。如果foo仅使用一个参数 b调用if,则将其设置为0。如果使用两个参数调用if,则b具有第二个参数的值。
2.12.2 优点
通常,您有一个使用许多默认值的函数,但是在极少数情况下,您想覆盖默认值。默认参数值提供了一种简便的方法,而不必为罕见的异常定义很多函数。由于Python不支持重载的方法/函数,因此默认参数是“伪造”重载行为的简便方法。
2.12.3缺点
默认参数在模块加载时评估一次。如果参数是可变对象(例如列表或字典),则可能会导致问题。如果函数修改了对象(例如,通过将项目附加到列表),则默认值将被修改。
2.12.4 结论
注意:不要在函数或方法定义中使用可变对象作为默认值。
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 = ()): ...
No: def foo(a, b=[]): ... No: def foo(a, b=time.time()): ... No: def foo(a, b=FLAGS.my_thing): ... No: def foo(a, b: Mapping = {}): ... |
2.13 属性
在通常使用简单,轻量级的访问器或设置器方法的地方,使用属性访问或设置数据。
2.13.1 定义
一种用于包装方法调用的方式,要求在轻量级计算时获取并设置属性作为标准属性访问。
2.13.2 优点
通过消除对简单属性访问的显式get和set方法调用,提高了可读性。允许计算是懒惰的。考虑使用Python方式维护类的接口。在性能方面,当直接变量访问是合理的时,允许属性绕过需要琐碎的访问器方法的情况。这也允许将来在不破坏接口的情况下添加访问器方法。
2.13.3 缺点
必须object在Python 2中继承。可以隐藏副作用,就像运算符重载一样。对于子类可能会造成混淆。
2.13.4 结论
使用新代码中的属性来访问或设置数据,而通常情况下,这些属性本可以使用简单,轻量级的访问器或设置器方法。属性应使用@property装饰器创建。
如果不覆盖属性本身,则对属性的继承可能不是显而易见的。因此,必须确保间接调用访问器方法,以确保属性会调用子类中重写的方法(使用模板方法设计模式)。
2.14 True/False的求值
尽可能使用隐式false
2.14.1 定义
Python在布尔上下文中会将某些值求值为False。简单地说,所有的“空”值都被认为是False,因此 0, None, [], {}, ''在布尔上下文中所有值都为是False。
2.14.2 优点
使用Python布尔值的条件更易于阅读且不易出错。在大多数情况下,它们也更快。
2.14.3 缺点
对于C / C ++开发人员来说可能看起来很奇怪。
2.14.4 结论
如果可能,请使用“隐式”假,例如,if foo:而不是if foo != []:。不过,请注意以下几点:
始终使用if foo is None:(或is not None)检查None值。例如,在测试是否将默认None 设置为的变量或参数设置为其他值时。另一个值可能是在布尔上下文中为False的值!
切勿将布尔变量与False使用进行比较==。使用if not x: 代替。如果您需要与区分False,None则将表达式链接起来,例如if not x and x is not None:。
对于序列(字符串,列表,元组),使用的事实,空序列为假,所以if seq:和if not seq:是优选的,以if len(seq): 及if not len(seq):分别。
当处理整数时,隐式false可能带来的收益大于收益(即,意外地处理None为0)。您可以将一个已知为整数(而不是的结果len())的值与整数0进行比较。
2.15 过时的语言特性
尽可能使用字符串方法而不是字符串模块。使用函数调用语法而不是apply。如果函数参数是内联lambda,则使用列表理解和for循环,而不是filter和map。使用for循环而不是reduce。
2.15.1 定义
当前版本的Python提供了人们通常更喜欢的替代构造。
2.15.2 结论
我们不使用任何不支持这些功能的Python版本,因此没有理由不使用新样式。
Yes: words = foo.split(':') [x[1] for x in my_list if x[2] == 5] map(math.sqrt, data) # 没有内联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) |
2.16 词法作用域(Lexical Scoping)
可以使用。
2.16.1 定义
嵌套的Python函数可以引用在封闭函数中定义的变量,但不能分配给它们。变量绑定使用词法作用域来解决,即基于静态程序文本。在块中对名称的任何赋值都会使Python将对该名称的所有引用都视为一个局部变量,即使使用是在赋值之前。如果发生全局声明,则将该名称视为全局变量。
使用此功能的一个示例是:
def get_adder(summand1): """返回一个将数字加到给定数字上的函数。""" def adder(summand2): return summand1 + summand2
return adder |
2.16.2 优点
通常会产生更清晰,更优雅的代码。特别让经验丰富的Lisp和Scheme(以及Haskell和ML等)的程序员感到欣慰。
2.16.3 缺点
可能导致令人困惑的错误。
2.16.4 结论
可以使用。
2.17 函数与方法装饰器
如果有明显的优势,请明智地使用装饰器。避免 @staticmethod并限制使用@classmethod。
2.17.1 定义
函数和方法的装饰器(也称为“ @符号”)。一种常见的修饰符是@property,用于将普通方法转换为动态计算的属性。但是,装饰器语法也允许用户定义装饰器。具体来说,对于某些功能my_decorator,这是:
class C: @my_decorator def method(self): # method body ... |
等效于:
class C: def method(self): # method body ... method = my_decorator(method) |
2.17.2 优点
优雅地指定了方法上的一些转换;转换可能会消除一些重复的代码,强制执行不变式等。
2.17.3 缺点
装饰器可以对函数的参数或返回值执行任意操作,从而导致令人惊讶的隐式行为。此外,装饰器在导入时执行。装饰器代码中的错误几乎不可能恢复。
2.17.4 结论
如果有明显的优势,请明智地使用装饰器。装饰器应遵循与功能相同的导入和命名准则。装饰器的python文档应该清晰的说明该函数是一个装饰器。为装饰器编写单元测试.避免装饰器自身对外界的依赖(例如,不要依赖文件,套接字,数据库连接等),因为在装饰器运行时(在导入时,可能来自pydoc或其他工具)它们可能不可用。在所有情况下,应(尽可能)确保使用有效参数调用的装饰器成功。
2.18 线程
不要依赖内置类型的原子性。
尽管Python的内置数据类型(例如字典)似乎具有原子操作,但在某些极端情况下,它们不是原子操作(例如,如果将 __hash__ 或 __eq__ 实现为Python方法),则不应依赖其原子性。您也不应该依赖于原子变量赋值(因为这又取决于字典)。
使用队列模块的Queue数据类型作为线程之间通信数据的首选方式。否则,请使用线程模块及其锁定原语。了解条件变量的合适使用方式,使用 threading.Condition 来取代低级别的锁了。
2.19 威力过大的特性
避免使用这些特性。
2.19.1 定义
Python是一种非常灵活的语言,可为您提供许多精美功能,例如自定义元类,访问字节码,即时编译,动态继承,对象重载,导入hack,反射(例如的某些用法 getattr()),对系统内部的修改,等等。
2.19.2 优点
这些是强大的语言功能。它们可以使您的代码更紧凑。
2.19.3 缺点
在并非绝对必要时使用这些“炫酷”功能非常诱人。难以阅读,理解和调试使用底层异常功能的代码。起初(原始作者)似乎没有这种方式,但是在重新访问代码时,它往往比更长但简单的代码更加困难。
2.19.4 结论
在代码中避免使用这些功能。
2.20 现代Python:Python 3和__future__导入
尽管不是每个项目都可以使用它,但所有代码都应编写为3兼容(并在3下进行测试)。
2.20.1 定义
python3是Python语言的一个重大变化。虽然现有的代码通常是在2.7的基础上编写的,但是有一些简单的事情可以让代码更加明确地表达其意图,从而更好地准备在python3下使用而不需要修改。
2.20.2 优点
一旦项目的所有依赖项都准备好了,用Python3编写的代码会更加明确,更容易在Python3下运行。
2.20.3 缺点
有些人觉得额外的样板很难看。将导入添加到实际上不需要导入所添加的功能的模块中是不寻常的。
2.20.4 结论
从__future__导入
鼓励使用from __future__ import语句。所有新代码应包含以下内容,现有代码应尽可能更新以兼容:
from __future__ import absolute_import from __future__ import division from __future__ import print_function |
2.21 类型注释代码(Type Annotated Code)
2.21.1 定义
类型注释(或“类型提示”)用于函数或方法的参数并返回值:
def func(a: int) -> List[int]: |
您还可以使用类似的PEP-526语法声明变量的类型 :
a: SomeType = some_func() |
或者在必须支持旧版Python版本的代码中使用类型注释。
a = some_func() # type: SomeType |
2.21.2 优点
类型注释提高了代码的可读性和可维护性。类型检查器会将许多运行时错误转换为构建时错误,并降低使用Power Features的能力。
2.21.3 缺点
必须保持类型声明是最新的。您可能会看到您认为是有效代码的类型错误。使用类型检查器可能会降低您使用Power Features的能力。
2.21.4 结论
强烈建议您在更新代码时启用Python类型分析。添加或修改公共API时,请包括类型注释,并在构建系统中启用通过pytype进行检查。由于静态分析对Python来说还比较陌生,因此我们认识到不良的副作用(例如错误推断的类型)可能会阻止某些项目采用。在这种情况下,鼓励作者添加带有TODO的注释或指向描述当前阻止在BUILD文件或代码本身中采用类型注释的问题的bug的链接。