python中的下划线含义

单下划线和双下划线在Python变量和方法名称中都有含义。其中一些含义仅仅是按照惯例,作为给程序员的提示,而另外一些则由Python解释器执行。

在本文中,主要讨论以下五种下划线模式和命名约定,以及它们如何影响Python程序的行为:

1.单前置下划线:_var 

2.单后置下划线:var_

3.双前置下划线:__var

4.双前置和后置下划线:__var__

5.单下划线:_

Quick Summary:


1.单前置下划线:_var 

单下划线前缀是一个提示,即以单个下划线开头的变量或方法将用于内部使用。PEP 8中定义了这种约定。Python不像java对公共变量和私有变量有着严格的区分,单下划线前缀不会影响程序的行为

class Test:
    def __init__(self):
        self.foo = 11
        self._bar = 23
>>> t = Test()
>>> t.foo
11
>>> t._bar
23

可以看到,_bar没有阻止进入类的内部并获取相应的变量值,在Python中,一个下划线前缀只是一种约定,至少在变量和方法名中是这样。

但是,前导下划线确实影响从模块导入名称的方式。假设有一个模块叫my_module:

# This is my_module.py:

def external_func():
    return 23

def _internal_func():
    return 42

如果使用通配符进行导入my_module中所有名称(在PEB 8中建议避免使用通配符的导入方式),则Python不会导入带有前导下划线的名称(除非模块定义了覆盖此行为的__all__列表):

>>> from my_module import *
>>> external_func()
23
>>> _internal_func()
NameError: "name '_internal_func' is not defined"

而常规的导入方式不会受到单前导下划线约定的影响:

>>> import my_module
>>> my_module.external_func()
23
>>> my_module._internal_func()
42

总结就是:单下划线是一种Python命名约定,指示名称用于内部使用。Python解释器通常不会强制执行它,只是作为对程序员的一个提示。

2.单后置下划线:var_

有时候一个变量最适合的名称已经被关键字所使用,在Python中类似def、class等关键字不能被用作变量名,可以在这些名称后面加单下划线以解决冲突。

>>> def make_object(name, class):
SyntaxError: "invalid syntax"

>>> def make_object(name, class_):
...     pass

总之,单后置下划线是为了与Python关键字命名冲突而约定的

3.双前置下划线:__var

前面两种命名模式都是一种约定,而双下划线前缀会导致Python解释器重写属性名,以避免子类中的命名冲突。这也被称为名称修饰( name mangling)——解释器在某种程度上更改变量名使得以后扩展类时更难产生冲突

class Test:
    def __init__(self):
        self.foo = 11
        self._bar = 23
        self.__baz = 23

可以用dir()方法看一下对象的属性:

>>> t = Test()
>>> dir(t)
['_Test__baz', '__class__', '__delattr__', '__dict__', '__dir__', 
 '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
 '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__',
 '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
 '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
 '__weakref__', '_bar', 'foo']

在这个列表里面没有__baz属性,但是可以看到有一个_Test__baz属性,这就叫做名称修饰,可以避免该属性被子类所覆盖。

现在创建一个类继承Test类,然后尝试覆盖Test类中的__baz属性:

class ExtendedTest(Test):
    def __init__(self):
        super().__init__()
        self.foo = 'overridden'
        self._bar = 'overridden'
        self.__baz = 'overridden'
>>> t2 = ExtendedTest()
>>> t2.foo
'overridden'
>>> t2._bar
'overridden'
>>> t2.__baz
AttributeError: "'ExtendedTest' object has no attribute '__baz'"

在执行t2.__baz时出现了AttributeError,我们看一下t2里面的属性:

>>> dir(t2)
['_ExtendedTest__baz', '_Test__baz', '__class__', '__delattr__',
 '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
 '__getattribute__', '__gt__', '__hash__', '__init__', '__le__',
 '__lt__', '__module__', '__ne__', '__new__', '__reduce__',
 '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
 '__subclasshook__', '__weakref__', '_bar', 'foo', 'get_vars']

可以发现,t2里面有_ExtendedTest__baz和_Test__baz,_Test__baz是继承自Test对象的属性,而_ExtendedTest__ba则是t2里面通过名称修饰得到的属性。

>>> t2._ExtendedTest__baz
'overridden'
>>> t2._Test__baz
42

双下划线前缀对编程者来说是完全透明的,如下面的一个例子:

class ManglingTest:
    def __init__(self):
        self.__mangled = 'hello'

    def get_mangled(self):
        return self.__mangled
>>> ManglingTest().get_mangled()
'hello'
>>> ManglingTest().__mangled
AttributeError: "'ManglingTest' object has no attribute '__mangled'"
>>> ManglingTest()._ManglingTest__mangled
'hello'

名称修饰也会应用到方法上:

class MangledMethod:
    def __method(self):
        return 42

    def call_it(self):
        return self.__method()
>>> MangledMethod().__method()
AttributeError: "'MangledMethod' object has no attribute '__method'"
>>> MangledMethod().call_it()
42
>>> MangledMethod()._MangledMethod__method()
42

下面是另一个名称修饰的例子:

_MangledGlobal__mangled = 23

class MangledGlobal:
    def test(self):
        return __mangled
>>> MangledGlobal().test()
23

在这里设置了一个全局变量_MangledGlobal__mangled,然后在创建一个MangledGlobal对象去调用test方法时,test方法返回的时_MangledGlobal__mangled,这说明名称修饰不是专门与类属性绑定的。它适用于类上下文中以两个下划线开头的任何名称

4.双前置和后置下划线:__var__

名称修饰并不会作用于双下划线前缀和后缀的变量:

class PrefixPostfixTest:
    def __init__(self):
        self.__bam__ = 42
>>> PrefixPostfixTest().__bam__
42

双前置和后置下划线命名在Python语言中保留为特殊用途,这条规则包括如__init__用于对象构造函数或__call__使对象可调用。最好避免在自己的代码中使用双下划线前缀和后缀的名称,以防止未来Python语言的新变化。

5.单下划线:_

根据惯例,一个单独的下划线有时用作一个名称,表示变量是临时的或不重要的。

>>> for _ in range(32):
...     print('Hello, World.')

可以在解包表达式时使用单个下划线作为不需要关心的变量,来忽略特定的值。同样,这个用法只是按照约定,在Python解释器中没有触发特殊的行为。单个下划线只是一个有效的变量名,有时用于此目的。

>>> car = ('red', 'auto', 12, 3812.4)
>>> color, _, _, mileage = car

>>> color
'red'
>>> mileage
3812.4
>>> _
12

除了用作临时变量之外,在大多数Python REPLs中,“_”是一个特殊的变量,表示解释器对最后一个表达式求值的结果

>>> 20 + 3
23
>>> _
23
>>> print(_)
23

>>> list()
[]
>>> _.append(1)
>>> _.append(2)
>>> _.append(3)
>>> _
[1, 2, 3]

Quick Summary:

译自:The Meaning of Underscores in Python

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值