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__的區別

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