Python 反射含有协程方法类的问题

2018-06-18 12:05:54 +08:00
 conn4575

项目有个需求要动态获取模块内的所有协程方法,查了点资料发现 python 里面反射对象可以使用getattr()和对象内置的__dict__属性两种方式,但是我发现两种方式再处理静态的协程方法时结果不一样:

a = __import__('a')
print('===========getattr()=============')
func_sync = getattr(a, 'func_sync')
func_async = getattr(a, 'func_async')
print(func_sync)  # <function func_sync at 0x7f827b98f510>
print(func_async)  # <function func_async at 0x7f8279cd01e0>
print(asyncio.iscoroutinefunction(func_async))  # True
# getattr class
A = getattr(a, 'A')
print(A)  # <class 'a.A'>
method_aa = getattr(A, 'aa')
method_bb = getattr(A, 'bb')
method_cc = getattr(A, 'cc')
print(method_aa)  # <bound method A.aa of <class 'a.A'>>  <----这里的 bound method 是什么意思?
print(method_bb)  # <function A.bb at 0x7f8279cd00d0>
print(method_cc)  # <function A.cc at 0x7f8279cd0158>
print(asyncio.iscoroutinefunction(method_aa))  # True  <---- 注意这里
print(asyncio.iscoroutinefunction(method_bb))  # True
print(asyncio.iscoroutinefunction(method_cc))  # False
print('=========== __dict__ =============')
A = a.__dict__['A']
func_sync = a.__dict__['func_sync']
func_async = a.__dict__['func_async']
print(asyncio.iscoroutinefunction(func_async))  # True
print(A)  # <class 'a.A'>
method_aa = A.__dict__['aa']
method_bb = A.__dict__['bb']
method_cc = A.__dict__['cc']
print(method_aa)  # <classmethod object at 0x7f827a21c908>  <---- 变成了 classmethod
print(method_bb)  # <function A.bb at 0x7f8279cd00d0>
print(method_cc)  # <function A.cc at 0x7f8279cd0158>
print(asyncio.iscoroutinefunction(method_aa))  # False <----- 不应该是 True 吗?
print(asyncio.iscoroutinefunction(method_bb))  # True
print(asyncio.iscoroutinefunction(method_cc))  # False

我感觉__dict__getattr()在反射对象的机制上应该有一些区别,但是 google 了半天也没搞明白为什么,求指教!

a.py

class A:

    @classmethod
    async def aa(cls):
        return 123

    async def bb(self):
        return 456

    def cc(self):
        return 789


def func_sync():
    return 'sync'


async def func_async():
    return 'async'
2706 次点击
所在节点    Python
7 条回复
wwqgtxx
2018-06-18 13:20:41 +08:00
个人猜测,应该原因在这里:

asyncio.iscoroutinefunction:
return (getattr(func, '_is_coroutine', None) is _is_coroutine or _inspect_iscoroutinefunction(func))

inspect.iscoroutinefunction:
return bool((isfunction(object) or ismethod(object)) andobject.__code__.co_flags & CO_COROUTINE)

通过实验
>>>A.aa
<bound method A.aa of <class 'A'>>
>>>A.__dict__["aa"]
<classmethod object at 0x000002BDE92F7F60>
>>>inspect.ismethod(A.__dict__["aa"])
False
>>>inspect.isfunction(A.__dict__["aa"])
False
>>>inspect.ismethod(A.aa)
True
>>>inspect.isfunction(A.aa)
False

所以你通过 A.__dict__['aa']得到的是一个 classmethod object 而根本就不是一个 method 也不是一个 function,当然他也就不是一个 coroutinefunction 了,至于为什么这么判断,那就得看写这段代码的人是怎么想的了
ruoyu0088
2018-06-18 13:27:55 +08:00
你需要了解一下 classmethod 的原理,相关关键词为:descriptor decorator
wwqgtxx
2018-06-18 13:42:51 +08:00
@ruoyu0088 感觉作为装饰器,staticmethod 和 vlassmethod 是 builtin 实现的,很多特性也就不能以普通 decorator 来看待
lolizeppelin
2018-06-19 09:37:53 +08:00
楼上的关键词是对的 你就不要乱假设

这种类装饰器的实现原理叫描述器 descriptor

描述器是 Python 的特殊玩意
lolizeppelin
2018-06-19 09:41:39 +08:00
python 里面反射对象可以使用 getattr()和对象内置的__dict__属性两种方式

这就是你错误的地方 你想简单了
吃透描述器你就知道原理了
conn4575
2018-06-19 09:47:56 +08:00
感谢楼上几位,根据楼上给的关键字查了一下官方文档 https://docs.python.org/3.6/howto/descriptor.html ,确实是装饰器引起的,看来有必要对装饰器与描述器的原理深入研究一波...
lolizeppelin
2018-06-19 10:26:51 +08:00
装饰器没什么好研究的 不要想太多
就一套娃语法糖

正常的闭包应用

特殊就描述器

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/463850

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX