我在写一个 telegram bot ,自己寨了一个小的 lib
class Client:
def callAPI(self, method="getMe", **kwargs):
调用 requests.post(....)
getMe = functools.partialmethod(callAPI, "getMe")
这样是成功的,可以用 getMe=functools.partialmethod 的方法定义一个名为 getMe 但实际上偏函数调用 callAPI 的方法
但是另外几种写法都不对,我不明白为什么: 第一个:
def __getattr__(self, APIname):
return functools.partialmethod(callAPI, APIname)
会导致错误 NameError: name 'callAPI' is not defined 我不明白为什么 getMe 直接赋值的时候可以找到 callAPI 方法,但是在__getattr__函数里就找不到 callAPI 方法,必须用 self.callAPI 的方式来找。是不是因为前者在 class 内,而后者在 method 内的,scope 不同的缘故?
第二个:
def __getattr__(self, APIname):
return functools.partialmethod(self.callAPI, APIname)
c=Client(token="....") c.getMe()会发生 TypeError: 'partialmethod' object is not callable
但是 c.getMe.func() 就可以正常执行
我不明白,为什么直接用 partialmethod 赋值出来那个函数就是 callable 的,但这里用__getattr__返回的却不是 callable 的呢
最后找到正确写法是:
def __getattr__(self, APIname):
return functools.partial(self.callAPI, APIname)
但是该用 partialmethod 的地方用了 partial ,总感觉不正经
1
killerirving 43 天前 1
1. “是不是因为前者在 class 内,而后者在 method 内的,scope 不同的缘故” 是的
2. 这种情况应该使用 partial 。partialmethod 是声明为 class descriptor 使用的,被读取时会调用__get__();而在函数中直接调用的 method 是__call__(),partialmethod class 中并没有定义该方法所以会有 not callable 的报错 |
2
julyclyde OP @killerirving 求教,为什么之前的写法,在 class 直属层直接使用 getMe = functools.partialmethod(callAPI, "getMe")是可以的呢?
按说如果 partialmethod 返回的那个对象需要再 dot func 才能调用,那这种写法下的 getMe 到底是个啥东西??为什么不需要 dot func 就可以用? |
3
killerirving 42 天前
c.getMe.func()其实是个错误使用,func 是 partialmethod class 的成员变量,也就是 self.callAPI 这个参数
``` class partial: """New function with partial application of the given arguments and keywords. """ __slots__ = "func", "args", "keywords", "__dict__", "__weakref__" def __new__(cls, func, /, *args, **keywords): if not callable(func): raise TypeError("the first argument must be callable") if hasattr(func, "func"): args = func.args + args keywords = {**func.keywords, **keywords} func = func.func self = super(partial, cls).__new__(cls) self.func = func self.args = args self.keywords = keywords return self ```` |
4
julyclyde OP @killerirving 我的疑问其实是:
我最开始的写法 ``` class Client: def callAPI(self, method="getMe", **kwargs): 调用 requests.post(....) getMe = functools.partialmethod(callAPI, "getMe") ``` 这里 getMe 是一个 partialmethod object ,是 callable 的 但是为什么我改成 ``` def __getattr__(self, APIname): return functools.partialmethod(callAPI, APIname) ``` 然后__getattr__返回的对象就不是 callable 呢? 看起来这俩都是 partialmethod object 啊? 或者可能,前者( getMe 直接赋值 partialmethod )并不是一个 partialmethod object ?? |
5
keakon 42 天前
partial 实现很简单,它的 __call__() 方法将新老参数合并在一起调用原函数。
因此 c.getMe() -> c. __getattr__('getMe') -> functools.partial(self.callAPI, 'getMe') -> self.callAPI('getMe') partialmethod 是一个没有定义 __call__() 方法的 descriptor ,而它的 __get__ 方法主要实现是调用 partial()。 因此 functools.partialmethod(self.callAPI, APIname) 返回的是一个不能调用的 partialmethod 对象。 而 getMe = functools.partialmethod(callAPI, "getMe") 是给 Client 类定义了一个叫 'getMe' 的 descriptor 。此时,c.getMe() -> functools.partialmethod(callAPI, "getMe").__get__(c, Client) -> Client.callAPI(c, "getMe")。 |
7
julyclyde OP @keakon 是不是可以理解为:
直接赋值那个,it doesn't HAVE TO be callable? 而__getattr__那个必须是 callable ,所以才暴露出来了 partialmethod 返回值不是 callable 这个问题? |
8
keakon 42 天前 1
@julyclyde 你先看看 descriptor 的作用吧。简单来说,如果一个类( Client )的属性( getMe )是 descriptor ,那么在访问这个类的实例( c )的同名属性( getMe )时,访问的实际是这个 descriptor 的 __get__() 方法。
getMe = functools.partialmethod(callAPI, "getMe") 正是给类定义了一个 descriptor ,而它的 __get__() 方法里返回了一个 callable 。 而 return functools.partialmethod(self.callAPI, APIname) 这个实现虽然返了 descriptor ,但它不是类的属性,因此访问时并不会调用 __get__()。 |
10
NoOneNoBody 38 天前
尽量避免用 partialmethod 吧,刚出来时有人提过 issue ,当时没有确信解释,应是个 bug (结果不可预料),后续版本我就没关注,不知道修了没有
可以用取巧方法,如闭包+partial ,partial 还是稳定的 |
11
julyclyde OP |