是否可以明确指定类方法的cls
自变量?
例如,这很好用,但是n
不使用self
,因此应该是一种分类方法:
class A():
def n(self):
print(self.__class__.__name__)
class B(A):
pass
A.n(self=B())
但是以下内容不起作用,尽管它似乎和我一样:
class A():
@classmethod
def n(cls):
print(cls.__name__)
class B(A):
pass
A.n(cls=B)
产生的异常是
TypeError: n() got multiple values for argument 'cls'
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)
当最后一行替换为
时有效 getattr(cls, fun.__name__)()
但是我觉得这很丑。
我希望标题问题的答案是“否”,所以我的后续(也许是实际的)问题是“为什么?”。我很困惑为什么不允许使用fun(cls)
。也许@classmethod
只是在做functools.partial
?
(可以说我在设计中做了一些非常规的事情,因为我也希望这些方法同时成为@classmethod
和@property
,这是不可能的。)
答案 0 :(得分:2)
这里的问题是,类方法实际上是类本身的方法。通过类访问的实例方法只是一个函数(至少在Python 3上,该方法删除了未绑定方法的概念);在实例上访问时,它只是一个绑定方法。但是,当您执行A.n
时,它就是在创建绑定类方法。 cls
已嵌入其中,因此您无法再次传递它。这是因为每个描述符协议的实现方式; classmethod
绑定(绑定到类)无论是在类还是实例上访问,仅当在实例上访问时才使用实例方法,从类访问时返回原始函数,而staticmethod
则从不绑定(总是返回原始功能)。
您有几种选择:
B.n()
。这就是您99%的时间应该做的事情。cls
,始终将其作为显式参数),请将其设为仍使用@staticmethod
的{{1}},这意味着它永远不会绑定,您必须每次都传递一个cls
参数cls
选项3为您的用例提供了最小的解决方法:
A.n.__func__(B)
或者,您可以仅通过使用名称本身来使其不那么难看;无论如何,您都不需要访问for cls in [B, C, D, E, F]:
for method in [A.n1, A.n2, A.n3]:
method.__func__(cls)
上的任何内容,只需执行以下操作:
A
或者为获得更高的效率和更简洁的代码,请提前创建访问器函数并使用它:
for cls in [B, C, D, E, F]:
for fun in ['n1', 'n2', 'n3']:
getattr(cls, fun)()
可以使用from operator import attrgetter
funcgetter = attrgetter('n1', 'n2', 'n3')
for cls in [B, C, D, E, F]:
for fun in funcgetter(cls):
fun()
做类似的技巧来减少使用时methodcaller
调用的冗长性,但是在这种情况下,我认为getattr
会更干净一些:>
attrgetter
答案 1 :(得分:1)
或者您可以将丑陋封装在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__)
@staticmethod
def call_method(cls, fun):
getattr(cls, fun.__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]:
A.call_method(cls, fun)