Python 隐藏法宝:双下划线 _ _Dunder_ _
昨天晚上在家里写代码写得脑壳发麻,结果小侄子跑过来问我:“叔叔,为什么你老敲那两个下划线啊,看着像打错了一样。”我一听差点没笑出来,但想了想,这问题其实不少刚学 Python 的人也都迷糊过。今天正好趁脑子还有点清醒,来聊聊这个 Python 里的隐藏小机关——双下划线,也叫 Dunder(Double Underscore)方法或者属性。
Dunder 是什么鬼?
这个词听起来跟哈利波特有点像,但其实是 Python 自己的一套“暗号”。你看到那种像 __init__、__str__ 这样包着两根下划线的东西,大多数时候,它不是你写错了,而是 Python 在搞特殊处理。这类名字基本都是给解释器看的,或者说是给类系统预留的钩子。
举个例子哈,比如最熟的 __init__:
classPerson:
def__init__(self, name):
self.name = name
你 new 一个对象的时候,不是用什么 init 方法,而是 Person("小明"),这时候其实就是 Python 自动调用了 __init__,你手动去调也行,但没人这么干。
那 __str__ 呢?
这个我真是踩坑踩过,比如你写个类,然后 print 它的时候,看到的是一串 <__main__.Person object at 0x7f7c03>,感觉就像 Python 在嘲讽你“你连 __str__ 都不写?”
classPerson:
def__init__(self, name):
self.name = name
def__str__(self):
returnf"这个人叫 {self.name}"
这样你 print 的时候,就能看出点人味儿了。
真正的隐藏玩法是“名称重整”
这个才是最阴的事儿。比如我写一个类,里面搞一个双下划线开头的变量,比如:
classSecret:
def__init__(self):
self.__token = "123456"
你以为你在类外面 print(obj.__token) 会拿到值?错!你会被打脸的。
其实 Python 并不是让你完全不能访问这个变量,它只是偷偷改了个名字,在内部变成了 _Secret__token,你试试:
obj = Secret()
print(obj._Secret__token) # 成功输出 123456
是不是有点像在公司代码里偷偷藏了个 backdoor……
为什么不推荐乱用?
因为这玩意其实没那么“私有”,它只是被“改了名字”。而且乱用容易坑死你团队的其他人,尤其是继承的时候,比如你子类想重写一个双下划线开头的方法,发现压根覆盖不了,因为名字被重命名了!
classA:
def__hello(self):
print("A")
classB(A):
def__hello(self):
print("B")
B()._B__hello() # 打印 B
B()._A__hello() # 打印 A
卧槽是不是看着就头大。
那单下划线呢?
单下划线比如 _name,就只是个“约定俗成”的私有,其实你想访问也能访问。它就是告诉你“喂,这个变量是内部用的哦,别乱碰”,但不是强制的。
什么时候该用双下划线?
就两种情况我觉得比较合理:
类库作者写库的时候,真怕别人乱碰内部接口。 某些特殊钩子函数,你不写不行,比如 __len__、__getitem__这种,要让对象支持len(obj)或obj[0],你就得写。
比如这样:
classMyList:
def__init__(self, data):
self._data = data
def__getitem__(self, index):
return self._data[index]
def__len__(self):
return len(self._data)
然后你可以这样玩:
lst = MyList([1, 2, 3])
print(lst[0]) # 输出 1
print(len(lst)) # 输出 3
很丝滑有没有。
不要被双下划线唬住,它不是魔法,只是语法糖背后的“潜规则”。如果你能读懂这些“糖衣炮弹”,你写 Python 就真的开始上道了。
-END-
我为大家打造了一份RPA教程,完全免费:https://www.songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB,点击下方公众号回复关键字 python 全部免费领