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。