python的_xxx, __xxx, __xxx__区别

_xxx

弱“内部使用”标识
如:”from M import *”,将不导入所有以下划线开头的对象,包括包、模块、成员。
表明该方法或属性不应该去调用
Python中不存在真正的私有方法。为了实现类似于c++中私有方法,可以在类的方法或属性前加一个“_”单下划线,意味着该方法或属性不应该去调用,它并不属于API。

xxx_

只是为了避免与python关键字的命名冲突

__xxx

伪私有方法

class A(object):
    def __init__(self):
        self.name = 'A'
    def __method(self):
        print("I'm a method in A")

a = A()
a.__method()

输出:

AttributeError: 'A' object has no attribute '__method'

虽然看上去在像是私有函数,在外部无法访问,但是其实python只是简单地给__method()改了下名字,仍然可以这样访问:

a._A__method()

输出:

I'm a method in A

了解了这么一个改名的机制,再看下面这个例子:

class BaseClass(object):
    def __init__(self):
        self.name = 'Base'
    def __method(self):
        print("I'm Base")
    def method(self):    
        self.__method()

class SubClass(BaseClass):
    def __init__(self):
        super(SubClass, self).__init__()
        self.name = 'Sub'
    def __method(self):  # 覆写父类__method()
        print("I'm Sub")
    def method(self):    # 覆写父类method()
        self.__method()


sub = SubClass()
sub.method()

此时的输出为:

I'm Sub

没问题,子类覆写了父类的method()方法,因此输出是”Sub”。如果不覆写:

class SubClass(BaseClass):
    def __init__(self):
        super(SubClass, self).__init__()
        self.name = 'Sub'
    def __method(self):  # 覆写父类__method()
        print("I'm Sub")
    #def method(self):     没有覆写父类method()
    #    self.__method()

调用sub.method()时,输出:

I'm Base

可以发现,子类调用的是从父类继承下来的method()方法,从而调用的自然是父类的__method__()

其实如果从改名的角度来思考,很容易理解。
在写每个类代码的时候,虽然我们写的都是:

def method(self):    
    self.__method()

但是BaseClass直接把代码改成了:

def method(self):    
    self._BaseClass__method()

对应着SubClass:

def method(self):    
    self._SubClass__method()

而继承可以理解成,如果子类没有覆写,则复制父类的代码下来。因而就出现了,调用sub.method()时,其实是调用了父类的_BaseClass__method(),输出BaseClass的情况。

为了加强继承的复制代码与改名机制的理解,我又写了如下代码

class BaseClass(object):
    def __init__(self):
        self.name = 'Base'
    def __method(self):
        print("I'm Base")
    def method(self):
        self._BaseClass__method()
        self._SubClass__method()

base = BaseClass()
base.method()

输出为:

I'm Base
AttributeError: 'BaseClass' object has no attribute '_SubClass__method'

对于父类来说,它的类方法只有_BaseClass__method(),因此调用_SubClass__method()时报错。
接着,运行如下代码:

class BaseClass(object):
    def __init__(self):
        self.name = 'Base'
    def __method(self):
        print("I'm Base")
    def method(self):
        self._BaseClass__method()
        self._SubClass__method()

class SubClass(BaseClass):
    def __init__(self):
        super(SubClass, self).__init__()
        self.name = 'Sub'
    def __method(self):
        print("I'm Sub")

sub = SubClass()
sub.method()

输出:

I'm Base
I'm Sub

对子类来说,继承的时候首先复制了父类的代码,因此父类的method()方法自然就被复制下来。然而,虽然父类的__method()方法和子类的__method()方法名字一样,但是由于改名机制的存在,其实两个函数的名字并不同,因此并不存在覆写的情况,而是SubClass不仅自己定义了一个_SubClass__method(),同时复制了父类的_BaseClass__method()。所以这里调用时输出是上面这样而不会报错。

__xxx__

“魔术”对象或属性
许多特殊的代码会调用对应的魔术方法。下面是总结的部分我遇到过的魔术方法:

__init__()

这个很常见,就是创建一个类的实例的时候会调用,相当于构造函数。

__call__()

如果一个类实现了这个函数,那么这个类就是“可调用对象”,可以通过如下方式调用:

class C:
    def __call__(self, name):
        print(name)

c = C()
c('Hello!')  # 会调用__call__()方法

输出:

Hello!

__getitem__()

class C:
    def __init__(self):
        self.list = [1,2,3,4,5]

    def __getitem__(self, item):
        return self.list[item]

c = C()

for i in range(5):
    print(c[i]) # 会调用__getitem__()方法

__iter__()__next__()

之所以把这两个放在一起,是因为我见到的这两个是在迭代器操作的时候同时出现的。直接看代码:

class IterDemo:
    def __iter__(self):
        print('__iter__() is called')
        return NextDemo()   # 需要返回一个实现了__next__()方法的类的对象

class NextDemo:
    def __next__(self):
        print('__next__() is called')

iterDemo = IterDemo()

nextDemo = iter(iterDemo)   # 调用iterDemo.__iter__()方法
next(nextDemo)              # 调用nextDemo.__next__()方法

__setattr__()

用在类内给成员变量赋值。

class C:
    def __init__(self):
        self.a = 1        #调用一次__setattr__()

    def __setattr__(self, key, value):
        dict = self.__dict__
        dict[key] = value
        print("__setattr__ !")

    def modify(self,new_a):
        self.a = new_a    #调用一次__setattr__()

c = C()
c.modify(2)

输出:

__setattr__ !
__setattr__ !

参考:

Python下划线与命名规范
python_____xx__的区别
pytorch学习笔记(四):输入流水线(input pipeline)
Python3关与迭代器next()使用为__next__()的一点注意事项

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值