一个在交流群里讨论过两轮的问题,答案竟然跟一个 PEP 有关
Python 中有没有办法通过类方法找到其所属的类?
这个问题看起来不容易理解,我可以给出一个例子:
class Test:
@xxx
def foo(self):
pass
现在有一个类和一个类方法,其中类方法上有一个装饰器。
我们的问题就是要在装饰器代码中动态地获得 Test 这个类(类名+类对象)。
去年 11 月份的时候,我在微信读者群里提出了这个问题,当时引起了小范围的讨论。
没想到在今年上个月的时候,群里又有人提了同样的问题(我在讨论结束后才看到),而且最终都找到了 stackoverflow 上一个同样的问题:

stackoverflow 上的问题提得很明确:Get defining class of unbound method object in Python 3 。但是 unbound method 的叫法已经不常见了,详细的讨论也就不展开了,感兴趣的同学可以去查阅。
这个问题的关键是要使用在 Python 3.3 中引入的__qualname__ 属性,通过它可以获取上层类的名称。
铺垫了这么多,开始进入本文的正题了:__qualname__ 属性是什么东西?为什么 Python 3 要特别引入它呢?
下文是 PEP-3155 的翻译摘录,清楚地说明了这个属性的来龙去脉。
完整内容可在 Github 仓库查看:https://github.com/chinesehuazhou/peps-cn/blob/master/StandardsTrack/3155--%E7%B1%BB%E5%92%8C%E6%96%B9%E6%B3%95%E7%9A%84%E7%89%B9%E5%AE%9A%E5%90%8D%E7%A7%B0.md
-------------------摘录开始--------------------
原理
一直以来,对于嵌套类的自省,Python 的支持很不够。给定一个类对象,根本不可能知道它是在某个类中定义的,还是在顶层模块中定义的;而且,如果是前者,也不可能知道它具体是在哪个类中定义的。虽然嵌套类通常被认为是不太好的用法,但这不应该成为不支持内层自省的理由。
Python 3 因为丢弃了以前的未绑定方法(unbound method),而受到了侮辱性的伤害。
在 Python 2 中,给出以下定义:
class C:
def f():
pass
你可以从C.f 对象中获得其所属的类:
>>> C.f.im_class
<class '__main__.C'>
这种用法在 Python 3 中已经没有了:
>>> C.f.im_class
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'im_class'
>>> dir(C.f)
['__annotations__', '__call__', '__class__', '__closure__', '__code__',
'__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__get__', '__getattribute__',
'__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__',
'__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__']
这就限制了用户可以使用的自省能力。当将程序移植到 Python 3 时,它可能会产生一些实际的问题,例如在 Twisted 的核心代码中,就多次使用到了这种自省方法。此外,这还限制了对 pickle 序列化的支持 。
提议
本 PEP 提议在函数和类中添加 __qualname__ 属性。
对于顶层的函数和类,__qualname__ 属性等于__name__ 属性。对于嵌套的类、方法和嵌套函数,__qualname__ 属性包含一个点式路径(dotted path),通向顶层模块下的对象。函数的局部命名空间在点式路径中由名为 <locals> 的组件表示。
函数和类的 repr() 和 str() 被修改为使用__qualname__ 而不再是__name__。
嵌套类的示例
>>> class C:
... def f(): pass
... class D:
... def g(): pass
...
>>> C.__qualname__
'C'
>>> C.f.__qualname__
'C.f'
>>> C.D.__qualname__
'C.D'
>>> C.D.g.__qualname__
'C.D.g'
嵌套函数的示例
>>> def f():
... def g(): pass
... return g
...
>>> f.__qualname__
'f'
>>> f().__qualname__
'f.<locals>.g'
不足之处
对于嵌套函数(以及在函数内部定义的类),由于无法从外部获得函数的命名空间,因此点式路径无法以动态编程的方式遍历。相比于空的__name__,它对于人类读者还是有些帮助的。
跟__name__属性一样,__qualname__ 属性是静态计算的,不会自动地重新绑定。
讨论
去除模块名称
跟__name__一样,__ qualname__ 不包含模块的名称。这使得它不受制于模块别名和重新绑定,也得以在编译期进行计算。
恢复 unbound 方法
恢复 unbound 方法只能解决此 PEP 解决了的部分问题,而且代价更高(额外的对象类型和额外的间接寻址,不如用额外的属性)。
-------------------摘录结束--------------------
后记
去年我在阅读ddt 库关于参数化测试的源码 时,偶然想到了文章开头的问题,但是没有作进一步的梳理(似乎感兴趣的人也不多)。没想到的是在群里又出现了同样的讨论,这让我意识到这个问题是有价值的。
前几天,我偶然间发现__qualname__ 属性有一个专门的 PEP,所以我就抽空把它翻译出来了——既是一种知识梳理,也是给大家做一个“科普”吧。说不定什么时候,还有人会遇到同样的问题呢,希望对大家有所帮助。
更多的 PEP 中文翻译内容,可在 Github 查阅:https://github.com/chinesehuazhou/peps-cn
一个在交流群里讨论过两轮的问题,答案竟然跟一个 PEP 有关的更多相关文章
- python学习总结---学习交流群里的问题总结
xml里面的过滤: <record id="action_partner_supplier_form_demo_ms" model="ir.actions.act_ ...
- 为什么 1KB = 1024Byte???群里讨论。
- 我们建了一个 Golang 硬核技术交流群(内含视频福利)
目录 目录 Golang 是什么? 我们为什么选择 Golang? Golang 是云时代的宠儿! 我们搞了一场 Golang 入门直播 Golang 是什么? Golang 是谷歌 2009 年发布 ...
- 新开了一个ABP交流的QQ群(579765441 ),欢迎加入
因为ABP架构设计交流群人数一直爆满,很多想交流ABP的朋友无法加进群里, 刚新建了一个QQ群,群号579765441 (ABP架构设计交流群2),欢迎对ABP感兴趣的朋友加入. 欢迎加QQ群: AB ...
- 2017-8-2新开了一个ABP交流的QQ群(291304962 ),欢迎加入
因为ABP架构设计交流群人数一直爆满,很多想交流ABP的朋友无法加进群里, 刚新建了一个QQ群,群号291304962(ABP架构设计交流群3),欢迎对ABP感兴趣的朋友加入. 欢迎加QQ群: ABP ...
- 欢迎加入.NET Core 技术QQ群一起讨论交流学习
群号:4656606 介绍:本群主要讨论.NET Core及其相关技术,如:IdentityServer4.ABP.Dcoker.Linux.Devops.微服务等,如果你正在使用或者准备使用.NET ...
- 剑指offer40:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字
1 题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 2 思路和方法 (1)异或:除了有两个数字只出现了一次,其他数字都出现了两次.异或运算中,任 ...
- 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制
你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...
- 群里分享的react的收藏一下!今日周末,改了个表单验证然后无所事事了!
今日周末,改了个表单验证然后无所事事了,然后把昨天群里分享的react的收藏一下尽管现在还在研究angular和nodeJs毕竟刚刚开始用有点不熟...没准以后会研究一下react毕竟看着下面这张图还 ...
随机推荐
- vue : 在vuex里写一个数组首尾元素互换的方法
不着急上代码,先想几个问题. vuex里怎么写方法? mutation里写vuex方法,组件中用commit调用. 数组首尾元素怎么互换? arr.splice(0, 0, arr[arr.lengt ...
- Maven原理学习
文章目录 一.Maven概述 二.maven依赖管理 三.maven文件结构 四.maven仓库的种类以及彼此联系 五.maven标准目录结构 六.mvn命令 七.maven生命周期 八.maven的 ...
- 设计模式:strategy模式
思想:将算法进行抽象,然后使用桥接的模式使用算法的抽象接口,达到算法整体替换的目的 理解:和桥接模式相同,只是桥接的两边分开的思想不同 例子: class Algrithm //算法的抽象 { pub ...
- 加班两个星期做的一个小系统~(winform)
不管怎么样~加班两个星期,单独一人,努力将公司需要用的系统给做出来了,也感谢提供技术帮助的可爱人儿~ 首先,系统有个检测版本的功能,若版本不是最新的,则会自动更新(公司要求,必须强制更新)~ 更新界面 ...
- 黎曼函数ζ(2n)的几种求法
\(\zeta (2n)\)的几种求法 目录 $\zeta (2n)$的几种求法 结论 欧拉的证明 进一步探索,$\zeta$ 函数.余切.伯努利数的关系 傅立叶分析证明 留数法证明 参考资料 结论 ...
- 如何在CSDN博客开头处加上版权声明?
1.首先在CSDN账号头像处打开"管理博客"选项. 2.然后在管理博客界面左侧找到设置下面的"博客设置"选项. 3.将博客设置里的"版权声明" ...
- Mysql Backup
1.完全备份# innobackupex --user=DBUSER --password=DBUSERPASS /path/to/BACKUP-DIR/如果要使用一个最小权限的用户进行备份,则可基于 ...
- 关于RecyclerView(一)基本使用
前言 最近在项目中用到了ListView,不知道为什么总是出现数据错乱的情况,另外RecyclerView包含很多Item动画,所以改成了RecyclerView. 简单使用 RecyclerView ...
- 使用opencv为没有透明通道的图像加入透明通道
在图像处理中,我们经常需要处理带透明通道的图片,比如为图片或视频添加水印,为图片或视频添加字幕.贴图等.然而,我们的素材图片未必总是带有透明通道.比如,素材的背景本该透明的地方,却是黑色和白色.有时, ...
- PHP vsprintf() 函数
实例 把格式化字符串写入变量中: <?php高佣联盟 www.cgewang.com$number = 9;$str = "Beijing";$txt = vsprintf( ...