项目有个需求要动态获取模块内的所有协程方法,查了点资料发现 python 里面反射对象可以使用getattr()
和对象内置的__dict__
属性两种方式,但是我发现两种方式再处理静态的协程方法时结果不一样:
getattr()
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
__dict__
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 了半天也没搞明白为什么,求指教!
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'
1
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 了,至于为什么这么判断,那就得看写这段代码的人是怎么想的了 |
2
ruoyu0088 2018-06-18 13:27:55 +08:00
你需要了解一下 classmethod 的原理,相关关键词为:descriptor decorator
|
3
wwqgtxx 2018-06-18 13:42:51 +08:00 via iPhone
@ruoyu0088 感觉作为装饰器,staticmethod 和 vlassmethod 是 builtin 实现的,很多特性也就不能以普通 decorator 来看待
|
4
lolizeppelin 2018-06-19 09:37:53 +08:00 via Android
楼上的关键词是对的 你就不要乱假设
这种类装饰器的实现原理叫描述器 descriptor 描述器是 Python 的特殊玩意 |
5
lolizeppelin 2018-06-19 09:41:39 +08:00 via Android
python 里面反射对象可以使用 getattr()和对象内置的__dict__属性两种方式
这就是你错误的地方 你想简单了 吃透描述器你就知道原理了 |
6
conn4575 OP 感谢楼上几位,根据楼上给的关键字查了一下官方文档 https://docs.python.org/3.6/howto/descriptor.html ,确实是装饰器引起的,看来有必要对装饰器与描述器的原理深入研究一波...
|
7
lolizeppelin 2018-06-19 10:26:51 +08:00 via Android 1
装饰器没什么好研究的 不要想太多
就一套娃语法糖 正常的闭包应用 特殊就描述器 |