这里有新鲜出炉的 Python 入门, 程序狗速度看过来!
Python 编程语言
Python 是一种面向对象, 解释型计算机程序设计语言, 由 Guido van Rossum 于 1989 年底发明, 第一个公开发行版发行于 1991 年. Python 语法简洁而清晰, 具有丰富和强大的类库. 它常被昵称为胶水语言, 它能够把用其他语言制作的各种模块 (尤其是 C/C++) 很轻松地联结在一起.
这篇文章主要给大家介绍了关于 Python 中单, 双下划线区别的相关资料, 文中通过示例代码介绍的非常详细, 对大家的学习或者工作具有一定的参考学习价值, 需要的朋友们下面随着小编来一起学习学习吧.
前言
Python 的代码风格由 PEP 8 描述. 这个文档描述了 Python 编程风格的方方面面. 在遵守这个文档的条件下, 不同程序员编写的 Python 代码可以保持最大程度的相似风格. 这样就易于阅读, 易于在程序员之间交流.
我们大家在学习 Python 的时候, 好像很多人都不理解为什么在方法 (method) 前面会加好几个下划线, 有时甚至两边都会加, 比如像__this__这种. 在我看到上面的文章之前, 我一直以为 Python 中这些下划线的作用就像 Golang 中方法 / 函数的大小写一样, 或是一些其他语言中的 private,public 的作用一样, 但仔细深究, 这不全是 Python 这样设计的初衷.
下面我们具体分析, 话不多说了, 来一起看看吧.
单下划线开头
我们经常看到方法或者属性前面加了单下划线, 并认为它表示该方法或者属性是该类型 (Python 和 Golang 一样, 不光类可以有方法, 很多类型甚至基本类型也可以定义方法) 的私有方法或属性. 但其实在 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 官方说明中得到答案: https://www.python.org/dev/peps/pep-0008/#method-names-and-instance-variables .
前面我们就说了, 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)) # 和上面等效
我们一般称__len__() 这种方法为 magic methods, 一些操作符后台调用的也是也是这些 magic methods, 比如 + 后台调用的是__add__,- 调用的是__sub__, 所以这种机制使得我们可以在自己的类中覆写操作符(见后面例子). 另外, 有的时候这种开头结尾双下划线方法仅仅是某些特殊场景的回调函数, 比如__init__() 会在对象的初始化时调用,__new__() 会在构建一个实例的时候调用等等. 下面我们看两个例子:
class CrazyNumber(object):
def __init__(self, n):
self.n = n
def __add__(self, other):
return self.n - other
def __sub__(self, other):
return self.n + other
def __str__(self):
return str(self.n)
num = CrazyNumber(10)
print(num) # output is: 10
print(num + 5) # output is: 5
print(num - 20) # output is: 30
在上面这个例子中, 我们覆写了 + 和 - 操作符, 将他们的功能交换了. 再看个例子:
class Room(object):
def __init__(self):
self.people = []
def add(self, person):
self.people.append(person)
def __len__(self):
return len(self.people)
room = Room()
room.add("Igor")
print len(room) # output is: 1
这个例子中, 因为我们实现了__len__(), 所以 Room 对象也可以使用 len 函数了.
所有此类的方法都在这里有说明: documentation.
结论
使用单下划线 (_one_underline) 开头表示方法不是 API 的一部分, 不要直接访问(虽然语法上访问也没有什么问题).
使用双下划线开头 (__two_underlines) 开头表示子类不能覆写该方法. 除非你真的知道你在干什么, 否则不要使用这种方式.
当你想让自己定义的对象也可以像 Python 内置的对象一样使用 Python 内置的一些函数或操作符 (比如 len,add,+,-,== 等) 时, 你可以定义该类方法.
当然还有些属性只在末尾加了但下划线, 这仅仅是为了避免我们起的一些名字和 Python 保留关键字冲突, 没有特殊含义.
注: 本文大部分内容参考自 Difference between _ , and __xx in Python .
总结
以上就是这篇文章的全部内容了, 希望本文的内容对大家的学习或者工作具有一定的参考学习价值, 如果有疑问大家可以留言交流, 谢谢大家对 PHPERZ 的支持.
来源: http://www.phperz.com/article/18/0130/361511.html