python-神奇的下划线

jacktangj 2019-12-16

2019-12-16 22:45:29

python中下划线有各种各样的作用,本章就来分别介绍一下各种下划线的功能。

一、开头单下划线 _VAR

开头单下划线还是挺常用的,在类中表示为保护变量/保护函数,但是需要注意的是,这个只是一种约定俗成,这不是Python强制规定的。

换言之,你依然可以访问一个类中以单下划线开头的变量或者方法而不报错。

class a:
    def __init__(self):
        self.a = 1
        self._b = 2

    def _func(self):
        print("Hello world.")
        

if __name__ == "__main__":
    aa = a()
    print(aa.a)
    print(aa._b)
    aa._func()

上述的代码运行是完全没有任何的问题的,这是因为Python中的单个下划线前缀仅仅是一个约定 - 至少相对于变量和方法名而言。

但是,前导下划线的确会影响从模块中导入名称的方式。

假设你在一个名为my_module的模块中有以下代码:

# This is my_module.py:

def external_func():
   return 23

def _internal_func():
   return 42

现在,如果使用通配符从模块中导入所有名称,则Python不会导入带有前导下划线的名称:

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

当然,如果不是使用通配符进行的导入,那么依然是不会出现任何的问题的,事实上,我们也不建议使用通配符的方式来进行方法的导入。

二、开头双下划线 __VAR

上述的单下划线开头有点类似于君子协定,看到了就是告诉你这个是私有的,不能外部访问的,但是实际上你要外部访问也没辙。但是,开头双下划线的就不一样了,这个在python中有固定的使用方式的。

双下划线前缀会导致Python解释器重写属性名称,以避免子类中的命名冲突。

python会对以双下划线开头的变量或者方法的name进行重写(_ClassName__VAR),在外部访问的时候如果直接按照变量名访问就会报错。

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

    def get_var(self):
        print(self.__baz) # 内部可以直接访问
    
    def __method(self):
        print("This is method.")


if __name__ == "__main__":
    a = Test()

    # print(a.__baz) # AttributeError: ‘Test‘ object has no attribute ‘__baz‘
    print(a._Test__baz)

    # 对内透明
    a.get_var()

    # a.__method() # AttributeError: ‘Test‘ object has no attribute ‘__method‘
    a._Test__method()

三、开头末尾双下划线

如果一个名字同时以双下划线开始和结束,则不会应用名称修饰。 由双下划线前缀和后缀包围的变量不会被Python解释器修改:

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

>>> PrefixPostfixTest().__bam__
42

但是,Python保留了有双前导和双末尾下划线的名称,用于特殊用途。 这样的例子有,__init__对象构造函数,或__call__ --- 它使得一个对象可以被调用。

这些dunder方法通常被称为神奇方法 - 但Python社区中的许多人(包括我自己)都不喜欢这种方法。

最好避免在自己的程序中使用以双下划线(“dunders”)开头和结尾的名称,以避免与将来Python语言的变化产生冲突。

相关推荐