是否可以显式指定cls类方法的参数?
例如,这工作正常,但n不使用self,因此应该是一个类方法:
class A():
def n(self):
print(self.__class__.__name__)
class B(A):
pass
A.n(self=B())
Run Code Online (Sandbox Code Playgroud)
但以下内容不起作用,虽然它对我来说似乎相当:
class A():
@classmethod
def n(cls):
print(cls.__name__)
class B(A):
pass
A.n(cls=B)
Run Code Online (Sandbox Code Playgroud)
产生的异常是
TypeError: n() got multiple values for argument 'cls'
Run Code Online (Sandbox Code Playgroud)
XY问题分析:我有很多类方法,并且A有很多类派生自A. 这本质上就是我想做的:
class A():
@classmethod
def n1(cls): print("1", cls.__name__)
@classmethod
def n2(cls): print("2", cls.__name__)
@classmethod
def n3(cls): print("3", cls.__name__)
class B(A): pass
class C(A): pass
class D(A): pass
class E(A): pass
class F(A): pass
for cls in [B, C, D, E, F]:
for fun in [A.n1, A.n2, A.n3]:
fun(cls)
Run Code Online (Sandbox Code Playgroud)
当最后一行替换为
getattr(cls, fun.__name__)()
Run Code Online (Sandbox Code Playgroud)
但我觉得这很丑陋。
我期望标题问题的答案是“否”,所以我的后续(也许是实际的)问题是“为什么?”。我很困惑为什么fun(cls)不允许。也许@classmethod只是在做一个functools.partial?
(可以说我在设计中做了一些非常规的事情,因为我也希望这些方法同时是 a@classmethod和 a ,这是不可能的。)@property
这里的问题是类方法是类本身的实际方法。通过类访问的实例方法只是一个函数(至少在 Python 3 上,它删除了未绑定方法的概念);它只是在实例上访问时的绑定方法。但是当你这样做时A.n,它正在创建一个绑定类方法;thecls已嵌入其中,因此您无法再次通过它。这是因为描述符协议在每个协议上的实现方式不同;aclassmethod绑定(到类),无论是在类还是实例上访问,实例方法仅在实例上访问时绑定,从类访问时返回原始函数,而 astaticmethod从不绑定(始终返回原始函数)。
您有几个选择:
B.n()。这就是你 99% 的时间都应该做的事情。cls隐式传递,始终作为显式参数),请将其设为@staticmethod仍然需要cls,这意味着它永远不会绑定,每次都必须传递一个参数clsA.n.__func__(B)选项 #3 为您的用例提供了最小的修复:
for cls in [B, C, D, E, F]:
for method in [A.n1, A.n2, A.n3]:
method.__func__(cls)
Run Code Online (Sandbox Code Playgroud)
或者,您可以通过仅使用名称本身来使其不那么难看;无论如何,您不需要访问任何内容A,因此只需执行以下操作:
for cls in [B, C, D, E, F]:
for fun in ['n1', 'n2', 'n3']:
getattr(cls, fun)()
Run Code Online (Sandbox Code Playgroud)
或者为了稍微提高效率和稍微简洁的代码,提前创建一个访问器函数并使用它:
from operator import attrgetter
funcgetter = attrgetter('n1', 'n2', 'n3')
for cls in [B, C, D, E, F]:
for fun in funcgetter(cls):
fun()
Run Code Online (Sandbox Code Playgroud)
可以使用类似的技巧来methodcaller减少getattr使用时调用的冗长性,但在这种情况下,我认为attrgetter更简洁:
from operator import methodcaller
funccallers = methodcaller('n1'), methodcaller('n2'), methodcaller('n3')
for cls in [B, C, D, E, F]:
for caller in funccallers:
caller(cls)
Run Code Online (Sandbox Code Playgroud)