Python中各种下划线使用总结和实例详解

Python中经常出现各种单下划线,双下划线,而且有的在前有的在后,有的是约定俗成的用法,有的则会强制对外隐藏。这一篇我们就一起来把各种下划线的用法说清楚。

我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。

五种下划线的用法

Python中下划线有如下五种用法:

  • 前置单下划线,例如_var
  • 后置单下划线,例如var_
  • 前置双下划线,例如__var
  • 前后双下划线,例如__var__
  • 单下划线,也就是单独的_

下面就对这五种用法分别用例子来进行说明

操作环境

下面的演示环境为

  • Python 3.7.1
  • IPython 7.2.0

前置单下划线,例如_var

这是一种约定俗成的写法,用来告诉程序员像这样定义的属性或者方法建议只被内部属用。这是PEP8中明确定义的一种写法。

Python并不像Java那样有私有和公共变量的概念,所以并不会强制某个属性或者方法只能被内部使用。

看下示例。

在文件test_underscore.py下定义如下类

class Test:
    def __init__(self):
        self.name = 'xiaofu'
        self._englishname = 'victor'

    def say(self):
        print(self.name)

    def _shout(self):
        print(self._englishname)

然后实例一个对象出来,尝试访问两个属性和两个方法,发现都可以被正常访问

In [1]: import test_underscore                                                                                                                                               

In [2]: xiaofu = test_underscore.Test()                                                                                                                                      

In [3]: xiaofu.name                                                                                                                                                          
Out[3]: 'xiaofu'

In [4]: xiaofu._englishname                                                                                                                                                  
Out[4]: 'victor'

In [5]: xiaofu.say()                                                                                                                                                         
xiaofu

In [6]: xiaofu._shout()                                                                                                                                                      
victor

所以总结起来,前置单下划线只是一种对程序员的建议,该属性或者方法只被内部使用。Python本身不会阻止该变量或者方法被实例对象访问

后置单下划线,例如var_

有的时候属性名或者方法名与某些系统关键字重复,这个时候就可以在后面加下划线来解决名字冲突。

看下示例。

In [7]: def test(name,class): pass                                                                                                                                           
  File "<ipython-input-7-d40cadd3ccf1>", line 1
    def test(name,class): pass
                      ^
SyntaxError: invalid syntax


In [8]: def test(name,class_): pass                                                                                                                                          

In [9]:    

做为另一个PEP8中明确定义的写法,后置单下划线用于解决名称冲突的场景。这也是一种非强制性的约定俗成的写法

前置双下划线,例如__var

这个需要重点说一说。

前面两种写法都是约定俗成的,但是这一次却不同。如果是前置双下划线的属性或方法,Python解释器会将该名字进行重写,以避免子类中出现不必要的名称冲突。这种操作被称为名字修饰(name mangling)。

看下示例。

修改上面的Test类如下

class Test:
    def __init__(self):
        self.name = 'xiaofu'
        self._englishname = 'victor'
        self.__hobby = 'basketball'

    def say(self):
        print(self.name)

    def _shout(self):
        print(self._englishname)

    def __play(self):
        print(self.__hobby)

重新import一下,尝试去查看实例对象的所有的属性和方法

In [1]: import test_underscore                                                                                                                                               

In [2]: xiaofu = test_underscore.Test()                                                                                                                                      

In [3]: dir(xiaofu)                                                                                                                                                          
Out[3]:
['_Test__hobby',
 '_Test__play',
 '__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_englishname',
 '_shout',
 'name',
 'say']

这里出现在最后的是我们自定义的四个属性和方法,但是前置双下划线的一个__hobby属性和__play()方法却不见了。如果仔细看,会发现在最上面出现了_Test__hobby_Test__play这两个属性。

会不会这两个就是我们定义的属性和方法呢?我们来访问看看

In [5]: xiaofu._Test__hobby                                                                                                                                                  
Out[5]: 'basketball'

In [6]: xiaofu._Test__play()                                                                                                                                                 
basketball

发现它们俩就是我们自己定义的__hobby__play(),只是Python帮我们对名字进行了重写,在前面加了一个类名前缀。所以前置双下划线的属性和方法是不可以直接被实例访问的,之所以加上“直接”,是因为Python只是改了个名字不让我们访问,用更改后的名字还是可以访问的。还是那句话,Python中没有私有和共有的概念,所以不能绝对让一个变量变为私有。

那么子类会如何去继承这种前置双下划线的属性和方法呢?

看下示例。

新建一个类来继承刚才的Test类

class ExtendedTest(Test):
    pass

查看一下子类的实例包含的所有属性和方法

In [1]: import test_underscore                                                                                                                                               

In [2]: xiaofu=test_underscore.ExtendedTest()                                                                                                                                

In [3]: dir(xiaofu)                                                                                                                                                          
Out[3]:
['_Test__hobby',
 '_Test__play',
 '__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_englishname',
 '_shout',
 'name',
 'say']

也就是说父类中定义的__hobby__play()也没有被直接继承,而是继承的_Test__hobby_Test__play()。所以访问的话也是会失败的

In [4]: xiaofu.__hobby                                                                                                                                                       
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-16b62e458c02> in <module>
----> 1 xiaofu.__hobby

AttributeError: 'ExtendedTest' object has no attribute '__hobby'

所以总结起来,前置双下划线的变量不能被实例访问,也不会继承给子类

前后双下划线,例如__var__

首先需要说明的是,虽然前后双下划线也包含前置双下划线,但是名字修饰并不会对其起作用

修改下前面的Test类

class Test:
    def __init__(self):
        self.name = 'xiaofu'
        self._englishname = 'victor'
        self.__hobby = 'basketball'
        self.__age__ = 99

    def say(self):
        print(self.name)

    def _shout(self):
        print(self._englishname)

    def __play(self):
        print(self.__hobby)

    def __grow__(self):
        print(self.__age__)

前后双下划线的属性和方法都可以正常访问

In [1]: import test_underscore                                                                                                                                               

In [2]: xiaofu=test_underscore.Test()                                                                                                                                        

In [3]: xiaofu.__age__                                                                                                                                                       
Out[3]: 99

In [4]: xiaofu.__grow__()                                                                                                                                                    
99

所以可以看到前后双下划线和普通前后都不带下划线的变量没有啥区别,确实是这样。不过前后双下划线的变量被预留做python中的特殊变量了,这些方法被称作魔术方法(magic methods),不建议开发中进行使用

单下划线,也就是单独的_

也是一种约定俗成的用法,有的时候使用单下划线来表示一个后面不会被使用的临时变量,例如循环中的index

In [1]: for _ in range(3): print('hello xiaofu')                                                                                                                             
hello xiaofu
hello xiaofu
hello xiaofu

当然这也是一种约定俗成的写法,想要去访问变量的值也是可以的

In [2]: for _ in range(3): print(_)                                                                                                                                          
0
1
2

还有一种用法是在交互式Python中,例如IPython,用单下划线表示上一次运算的结果

In [1]: 1+2                                                                                                                                                                  
Out[1]: 3

In [2]: _+1                                                                                                                                                                  
Out[2]: 4

In [3]: _+2                                                                                                                                                                  
Out[3]: 6

但是这样子的前提是没有对_进行赋值

In [2]: for _ in range(3): print(_)                                                                                                                                          
0
1
2

In [3]: 1+2+3                                                                                                                                                                
Out[3]: 6

In [4]: _+1                                                                                                                                                                  
Out[4]: 3

In [5]: _+1                                                                                                                                                                  
Out[5]: 3

总结

下面这个表格对上面五种下划线的写法进行了一个总结

格式例子说明
前置单下划线_var约定俗成的写法,非强制,表明该变量只用作内部使用
后置单下划线var_约定俗成的写法,非强制,用来解决名字冲突
前置双下划线__var强制,Python通过名字修饰使得变量不能被外部访问和继承
前后双下划线__var__约定俗成的写法,预留给特殊变量,不建议开发时使用
单下划线_约定俗成的写法,非强制,用来表示临时变量或者交互式环境中的上一次计算的结果
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值