python中_、__和__xx__的区别

0 前言

python灵活的语法,给我们带了一些便利,同时也给我们带了一些困惑。其中就是我们常见的_、__和__xx__的作用和区别,这节我们就来看一下。

1 理论

  1. Python中不存在真正的私有方法。为了实现类似于c++中私有方法,可以在类的方法或属性前加一个“_”单下划线,意味着该方法或属性不应该去调用,它并不属于API。但是,这只是一个形式上的约定,python并不阻止调用。
  2. __双下划线的作用是避免覆盖其内容,实现的机制是在带有双下划线的方法或属性前加上_类名的标识。由于,python自动对方法和属性进行了改写,所以直接调用带有双下划线的方法是调用不到的。
  3. xx”经常是操作符或本地函数调用的magic methods。在上面的例子中,提供了一种重写类的操作符的功能。它是用于Python调用的。

2 例子

首先定义两个类:

class Student(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def _getname(self): #单下划线
        print('返回名字')
        return self.name
    def __getage(self): #双下划线
        print('Stuent:返回年龄')
        return self.age

    def __str__(self):
        return '{}:{}'.format(self.name, self.age)
class UnderStudent(Student):
    def __init__(self, name, age, university):
        super(UnderStudent,self).__init__(name, age)
        self.university = university

    def __getage(self):
        print('UnderStudent:返回年龄')
        return self.age

    def __str__(self):
        return '{}:{}:{}'.format(self.name, self.age, self.university)

在这里插入图片描述
可以看到带有单下划线的方法,可以在类外直接调用,这说明它不是真正的私有方法,只是一个约定。
带有双下划线的方法,直接调用会提示找不到该方法。我们使用dir函数来显示类所有的属性看看。

print(dir(Student))
print(dir(UnderStudent))
['_Student__getage', '__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__', '_getname']
['_Student__getage', '_UnderStudent__getage', '__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__', '_getname']

通过输出内容可以看到,在Student类中__getage被改写为_Student__getage,UnderStudent继承了_Student__getage。由于我们在UnderStudent中也定义了__getage,他被改写为_UnderStudent__getage,避免了对父类Student__getage的覆盖。 可见双下划线的方法在类外是不能调用的,因为它的名字已经被改变。
既然知道改写后的方法名,我们就可以直接使用改写后的方法名进行调用了。

if __name__ == '__main__':
    s = Student('张三', 20)
    print(s)
    print(s._getname())
    # print(s.__getage())
    print(s._Student__getage())

    print('------------------------------------')
    us = UnderStudent('李四', 22, '哈工大')
    print(us)
    print(us._getname())
    # print(us.__getage())
    print(us._Student__getage())
    print(us._UnderStudent__getage())

在这里插入图片描述
既然双下划线的属性在外部不能调用,那么这种方法有什么用呢?作用是,这种方法可以在类内部被调用,并且不能被子类的方法覆盖。
我们为Student类,添加message属性

class Student(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def _getname(self):
        print('返回名字')
        return self.name
    def __getage(self):
        return 'Student:{}'.format(self.age)

    def message(self):
        print('In Student')
        return self.__getage()

    def __str__(self):
        return '{}:{}'.format(self.name, self.age)

class UnderStudent(Student):
    def __init__(self, name, age, university):
        super(UnderStudent,self).__init__(name, age)
        self.university = university
    def __getage(self):
        return 'UnderStudent:'.format(self.age)

    def __str__(self):
        return '{}:{}:{}'.format(self.name, self.age, self.university)

if __name__ == '__main__':
    s = Student('张三', 20)
    print(s)
    print(s._getname())
    print(s.message())


    print('------------------------------------')
    us = UnderStudent('李四',22,'哈工大')
    print(us)
    print(us._getname())
    print(us.message())

在这里插入图片描述
在method方法中调用了__getage,虽然在UnderStudent对__getage方法进行了改写,但是us对象调用的仍然是Student的__getage方法。
下面再来看,如果在UnderStudent也添加message方法呢?

class Student(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def _getname(self):
        print('返回名字')
        return self.name
    def __getage(self):
        return 'Student:{}'.format(self.age)

    def message(self):
        print('In Student')
        return self.__getage()

    def __str__(self):
        return '{}:{}'.format(self.name, self.age)

class UnderStudent(Student):
    def __init__(self, name, age, university):
        super(UnderStudent,self).__init__(name, age)
        self.university = university
    def __getage(self):
        return 'UnderStudent:{}'.format(self.age)

    def message(self):
        print('In UnderStudent')
        return self.__getage()

    def __str__(self):
        return '{}:{}:{}'.format(self.name, self.age, self.university)

if __name__ == '__main__':
    s = Student('张三', 20)
    print(s)
    print(s._getname())
    print(s.message())


    print('------------------------------------')
    us = UnderStudent('李四',22,'哈工大')
    print(us)
    print(us._getname())
    print(us.message())

在这里插入图片描述
这时候us调用message,message调用的就是UnderStudent的__getage方法了。

下面再看一个关于__xx__方法的例子:

class CrazyNumber(object):
    def __init__(self, n): 
        self.n = n 
    def __add__(self, other): 
        return self.n - other 
    def __sub__(self, other): 
        return self.n + other 
    def __str__(self): 
        return str(self.n) 

num = CrazyNumber(10) 
print num # 10
print num + 5 # 5
print num - 20 # 30

这个很好理解就不解释了。

总结

  1. 单下划线,形式上约定为私有方法,单python并不阻止调用。
  2. __双下划线的作用是避免覆盖其内容,实现的机制是在带有双下划线的方法或属性前加上_类名的标识。
  3. xx”用于Python调用的操作符或本地函数调用的magic methods。

参考文章:python _、__和__xx__的区别

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章