【问题标题】:How to make sure parent method is always called before child overrides it in a decorator?如何确保在子方法在装饰器中覆盖它之前总是调用父方法?
【发布时间】:2016-02-01 21:52:00
【问题描述】:

所以我有一个父类:

class Parent(Object):
    def function(self):
        do_something()

还有很多子类:

class Child1(Parent):
    def function(self):
        do_something_else_1()

class Child2(Parent):
    def function(self):
        do_something_else_2()

...

我想确保父级function() 总是在子级function() 之前被调用,这样每次调用function() 时也调用do_something(),无论是哪个班级。现在,我知道我可以这样做:

class Child1(Parent):
    def function(self):
        super(Child1, self).function()
        do_something_else_1()

class Child2(Parent):
    def function(self):
        super(Child2, self).function()
        do_something_else_2()

...

但我不想对每个子类都这样做,因为这些子类是动态生成的,而且这些子类本身正在进一步扩展。相反,我想做一些看起来像

class Child1(Parent):
    @call_parent
    def function(self):
        do_something_else_1()

class Child2(Parent):
    @call_parent
    def function(self):
        do_something_else_2()

...

并编写一个装饰器来完成同样的任务。

我有两个问题:

  1. 这是个好主意吗?我是否以预期的方式使用装饰器和函数覆盖?
  2. 我将如何编写这个装饰器?

【问题讨论】:

  • 一行@call_parent 比一行super(Class, self).method(...) 好多少,尤其是考虑到后者更传统并且可能更明确和灵活?
  • ... 并且不会将函数调用的开销添加到方法调用中?
  • 我在上面的帖子中对其进行了编辑,但这是因为这些子类是动态生成的,并且因为这些子类也在扩展。我不想一直打电话给super(Class, self).method(...)
  • @MichaelDong:一路向下调用super(Class, self.).method(...) 有什么问题?为什么动态生成类会让你更喜欢装饰器?
  • @user2357112:我没有充分的理由。如果在装饰器中这样做的想法有问题,我不会这样做。我想看看有没有什么问题。但真正的原因是我必须更改生成类的代码。

标签: python python-2.7 oop


【解决方案1】:

这是个好主意吗?我是否以预期的方式使用装饰器和函数覆盖?

在不了解您系统的详细信息的情况下,这个问题很难回答。 仅从抽象示例来看,它看起来还不错,但是用类似 @call_parent 的东西替换显式和清晰的 super() 调用并不是一个好主意。

每个人都知道或很容易发现super() 做了什么,而装饰器只会造成混乱。

我将如何编写这个装饰器?

不要写装饰器,你可以使用template method:

class Parent(Object):

    def function(self):
        do_something()
        do_something_in_child()

    def do_something_in_child():
        pass

现在在子类中你只覆盖do_something_in_childfunction 只保留在Parent,所以你确定你的do_something() 总是被调用。

class Child1(Parent):

    def do_something_in_child(self):
        do_something_else_1():

class Child2(Parent):

    def do_something_in_child(self):
        do_something_else_2():

class Child3(Parent):
    # no override here, function() will do the same what it does in Parent
    pass

【讨论】:

  • 是的 - 在代码中明确,非常清晰,非常简单。不需要装饰器。
  • 我认为这是一个很好的通用答案。但是,我的具体用例是父类是unittest 包的TestCase 类的扩展,它自己的test_case() 方法由测试运行程序调用。父类TestCase 有很多子类,有自己的test_case() 方法。然后动态扩展这些孩子。我想我只想装饰孩子们,而不用担心当他们动态扩展时会发生什么。这不是个好主意吗?
  • @MichaelDong 好吧,我不能说我明白你到底想做什么。对于测试,我通常在父类setUp() 方法中做一般的事情,然后在每个测试用例之前自动调用它。这样你可能不需要父类中的test_case
【解决方案2】:

我不精通 Python,但你可以这样:

# Function in childredn. Overrides parent one.
def function(self):
        # child code
        super().function() #however it is used
        # more child code 

如果这不合理,请查看模板方法设计模式。

# Function in parent. Do not override this one
def function(self):
    # your parent code
    function_do_something()
    # more code if you need it

# function in parent. Children overryde this one
def function_do_something():
...

而且,你总是可以让 function_do_something() 为空,以便只执行你的父构造函数。

【讨论】:

    猜你喜欢
    • 2022-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-08
    • 2014-05-11
    • 1970-01-01
    • 1970-01-01
    • 2019-06-18
    相关资源
    最近更新 更多