Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/284.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中对每个祖先调用方法_Python_Datamodel - Fatal编程技术网

在Python中对每个祖先调用方法

在Python中对每个祖先调用方法,python,datamodel,Python,Datamodel,在python中,我有一个类为“D”的对象,我想按顺序执行由“D”及其每个祖先(“A”、“B”和“C”)定义的“run”方法 我能做到这一点 class A(object): def run_all(self): # I prefer to execute in revere MRO order for cls in reversed(self.__class__.__mro__): if hasattr(cls, 'run'):

在python中,我有一个类为“D”的对象,我想按顺序执行由“D”及其每个祖先(“A”、“B”和“C”)定义的“run”方法

我能做到这一点

class A(object):
    def run_all(self):
        # I prefer to execute in revere MRO order
        for cls in reversed(self.__class__.__mro__):
            if hasattr(cls, 'run'):
                # This works
                cls.run(self)
                # This doesn't
                #cls.__getattribute__(self, 'run')()

    def run(self):
        print "Running A"

class B(A):
    def run(self):
        print "Running B"

class C(A):
    def run(self):
        print "Running C"

class D(C, B):
    def run(self):
        print "Running D"

if __name__ == "__main__":
    D().run_all()
导致

$ python test.py 
Running A
Running B
Running C
Running D
但是在实践中,我不知道要执行的方法的名称。但是,如果我使用getattribute()(请参见注释)行尝试此操作,它将不起作用:

$ python test.py 
Running D
Running D
Running D
Running D
因此,我的问题是:

  • 为什么不起作用

  • 这是最好的方法吗


  • 您不应该使用
    \uuuu getattribute\uuuu
    方法

    只需执行以下操作:

    getattr(cls, 'run')(self)
    

    你为什么不干脆用
    super
    ?虽然有些,但它的设计正是考虑到这种情况,我会毫不犹豫地使用它

    发件人:

    这对于访问继承的 已在中重写的方法 班级。搜索顺序与相同 getattr()使用的,但 类型本身被跳过。[……]这 使实施成为可能 “菱形图”,其中有多个底图 类实现相同的方法

    更新:在您的情况下,它会变成这样:

    class A(object):
    
        def run(self):
            print "Running A"
    
    class B(A):
        def run(self):
            super(B, self).run()
            print "Running B"
    
    class C(A):
        def run(self):
            super(C, self).run()
            print "Running C"
    
    class D(C, B):
        def run(self):
            super(D, self).run()
            print "Running D"
    
    if __name__ == "__main__":
        D().run()
    

    如果您可以更改所有的
    run
    实现(并在D中调用
    run
    而不是
    run\u all
    ),这将起作用:

    class A(object):
        def run(self):
            print "Running A"
    
    class B(A):
        def run(self):
            super(B, self).run()
            print "Running B"
    
    class C(A):
        def run(self):
            super(C, self).run()
            print "Running C"
    
    class D(C, B):
        def run(self):
            super(D, self).run()
            print "Running D"
    
    if __name__ == "__main__":
        D().run()
    
    请注意,我没有在根类中使用
    super
    ,它“知道”没有更多的超类可以访问(
    object
    没有定义
    run
    方法)。不幸的是,在Python2中,这不可避免地是冗长的(并且也不适合通过decorator实现)

    如果我正确理解您的目的,那么您对hasattr的检查是非常脆弱的——如果类定义或继承了该属性,它将发现该类“具有”该属性。因此,如果您有一个中间类,该类不重写
    run
    ,但确实发生在
    \uu mro\uu
    上,那么它继承的
    run
    版本将在您的方法中被调用两次。例如,考虑:

    class A(object):
        def run_all(self):
            for cls in reversed(self.__class__.__mro__):
                if hasattr(cls, 'run'):
                    getattr(cls, 'run')(self)
        def run(self):
            print "Running A"
    class B(A): pass
    class C(A):
        def run(self):
            print "Running C"
    class D(C, B): pass
    
    if __name__ == "__main__":
        D().run_all()
    
    这张照片

    Running A
    Running A
    Running C
    Running C
    
    对于
    run
    B
    D
    继承而不重写的版本,有两个“口吃”(分别来自
    A
    C
    )。假设我是对的,这不是你想要的效果,如果你想避免
    super
    ,你可以尝试将
    run\u all
    改为:

    def run_all(self):
        for cls in reversed(self.__class__.__mro__):
            meth = cls.__dict__.get('run')
            if meth is not None: meth(self)
    
    在我的最新示例中,用两个不同的
    def
    s替换
    A
    C
    中的
    run
    ,使示例打印:

    Running A
    Running C
    
    我想这可能更接近你想要的


    还有一个侧重点:不要重复这项工作——hasattr-guarding-getattr,或在测试保护dict-access中的
    ——无论是在保护中签入,还是在保护访问器中,都必须在内部重复完全相同的工作,没有什么好的效果。相反,对单个
    getattr
    调用(或dict的
    get
    方法)使用
    None
    的第三个参数:这意味着如果该方法不存在,您将检索一个
    None
    值,然后您可以防止该调用发生。这正是dicts有一个
    get
    方法和
    getattr
    有第三个可选的“default”参数的原因:为了便于直接应用,“不要重复自己”,这是优秀编程的一条非常重要的准则!)

    你可以使用getattribute,但它看起来很“不好”-cls.\uuuuuuGetAttribute\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,
    super
    需要明确的类规范,正如我的答案中的第一个代码建议——使用
    self.\uuuuu class\uuuuu
    ,在其中无法正常工作。(顺便说一句,Python3这样要好一点)为什么不直接使用super(D,self).run()?我在主要答案中举了一个例子,在我的例子中,子类将表示可以继承的服务器配置,等等,我试图在python的限制内尽可能保持语法的声明性。因此,我不希望每个子类的每个方法都必须手动调用super。@Roberto,你说“即使在层次结构的“根”类上也需要调用super”是完全错误的!只要试一下你的代码,你就会看到一个带有
    AttributeError的回溯:“super”对象没有属性“run”
    ;删除你声称需要的
    super
    调用
    A.run
    ,就像我的答案代码省略了它一样,你会看到回溯消失,一切正常。。。当然,这与我的回答完全一样,因为,一旦这个可怕的错误被修复,您的代码就会与我的代码相同;-)。这正是我想要的,你说的“口吃”是对的,我没有想到。谢谢大家!另外,据我所知,要将super与多重继承结合使用,您确实需要从“root”类调用它,以便遍历整个树/菱形/任何东西(正如Roberto在下面所做的),尽管我可能在这一点上错了。