Python 在子类中动态添加hasattr()不可见的方法
我对动态添加到Python类的方法有一个问题。考虑下面的一组类,方法“代码> > STR 为任何定义的<代码> <代码>方法动态添加。Python 在子类中动态添加hasattr()不可见的方法,python,class,inheritance,Python,Class,Inheritance,我对动态添加到Python类的方法有一个问题。考虑下面的一组类,方法“代码> > STR 为任何定义的 方法动态添加。 class ToStr(object): @classmethod def add_str_method(cls, name): method = getattr(cls, name) def str_method(self, *args, **kwargs): return str(method(*arg
class ToStr(object):
@classmethod
def add_str_method(cls, name):
method = getattr(cls, name)
def str_method(self, *args, **kwargs):
return str(method(*args, **kwargs))
setattr(cls, '{0}_str'.format(name), str_method)
class Even(ToStr):
@classmethod
def even(cls, values):
return [value for value in values if value%2 == 0]
Even.add_str_method('even')
class Large(ToStr):
@classmethod
def large(cls, values):
filtered = [value for value in values if value > 5]
if hasattr(cls, 'even'):
filtered = cls.even(filtered)
return filtered
Large.add_str_method('large')
class Special(Even, Large):
pass
请注意,动态添加的%\u str
函数是实例方法,而%
是类方法。另外,large
方法取决于even
方法的存在,该方法由hasattr(cls,'even')
确定
现在我比较了每个类中%
和%\u str
方法的输出,结果让我感到困惑:
# Even.even
Even().even(values) [0, 2, 4, 6, 8, 10]
Even().even_str(values) [0, 2, 4, 6, 8, 10]
# Large.large
Large().large(values) [6, 7, 8, 9, 10]
Large().large_str(values) [6, 7, 8, 9, 10]
# Special.even
Special().even(values) [0, 2, 4, 6, 8, 10]
Special().even_str(values) [0, 2, 4, 6, 8, 10]
# Special.large
Special().large(values) [6, 8, 10]
Special().large_str(values) [6, 7, 8, 9, 10]
Special.large_str()
方法不会删除偶数值,尽管Special
类应该通过hasattr
检查从even
类继承它
所以我的问题是:为什么动态添加方法时这些方法不由hasattr
标识?
更新: 此效果不取决于
特殊
类定义中超类的顺序。如果将
偶数
和大
方法定义为实例方法,而不是类方法,则不会发生这种效果,如下面的示例所示
class ToStr(object):
@classmethod
def add_str_method(cls, name):
method = getattr(cls, name)
def str_method(self, *args, **kwargs):
return str(method(self, *args, **kwargs))
setattr(cls, '{0}_str'.format(name), str_method)
class Even(ToStr):
def even(self, values):
return [value for value in values if value%2 == 0]
Even.add_str_method('even')
class Large(ToStr):
def large(self, values):
filtered = [value for value in values if value > 5]
if hasattr(self, 'even'):
filtered = self.even(filtered)
return filtered
Large.add_str_method('large')
这种情况下的问题是可以对类调用
classmethod
s,因此当您对类执行classmethod
的getattr
操作时,它将是一个绑定方法(第一个参数已经填充)。这意味着您将添加记住在getattr
中使用的类的方法。因此,调用Special().large\u str(values)
将调用str\u方法
,但是方法
内部调用将只调用large.large
withcls=large
但large
本身没有甚至方法
class ToStr(object):
@classmethod
def add_str_method(cls, name):
method = getattr(cls, name)
def str_method(self, *args, **kwargs):
return str(method(*args, **kwargs))
setattr(cls, '{0}_str'.format(name), str_method)
class Even(ToStr):
@classmethod
def even(cls, values):
return [value for value in values if value%2 == 0]
Even.add_str_method('even')
class Large(ToStr):
@classmethod
def large(cls, values):
filtered = [value for value in values if value > 5]
if hasattr(cls, 'even'):
filtered = cls.even(filtered)
return filtered
Large.add_str_method('large')
class Special(Even, Large):
pass
另一方面,not-classmethods
将在getattr
中返回一个自由函数,因此第一个参数不会被固定,这就是为什么在第二种方法中需要在方法调用stru方法中包含self
参数的原因
class ToStr(object):
@classmethod
def add_str_method(cls, name):
method = getattr(cls, name)
def str_method(self, *args, **kwargs):
print(method) # added a print!
return str(method(*args, **kwargs))
setattr(cls, '{0}_str'.format(name), str_method)
class Even(ToStr):
@classmethod
def even(cls, values):
return [value for value in values if value%2 == 0]
Even.add_str_method('even')
class Large(ToStr):
@classmethod
def large(cls, values):
print(cls) # added a print!
filtered = [value for value in values if value > 5]
if hasattr(cls, 'even'):
filtered = cls.even(filtered)
return filtered
Large.add_str_method('large')
class Special(Even, Large):
pass
说明了这种行为:
>>> Special().large_str(list(range(11)))
<bound method Large.large of <class '__main__.Large'>> # bound method
<class '__main__.Large'> # wrong cls
'[6, 7, 8, 9, 10]' # wrong result
谢谢这对我来说并不明显。但是有没有办法克服这个问题,维护类方法?在这个特定的例子中,这没有什么意义,但我在Django框架中有一个类似的情况,我需要手动定义类方法,并动态添加实例方法。@BartoNaz尝试改用元类或类装饰器。“我想在这种情况下你不会遇到这个问题。”巴托纳兹问得好。最简单的解决方案(如果您只处理classmethod
s)是在str\method
中调用return str(method.\uuu func\uuuu(self,*args,**kwargs))
。\uuuu func\uuuu
返回未绑定的方法,因此它应该可以正常工作,但是您不能在类上调用large\u str
,它只适用于实例。所有其他方法都需要非平凡的解决方案,如元类、类修饰符或自己的classmethod
(应该在一个新问题中提问,因为您只会问“为什么动态添加方法时这些方法不由hasattr识别?”)没有实际的用例,很难给出具体的解决方案。此类问题的通用解决方案非常复杂。@MSeifert谢谢!这很好用!而且large\u str
方法不应该只在实例上调用吗?我看不出您的解决方案如何限制我最初的实现。或者您的意思是,原则上不可能用这种方法动态添加可继承类方法?
class ToStr(object):
@classmethod
def add_str_method(cls, name):
method = getattr(cls, name)
def str_method(self, *args, **kwargs):
return str(method.__func__(self, *args, **kwargs)) # this line changed
setattr(cls, '{0}_str'.format(name), str_method)