Python中下划线的区别
网上关于下划线的说法不一,主要有两类,总结比较如下:
其一
Python 用下划线作为变量前缀和后缀指定特殊变量
_xxx 不能用’from module import *’导入
_xxx_ 系统定义名字
__xxx 类中的私有变量名
核心风格:避免用下划线作为变量名的开始。
一般来讲,变量名_xxx被看作是“私有 的”,在模块或类外不可以使用。当变量是私有的时候,用_xxx
来表示变量是很好的习惯。因为变量名__xxx__
对Python 来说有特殊含义,对于普通的变量应当避免这种命名风格。
“单下划线” 开始的成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量;
“双下划线” 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。
以单下划线开头(_foo
)的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用“from xxx import *”而导入;
以双下划线开头的(__foo__
)代表类的私有成员;以双下划线开头和结尾的(__foo__
)代表python里特殊方法专用的标识,如 __init__
()代表类的构造函数。
其二
单下划线开头
加了单下划线,通常认为它表示该方法或者属性是该类型的私有方法或属性。其实,在Python中不存在真正意义上的私有方法或者属性,前面加单下划线_只是表示你不应该去访问这个方法或者属性,因为它不是API的一部分。
Python
class BaseForm(StrAndUnicode):
...
def _get_errors(self):
"Returns an ErrorDict for the data provided for the form"
if self._errors is None:
self.full_clean()
return self._errors
errors = property(_get_errors)
该代码片段来自Django源码(django/forms/forms.py)。这段代码的设计就是errors属性是对外API的一部分,如果你想获取错误详情,应该访问errors属性,而不是(也不应该)访问_get_errors方法。
双下划线开头
很多地方的说法是:Python中双下划线开头表示私有。这样理解可能也不能说错,但这不是Python设计双下划线开头的初衷和目的,Python设计此的真正目的仅仅是为了避免子类覆盖父类的方法。示例:
class A(object):
def __method(self):
print("I'm a method in class A")
def method_x(self):
print("I'm another method in class A\n")
def method(self):
self.__method()
self.method_x()
class B(A):
def __method(self):
print("I'm a method in class B")
def method_x(self):
print("I'm another method in class B\n")
if __name__ == '__main__':
print("situation 1:")
a = A()
a.method()
b = B()
b.method()
print("situation 2:")
# a.__method()
a._A__method()
执行结果:
situation 1:
I'm a method in class A
I'm another method in class A
I'm a method in class A
I'm another method in class B
situation 2:
I'm a method in class A
需要注意:
A类中我们定义了__method()
、method_x
和method()
三个方法;然后我们重新定义一个类B,继承自A,并且在B类中覆写(override)了其父类的__method()
和method_x方法,但是从输出结果看,B对象调用method()方法时调用了其父类A的__method()
方法和自己的method_x()方法。也就是说,__method()
覆写没有生效,而method_x()覆写生效了。而这也正是Python设计双下划线开头的唯一目的。
这一点也可在Python官方说明中得到答案。前面我们就说了,Python中不存在真正意义上的私有变量。对于双下划线开头的方法和属性虽然我们不能直接引用,那是因为Python默认在其前面加了前缀_
类名,所以就像situation 2下面的代码,虽然我们不能用a直接访问__method()
,但却可以加上前缀去访问,即_A__method()
。
开头结尾双下划线
一般来说像__this__
这种开头结尾都加双下划线的方法表示这是Python自己调用的,你不要调用。比如我们可以调用len()函数来求长度,其实它后台是调用了__len__()
方法。一般我们应该使用len,而不是直接使用__len__()
:
a = [1, 2, 3]
print(len(a))
print(a.__len__()) # 和上面等效
num = 10
print(num + 10)
print(num.__add__(10)) # 和上面等效
总结
- 使用单下划线(
_one_underline
)开头表示方法不是API的一部分,不要直接访问(虽然语法上访问也没有什么问题)。 - 使用双下划线开头(
__two_underlines
)开头表示子类不能覆写该方法。除非你真的知道你在干什么,否则不要使用这种方式。 - 当你想让自己定义的对象也可以像Python内置的对象一样使用Python内置的一些函数或操作符(比如len、add、+、-、==等)时,你可以定义该类方法。
- 当然还有些属性只在末尾加了但下划线,这仅仅是为了避免我们起的一些名字和Python保留关键字冲突,没有特殊含义。
Python中下划线完全解读
Python中单、双下划线的区别总结
Difference between _, __ and xx in Python