【问题标题】:Is it possible to do dynamic method overriding from a method of another class? If so, How?是否可以从另一个类的方法进行动态方法覆盖?如果是这样,如何?
【发布时间】:2018-01-11 15:45:32
【问题描述】:

我查看了以下帖子和博客,这些帖子和博客解释了如何使用函数执行此操作。我想知道对类或对象方法做同样的事情是否合法? 将感谢适用于 Python2 的答案(尽管很高兴知道它如何适用于 Python3)。

来自stackoverflow的来源:

Override module method where from...import is used 和 Override a method at instance level

内容相似的博客:

https://tryolabs.com/blog/2013/07/05/run-time-method-patching-python/ http://igorsobreira.com/2011/02/06/adding-methods-dynamically-in-python.html

这段代码说明了我的意图和我的尝试:

class Person:
        def __init__(self, name):
            self.name = name


    class OtherPerson:
        def __init__(self, name):
            self.name = name 

        def do_something(self):
            print self.name + '$$'

  p = Person('alpha')
  p.do_something = OtherPerson.do_something
  # TypeError: unbound method do_something() must be called with 
  OtherPerson #instance as first argument (got nothing instead)
  p.do_something()
  op = OtherPerson('beta')
  p.do_something = op.do_something

  # output: 'beta$$' I would like to get 'alpha$$'
  p.do_something()

尝试了@khelwood 的以下建议:

Person.do_something = OtherPerson.do_something
#TypeError: unbound method do_something() must be called with OtherPerson instance as first argument (got nothing instead)
# Works if OtherPerson.do_something is staticmethod
p.do_something()

【问题讨论】:

  • 你可以做Person.do_something = OtherPerson.do_something,如果这是你的要求
  • 我敢肯定,任何国家/地区的书籍都没有法律规定它非法...;)
  • @khelwood 感觉它会起作用,但不幸的是它不起作用。得到同样的错误:TypeError: unbound method do_something() must be called with OtherPerson instance as first argument (什么都没有代替)
  • 那我猜你使用的是 Python 2。
  • @khelwood :正确,使 Person 和 OtherPerson 从对象扩展也无济于事。那么在 Python 2 中是 not allowed 而在 Python 3 中是 allowed 吗?这解决了我猜想的问题的法律部分! (在:deceze)

标签: python dynamic overriding


【解决方案1】:

关键是将正确的参数传递给方法。 让我们仔细看看您首先遇到的错误:

TypeError: unbound method do_something() must be called with 
OtherPerson #instance as first argument (got nothing instead)

当您查看OtherPerson.do_something 时,很明显它期望一个实例作为它的第一个参数。 所以现在p.do_something 指的是OtherPerson.do_something,它需要第一个参数。 因此,在当前状态下,正确的调用应该是:

p.do_something(p)

当然,这不是很好,因为您必须指定实例两次。 这是因为该方法现在未绑定:它不知道调用它的实例,即它不知道self。

我提出的解决方案在于让p.do_something 引用一个调用 OtherPerson.fo_something 与 p 作为第一个参数的函数 em>。


让我们有两个类,Foo 和 Bar,定义如下:

class Foo:
    def __init__(self, x):
        self.x = x

    def speak(self):
        print("Foo says:", self.x)


class Bar:
    def __init__(self, x):
        self.x = x

    def speak(self):
        print("Bar says:", self.x)

假设您有一个来自 Foo 类的 foo 实例。 现在,您想要动态覆盖其speak 方法,以便它调用Bar 代替。 您可以简单地将foo.speak 重新分配给调用Bar.speak 的函数。

>>> foo = Foo(2)
>>> foo.speak()
Foo says: 2
>>> foo.speak = lambda: Bar.speak(foo)
>>> foo.speak()
Bar says: 2

您可以使其更加通用。 为了举例,让我们编写一个函数,它接受一个实例、一个方法名和一个目标类,并用目标类覆盖实例的匹配方法:

def override(instance, method_name, target_class):
    class_method = getattribute(target_class, method_name)

    def new_method(*args, **kwargs):
        return class_method(instance, *args, **kwargs)

    setattribute(instance, method_name, new_method)

您可以观察到相同的预期行为:

>>> foo = Foo(2)
>>> override(foo, "speak", Bar)
>>> foo.speak()
Bar says: 2

【讨论】:

  • 感谢您的回答,确实得到了很好的解释。不过,这似乎不适用于 Python2!您能否解释一下如何修改它以适用于 Python 2.7?只是最初的例子,我用覆盖方法得到了最后一个通用部分。
  • Cont.. 错误现在是:TypeError: unbound method speak() must be called with Bar instance as first argument (got Foo instance)
  • @S.Arora 它看起来像 Python 2 的另一个错误:duck-typing 有缺陷,因为执行了类型检查。如果Bar.do_something unbound 方法需要Bar 实例,则别无选择,只能将foo 强制转换为Bar 实例,这显然需要编写转换函数。与在 Python 3 中实现的容易程度相比,我想说在 Python 2 中以纯动态方式是不可行的。
  • 这很合理,我会接受你的回答,因为它无论如何都适用于 Python3。
【解决方案2】:

如果 OtherPerson.do_something 是静态方法,则可以这样做:

class Person(object):
    def __init__(self, name):
        self.name = name


class OtherPerson(object):
    def __init__(self, name):
        self.name = name

    @staticmethod
    def do_something(self):
        print self.name + '$$'


p = Person('alpha')
Person.do_something = OtherPerson.do_something

p.do_something() #alpha$$

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-17
    • 2012-08-21
    • 1970-01-01
    • 2021-05-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多