Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/277.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在子类中动态添加hasattr()不可见的方法_Python_Class_Inheritance - Fatal编程技术网

Python 在子类中动态添加hasattr()不可见的方法

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

我对动态添加到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(*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
with
cls=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)