gpt4 book ai didi

python - 动态添加的方法对子类中的 hasattr() 不可见

转载 作者:太空宇宙 更新时间:2023-11-04 05:08:35 26 4
gpt4 key购买 nike

我对动态添加到 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 中父类(super class)的顺序类定义。
如果 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')

最佳答案

这种情况下的问题是 classmethod s 可以在一个类上调用,所以当你这样做时 getattrclassmethod在类上它将是一个绑定(bind)方法(第一个参数已经填写)。这意味着您将添加记住您在 getattr 中使用的类的方法。 .因此调用Special().large_str(values)会调用str_method但是method在里面打电话只会打电话Large.largecls=Large但是Large本身没有even方法。

另一方面,不是- classmethods将在 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 中的包装函数(在这种情况下,将返回未绑定(bind)的方法):

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)

关于python - 动态添加的方法对子类中的 hasattr() 不可见,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43683218/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com