强制性:命名约定
应避免的命名
永远不要使用字符‘l’(小写字母el)、‘O’(大写字母oh)或‘I’(大写字母eye)作为单字符变量名。在某些字体中,这些字符与数字1和0难以区分。当想使用‘l’时,改用‘L’。
ASCII兼容性
标准库中使用的标识符必须符合PEP 3131策略部分中描述的ASCII兼容性。
包和模块名称
模块名称应该简短,全部小写。如果可以提高可读性,可以在模块名称中使用下划线。Python包也应该有简短、全部小写的名称,但不建议使用下划线。
当用C或C++编写的扩展模块有一个提供更高层次(例如,更面向对象)接口的Python模块时,C/C++模块应以前导下划线命名(例如,_socket)。
类名称
类名称通常应使用CapWords(大驼峰或帕斯卡命名法)约定。
在接口已文档化且主要用作可调用对象的情况下,也可以使用函数命名约定。
请注意,内置名称有单独的约定:大多数内置名称是单个单词(或两个连在一起的单词),只有异常名称和内置常量使用CapWords约定。
类型变量名称
在PEP 484中引入的类型变量名称通常应使用CapWords,并倾向于使用简短名称:T、AnyStr、Num。建议为用于声明协变或逆变行为的变量分别添加_co或_contra后缀:
from typing import TypeVar
VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)
异常名称
由于异常应该是类,因此这里适用类的命名约定。但是,你应该在异常名称后使用“Error”后缀(如果异常实际上是一个错误)。
全局变量名称
(希望这些变量仅用于模块内部。)命名约定与函数相似。
设计为通过from M import * 使用的模块应使用__all__机制来防止导出全局变量,或者使用较旧的约定,即在全局变量名前加上下划线(你可能希望这样做来表示这些全局变量是“模块非公开的”)。
函数和变量名称
函数名称应小写,必要时用下划线分隔单词以提高可读性。
变量名称遵循与函数名称相同的约定。
只有在已经普遍采用mixedCase风格(驼峰命名法)的上下文中(例如threading.py),才允许使用mixedCase,以保持向后兼容性。
函数和方法参数
对于实例方法的第一个参数,总是使用self。
对于类方法的第一个参数,总是使用cls。
如果函数参数的名称与保留关键字冲突,通常最好添加一个尾随下划线,而不是使用缩写或拼写错误。因此,class_比clss更好。(也许更好的是,通过使用同义词来避免此类冲突。)
方法名称和实例变量
使用方法命名规则:小写,必要时用下划线分隔单词以提高可读性。
对于非公共方法和实例变量,仅使用一个前导下划线。
为避免与子类中的名称冲突,请使用两个前导下划线来调用Python的名称改编规则。Python使用类名来改编这些名称:如果类Foo有一个名为__a的属性,则不能通过Foo.__a来访问它。(一个固执的用户仍然可以通过调用Foo._Foo__a来访问它。)通常,仅当需要避免与子类中的属性名称冲突时,才应使用双前导下划线。
注意:关于__names的使用存在一些争议(请参见下文)。
常量
常量通常在模块级别定义,并全部使用大写字母,单词之间用下划线分隔。示例包括MAX_OVERFLOW和TOTAL。
为继承而设计
始终决定类的方法和实例变量(统称为“属性”)应该是公开的还是非公开的。如果有疑问,请选择非公开的;之后将非公开属性改为公开比将公开属性改为非公开更容易。
公开属性是那些你期望类的无关客户端使用的属性,同时你承诺避免进行向后不兼容的更改。
非公开属性则是不打算由第三方使用的属性;你不保证非公开属性不会更改甚至被删除。
我们在这里不使用“私有”一词,因为在Python中(除非进行通常不必要的大量工作),没有属性是真正私有的。
属性的另一个类别是那些属于“子类API”一部分的属性(在其他语言中通常称为“受保护的”)。有些类被设计为可以被继承,以扩展或修改类的行为方面。在设计这样的类时,请小心明确决定哪些属性是公开的,哪些属性是子类API的一部分,以及哪些属性确实仅应由你的基类使用。
考虑到这一点,以下是Pythonic的指南:
- 公开属性不应该有前导下划线。
- 如果你的公开属性名与保留关键字冲突,请在你的属性名后添加一个尾随下划线。这比缩写或拼写错误更好。(但是,尽管有这个规则,‘cls’是用于任何已知为类的变量或参数的首选拼写,特别是类方法的第一个参数。)
注意1:请参阅上面关于类方法的参数名称建议。
- 对于简单的公开数据属性,最好只暴露属性名,而不是复杂的访问器/修改器方法。请记住,Python提供了一种简单的路径来进行未来的增强,如果你发现简单的数据属性需要增加功能行为。在这种情况下,可以使用属性(properties)来在简单的数据属性访问语法背后隐藏功能实现。
注意1:尽量保持功能行为无副作用,尽管像缓存这样的副作用通常是可接受的。
注意2:避免对计算昂贵的操作使用属性;属性符号让调用者相信访问是(相对)便宜的。
- 如果你的类打算被子类化,并且你有不希望子类使用的属性,请考虑使用双前导下划线且没有尾随下划线的命名方式。这会触发Python的名称改写算法,将类名改写进属性名中。这有助于避免在子类不小心包含同名属性时发生属性名冲突。
注意1:请注意,在改写名称时仅使用简单的类名,因此如果子类选择了相同的类名和属性名,你仍然可能会遇到名称冲突。
注意2:名称改写可能会使某些用途(如调试和__getattr__())变得不那么方便。但是,名称改写算法已经得到了很好的文档说明,并且易于手动执行。
注意3:并非所有人都喜欢名称改写。请尝试在避免意外名称冲突与高级调用者的潜在使用需求之间找到平衡。
公共和内部接口
任何向后兼容性保证仅适用于公共接口。因此,让用户能够清楚地区分公共接口和内部接口是非常重要的。
已文档化的接口被视为公共接口,除非文档中明确声明它们是临时的或内部接口,并免除通常的向后兼容性保证。所有未文档化的接口都应视为内部接口。
为了更好地支持自省,模块应使用__all__属性明确声明其公共API中的名称。将__all__设置为空列表表示该模块没有公共API。
即使__all__设置得当,内部接口(包、模块、类、函数、属性或其他名称)仍应以单个前导下划线为前缀。
如果任何包含的命名空间(包、模块或类)被视为内部,则相应的接口也被视为内部。
导入的名称应始终被视为实现细节。除非它们是包含模块的API中明确记录的一部分(如os.path或包的__init__模块,该模块公开了来自子模块的功能),否则其他模块不得依赖对这些导入名称的间接访问。
相关文章: