一个在交流群里讨论过两轮的问题,答案竟然跟一个 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毕竟看着下面这张图还 ...
随机推荐
- Captura - 免费好用还开源的录屏软件
首先下载这个软件,国内下载很慢这里提供一个国内下载UCloud-OSS 软件打开后默认英文,现在我们切换到中午模式 在录制屏幕的同时获取声音
- ThreadLocal 原理
ThreadLocal是什么 ThreadLocal是一个本地线程副本变量工具类.主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用, ...
- 部署SpringBoot到阿里云
目录 安装Mysql 1. 下载命令 2. 进行repo的安装: 3. 安装mysql 部署SpringBoot到阿里云服务器 1.IDEA下载插件 2.进入 Preference 配置一个 Acce ...
- python Scrapy 从零开始学习笔记(二)
在之前的文章中我们简单了解了一下Scrapy 框架和安装及目录的介绍,本章我们将根据 scrapy 框架实现博客园首页博客的爬取及数据处理. 我们先在自定义的目录中通过命令行来构建一个 scrapy ...
- IO—》File类
IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再把这些数据 ...
- this指向面试题两则
面试题1 let len = 10; function fn() { console.info(this.len) } fn(); // A let Person = { len: 5, say: f ...
- 数据结构C语言实现----顺序查找
建立上图的一个txt文件: 1004 TOM 1001002 lily 951001 ann 931003 lucy 98 用一个c程序读入这个表一个结构体数组中: 结构体如下: //学生数据结构体 ...
- 巩固复习(Django最基础的部分_具体查看官方文档)
Django学习路1 1.脚本不能随便运行,没准 linux 运行完就上不去了 2.pip 在 linux 上 写 pip3 同理 python 写为 python3 3.在 pycharm 上安装库 ...
- PHP isset() 函数
isset() 函数用于检测变量是否已设置并且非 NULL.高佣联盟 www.cgewang.com 如果已经使用 unset() 释放了一个变量之后,再通过 isset() 判断将返回 FALSE. ...
- P1429 平面最近点对[加强版] 随机化
LINK:平面最近点对 加强版 有一种分治的做法 因为按照x排序分治再按y排序 可以证明每次一个只会和周边的六个点进行更新. 好像不算很难 这里给出一种随机化的做法. 前置知识是旋转坐标系 即以某个点 ...