一个在交流群里讨论过两轮的问题,答案竟然跟一个 PEP 有关

简介: 这个问题的关键是要使用在 Python 3.3 中引入的__qualname__ 属性,通过它可以获取上层类的名称。铺垫了这么多,开始进入本文的正题了:__qualname__ 属性是什么东西?为什么 Python 3 要特别引入它呢?

Python 中有没有办法通过类方法找到其所属的类?

这个问题看起来不容易理解,我可以给出一个例子:

class Test:
    @xxx
    def foo(self):
        pass
复制代码

现在有一个类和一个类方法,其中类方法上有一个装饰器。

我们的问题就是要在装饰器代码中动态地获得 Test 这个类(类名+类对象)。

这个问题的关键是要使用在 Python 3.3 中引入的__qualname__ 属性,通过它可以获取上层类的名称。

铺垫了这么多,开始进入本文的正题了:__qualname__ 属性是什么东西?为什么 Python 3 要特别引入它呢?

原理

一直以来,对于嵌套类的自省,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),通向顶层模块下的对象。函数的局部命名空间在点式路径中由名为  的组件表示。

函数和类的 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 解决了的部分问题,而且代价更高(额外的对象类型和额外的间接寻址,不如用额外的属性)。

目录
相关文章
|
8月前
|
Go 索引
「连载」最最通俗易懂,并带小说情节的Go语言入门教程(连载四),没有之一,不接受反驳
「连载」最最通俗易懂,并带小说情节的Go语言入门教程(连载四),没有之一,不接受反驳
|
8月前
|
Go Windows
最最通俗易懂,并带小说情节的Go语言入门教程(连载二),没有之一,不接受反驳
最最通俗易懂,并带小说情节的Go语言入门教程(连载二),没有之一,不接受反驳
|
8月前
|
程序员 Go
最最通俗易懂,并带小说情节的Go语言入门教程,没有之一,不接受反驳
最最通俗易懂,并带小说情节的Go语言入门教程,没有之一,不接受反驳
|
8月前
|
Kubernetes Cloud Native JavaScript
「连载」最最通俗易懂,并带小说情节的Go语言入门教程(连载三),没有之一,不接受反驳
「连载」最最通俗易懂,并带小说情节的Go语言入门教程(连载三),没有之一,不接受反驳
|
11月前
|
机器学习/深度学习 人工智能 供应链
如何正确的提问,获得最有效的回答帮助?
如何提高程序员的工作效率? 如何在云计算中实现智能化? 如何使用人工智能技术改善医疗保健行业? 如何保护个人隐私在数字化时代? 如何应对人工智能带来的就业变革? 如何构建高可靠性的云计算系统? 如何使用区块链技术改善供应链管理? 如何实现可持续的数字化转型? 如何使用机器学习技术预测未来趋势? 如何构建安全可靠的物联网系统?
237 1
|
SQL 安全 搜索推荐
提问的艺术:如何让别人喜欢回答你的提问
提问的艺术:如何让别人喜欢回答你的提问
299 0
提问的艺术:如何让别人喜欢回答你的提问
|
计算机视觉 Python
交流群里讨论热烈的题目 用Python轻松搞定它!
学编程,很多题目可能并不像看起来那样简单,实践出真知,动手才能发现问题,多思考才能解决问题,切忌眼高手低!
108 0
交流群里讨论热烈的题目 用Python轻松搞定它!
|
自然语言处理 JavaScript 前端开发
2018-07-18 万马齐喑究可哀-中文编程的又一波"讨论"
对问题"假设中国人最先开发电脑和设计程序语言,那么各种程序语言会使用汉字吗?"的回应. A response to the question "what if the Chinese invented computer first?"
808 0
|
算法 Java 程序员
20位程序员关于求职的疑问,以及我给出的参考答案
作者:陆小凤 首发:公众号【程序员江湖】 阅读本文大概需要 6 分钟。 前几天发了一条朋友圈对于求职小伙伴们提出的问题,我进行了收集整理,统一反馈。也许这20个问题也是你们遇到的问题,所以趁着年前赶紧把它发出来。
|
程序员
如何用一段简单的代码讲述一个悲伤的故事?
程序员的悲伤故事难道不应该是: 别人的老板晚上带他出去耍,你的老板半夜催你改代码; 别的程序员工资高、待遇好,而你只是血压高、心态好…… 擦干眼泪告诉自己:程序员前半生的悲伤都不是事儿,因为后半生你就慢慢习惯了。
939 0