浅谈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 socket编程实例详解
May 27 Python
python获取元素在数组中索引号的方法
Jul 15 Python
python数据结构之链表详解
Sep 12 Python
使用python 和 lint 删除项目无用资源的方法
Dec 20 Python
python+opencv实现霍夫变换检测直线
Oct 23 Python
python3 小数位的四舍五入(用两种方法解决round 遇5不进)
Apr 11 Python
python实现最大子序和(分治+动态规划)
Jul 05 Python
Django框架组成结构、基本概念与文件功能分析
Jul 30 Python
python3中eval函数用法使用简介
Aug 02 Python
Python关于__name__属性的含义和作用详解
Feb 19 Python
python中读入二维csv格式的表格方法详解(以元组/列表形式表示)
Apr 24 Python
Django怎么在admin后台注册数据库表
Nov 14 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与MySQL数据库操作常用代码 收集
2010/03/21 PHP
PHP四大安全策略
2014/03/12 PHP
PHP PDOStatement对象bindpram()、bindvalue()和bindcolumn之间的区别
2014/11/20 PHP
Thinkphp 中 distinct 的用法解析
2016/12/14 PHP
Yii2 queue的队列使用详解
2019/07/19 PHP
php设计模式之正面模式实例分析【星际争霸游戏案例】
2020/03/24 PHP
JQUBar 基于JQUERY的柱状图插件
2010/11/23 Javascript
Textbox控件注册回车事件及触发按钮提交事件具体实现
2013/03/04 Javascript
使用javascript实现简单的选项卡切换
2015/01/09 Javascript
js实现右下角提示框的方法
2015/02/03 Javascript
JavaScript中的Math.LOG2E属性使用详解
2015/06/14 Javascript
javascript日期处理函数,性能优化批处理
2015/09/06 Javascript
javascript自动恢复文本框点击清除后的默认文本
2016/01/12 Javascript
js实现图片缓慢放大缩小效果
2016/08/02 Javascript
浅析使用BootStrap TreeView插件实现灵活配置快递模板
2016/11/28 Javascript
JS实现的DIV块来回滚动效果示例
2017/02/07 Javascript
js获取浏览器的各种属性
2017/04/27 Javascript
AugularJS从入门到实践(必看篇)
2017/07/10 Javascript
最简单的JS实现json转csv的方法
2019/01/10 Javascript
Nodejs使用archiver-zip-encrypted库加密压缩文件时报错(解决方案)
2019/11/18 NodeJs
vue3.0中使用postcss-pxtorem的具体方法
2019/11/20 Javascript
PyCharm代码回滚,恢复历史版本的解决方法
2018/10/22 Python
pytorch 调整某一维度数据顺序的方法
2018/12/08 Python
Python中logging实例讲解
2019/01/17 Python
Django在admin后台集成TinyMCE富文本编辑器的例子
2019/08/09 Python
Python生成并下载文件后端代码实例
2020/08/31 Python
python读取excel数据并且画图的实现示例
2021/02/08 Python
华为慧通笔试题
2016/04/22 面试题
如何在发生故障的节点上重新安装 SQL Server
2013/03/14 面试题
销售自我评价
2013/10/22 职场文书
大家检讨书5000字
2014/02/03 职场文书
管理建议书范文
2014/05/13 职场文书
小学生纪念九一八事变演讲稿
2014/09/14 职场文书
幼儿园大班教育随笔
2015/08/14 职场文书
2019毕业典礼主持词!
2019/07/05 职场文书
导游词之麻姑仙境
2019/11/18 职场文书