浅谈python 里面的单下划线与双下划线的区别


Posted in Python onDecember 01, 2017

在学习Python的时候,很多人都不理解为什么在方法(method)前面会加好几个下划线,有时甚至两边都会加,比如像 __this__ 这种。在我看到上面的文章之前,我一直以为Python中这些下划线的作用就像Golang中方法/函数的大小写一样,或是一些其他语言中的 private 、 public 的作用一样,但仔细深究,这不全是Python这样设计的初衷。下面我们具体分析。

主要存在四种命名

1. object # 公用方法
2. __object__ # 内建方法,用户不要这样定义
3. __object # 全私有,全保护
4. _object # 半保护

核心风格:避免用下划线作为变量名的开始。

因为下划线对解释器有特殊的意义,而且是内建标识符所使用的符号,我们建议程序员避免用下划线作为变量名的开始。一般来讲,变量名_object被看作是“私有的”,在模块或类外不可以使用,不能用'from module import *'导入。当变量是私有的时候,用_object来表示变量是很好的习惯。

单下划线+类名,eg:_Class__object 机制就可以访问__object__了。因为变量名__object__对Python 来说有特殊含义,对于普通的变量应当避免这种命名风格。

“单下划线” 开始的成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量;”双下划线” 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。(如下列所示)

以单下划线开头_foo的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用“from xxx import *”而导入;以双下划线开头的__foo代表类的私有成员;以双下划线开头和结尾的__foo__代表python里特殊方法专用的标识,如 __init__()代表类的构造函数。

class Foo():
  def __init__():
    ...

  def public_method():
    print 'This is public method'

  def __fullprivate_method():
    print 'This is fullprivate_method'

  def _halfprivate_method():
    print 'This is halfprivate_method'

  f = Foo()
  f.public_method() # OK
  f.__fullprivate_method() # Error occur
  f._halfprivate_method() # OK
  f._Foo__fullprivate_method() # OK

从上面的例子可以看出,f._halfprivate_method()可以直接访问,确实是。不过根据python的约定,应该将其视作private,而不要在外部使用它们,(如果你非要使用也没辙),良好的编程习惯是不要在外部使用它。同时,根据Python docs的说明,_object和__object的作用域限制在本模块内。

大家看下面这段程序的输出:

class A(object):
  def __init__(self):
    self.__private()
    self.public()
  def __private(self):
    print 'A.__private()'
  def public(self):
    print 'A.public()'
class B(A):
  def __private(self):
    print 'B.__private()'
  def public(self):
    print 'B.public()'
b = B()

初探

正确的答案是:

A.__private()
B.public()

我们分别看下类A和类B的属性都有哪些:

>>> print '\n'.join(dir(A))
_A__private
__init__
public

>>> print '\n'.join(dir(B))
_A__private
_B__private
__init__
public

为什么类A有个名为_A__private的 属性呢?而且__private消失了!这就要谈谈Python的私有变量“矫直”了。

Python把以两个或以上下划线字符开头且没有以两个或以上下划线结尾的变量当作私有变量。私有变量会在代码生成之前被转换为长格式(变为公有)。转换机制是这样的:在变量前端插入类名,再在前端加入一个下划线字符。这就是所谓的私有变量矫直(Private name mangling)。如类 A里的__private标识符将被转换为_A__private,这就是上一节出现_A__private和__private消失的原因了。

再讲两点题外话:

一是因为矫直会使标识符变长,当超过255的时候,Python会切断,要注意因此引起的命名冲突。

二是当类名全部以下划线命名的时候,Python就不再执行矫直。如:

class ____(object):
  def __init__(self):
    self.__method()
  def __method(self):
    print '____.__method()'

print '\n'.join(dir(____))
__class__
__delattr__
__dict__
__doc__
__getattribute__
__hash__
__init__
__method       # 没被矫直
__module__
__new__
__reduce__
__reduce_ex__
__repr__
__setattr__
__str__
__weakref__

obj = ____()
____.__method()

obj.__method()   # 可以外部调用
____.__method()

现在我们回过头来看看为什么会输出“A.__private()”吧!

矫直之后,类A的代码就变成这样了:

class A(object):
  def __init__(self):
    self._A__private()     # 这行变了
    self.public()
  def _A__private(self):      # 这行也变了
    print 'A.__private()'
  def public(self):
    print 'A.public()'

因为在类B定义的时候没有覆盖__init__方法,所以调用的仍然是A.__init__,即执行了self._A__private(),自然输出“A.__private()”了。

下面的两段代码可以增加说服力,增进理解:

class C(A):
  def __init__(self): # 重写 __init__ ,不再调用self._A__private
    self.__private()# 这里绑定的是 _C_private
    self.public()
  def __private(self):
    print 'C.__private()'
  def public(self):
    print 'C.public()'

c = C()
答案:
C.__private()
C.public()
class A(object):
  def __init__(self):
    self._A__private()  # 调用一个没有定义的函数,但可执行 
    self.public()
  def __private(self):
    print 'A.__private()'
  def public(self):
    print 'A.public()'

a = A()
答案:
A.__private()
A.public()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python中xrange用法分析
Apr 15 Python
Python探索之pLSA实现代码
Oct 25 Python
python执行CMD指令,并获取返回的方法
Dec 19 Python
利用arcgis的python读取要素的X,Y方法
Dec 22 Python
python爬取cnvd漏洞库信息的实例
Feb 14 Python
如何使用Python进行OCR识别图片中的文字
Apr 01 Python
Django中create和save方法的不同
Aug 13 Python
python操作docx写入内容,并控制文本的字体颜色
Feb 13 Python
解析pip安装第三方库但PyCharm中却无法识别的问题及PyCharm安装第三方库的方法教程
Mar 10 Python
使用keras实现BiLSTM+CNN+CRF文字标记NER
Jun 29 Python
django使用多个数据库的方法实例
Mar 04 Python
两行代码解决Jupyter Notebook中文不能显示的问题
Apr 24 Python
vscode 远程调试python的方法
Dec 01 #Python
Python中单、双下划线的区别总结
Dec 01 #Python
从CentOS安装完成到生成词云python的实例
Dec 01 #Python
Django的分页器实例(paginator)
Dec 01 #Python
浅谈python装饰器探究与参数的领取
Dec 01 #Python
Python简单读取json文件功能示例
Nov 30 #Python
Python实现嵌套列表及字典并按某一元素去重复功能示例
Nov 30 #Python
You might like
PHP5.2下preg_replace函数的问题
2015/05/08 PHP
PHP实现mysqli批量执行多条语句的方法示例
2017/07/22 PHP
PHP反射原理与用法深入分析
2019/09/28 PHP
ThinkPHP5.1验证码功能实现的示例代码
2020/06/08 PHP
javascript 面向对象思想 附源码
2009/07/07 Javascript
javascript cookie操作类的实现代码小结附使用方法
2010/06/02 Javascript
jQuery的Scrollify插件实现滑动到页面下一节点
2015/07/05 Javascript
WEB前端开发都应知道的jquery小技巧及jquery三个简写
2015/11/15 Javascript
Node.js Streams文件读写操作详解
2016/07/04 Javascript
JavaScript基础重点(必看)
2016/07/09 Javascript
Angualrjs 表单验证的两种方式(失去焦点验证和点击提交验证)
2017/05/09 Javascript
JS运动特效之完美运动框架实例分析
2018/01/24 Javascript
Ant Design Pro 之 ProTable使用操作
2020/10/31 Javascript
[01:25]DOTA2超级联赛专访iG 将调整状态找回自己
2013/06/05 DOTA
Python程序中使用SQLAlchemy时出现乱码的解决方案
2015/04/24 Python
Python实现将Excel转换为json的方法示例
2017/08/05 Python
Python实现字符串格式化输出的方法详解
2017/09/20 Python
详解Numpy中的广播原则/机制
2018/09/20 Python
python 实现交换两个列表元素的位置示例
2019/06/26 Python
Python3+Requests+Excel完整接口自动化测试框架的实现
2019/10/11 Python
PYTHON发送邮件YAGMAIL的简单实现解析
2019/10/28 Python
mac使用python识别图形验证码功能
2020/01/10 Python
Tensorflow进行多维矩阵的拆分与拼接实例
2020/02/07 Python
python编程进阶之异常处理用法实例分析
2020/02/21 Python
HTML5和以前HTML4的区别整理
2013/10/20 HTML / CSS
Peter Alexander新西兰站:澳大利亚领先的睡衣设计师品牌
2016/12/10 全球购物
获取邓白氏信用报告:Dun & Bradstreet
2019/01/22 全球购物
外贸英语专业求职信范文
2013/12/25 职场文书
机电专业个人求职信范文
2013/12/30 职场文书
办理生育手续介绍信
2014/01/14 职场文书
爱岗敬业演讲稿范文
2014/01/14 职场文书
访谈节目策划方案
2014/05/15 职场文书
区域销售大会开幕词
2016/03/04 职场文书
2017元旦、春节期间廉洁自律承诺书
2016/03/25 职场文书
一次项目中Thinkphp绕过禁用函数的实战记录
2021/11/17 PHP
Mac电脑OS系统下安装Nginx的详细教程
2022/04/14 Servers