0 前言
python靈活的語法,給我們帶了一些便利,同時也給我們帶了一些困惑。其中就是我們常見的_、__和__xx__的作用和區別,這節我們就來看一下。
1 理論
- Python中不存在真正的私有方法。爲了實現類似於c++中私有方法,可以在類的方法或屬性前加一個“_”單下劃線,意味着該方法或屬性不應該去調用,它並不屬於API。但是,這只是一個形式上的約定,python並不阻止調用。
- __雙下劃線的作用是避免覆蓋其內容,實現的機制是在帶有雙下劃線的方法或屬性前加上_類名的標識。由於,python自動對方法和屬性進行了改寫,所以直接調用帶有雙下劃線的方法是調用不到的。
- “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
這個很好理解就不解釋了。
總結
- 單下劃線,形式上約定爲私有方法,單python並不阻止調用。
- __雙下劃線的作用是避免覆蓋其內容,實現的機制是在帶有雙下劃線的方法或屬性前加上_類名的標識。
- “xx”用於Python調用的操作符或本地函數調用的magic methods。