我遇到了一个动态添加到Python类的方法的问题。考虑以下一组类,其中为任何已定义的<some>_str
方法动态添加了方法<some>
。
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
请注意,动态添加的%_str
函数是实例方法,而%
是类方法。此外,large
方法取决于even
方法的存在,该方法由hasattr(cls, 'even')
确定。
现在我比较每个类中%
和%_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
类应该从Even
类继承它,并通过hasattr
检查。
所以我的问题是:在动态添加方法时,hasattr
为什么不识别这些方法?
更新:
此效果不依赖于Special
类定义中超类的顺序
如果将even
和large
方法定义为实例方法而不是类方法,则不会发生此效果,如下例所示。
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')
答案 0 :(得分:1)
在这种情况下的问题是可以在类上调用classmethod
,所以当你对类getattr
执行classmethod
时,它将是一个绑定方法(第一个)参数已填写)。这意味着您将添加记住您在getattr
中使用的类的方法。因此,拨打Special().large_str(values)
的电话会调用str_method
,但method
内部的Large.large
电话只会使用cls=Large
拨打Large
,但even
本身没有classmethods
1}}方法。
另一方面,不是 - getattr
将返回self
中的自由函数,因此第一个参数将不会被修复,这就是为什么你需要包含method
参数的原因str_method
- 在您的第二种方法中调用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
说明了这种行为:
<function Large.large at 0x0000024B2FE808C8> # free function
<__main__.Special object at 0x0000024B2FE7C3C8> # correct instance
'[6, 8, 10]' # correct result
而正常的方法变体打印:
str_method
您的案例中可能的解决方案/解决方法 将调用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)
中的包装函数(在这种情况下将返回未绑定的方法):
Items