【问题标题】:Redefinition of class method in python在python中重新定义类方法
【发布时间】:2012-04-07 17:03:15
【问题描述】:

上下文

我正在尝试为我的代码添加一些“插件”(我不确定这是否是正确的定义)。 “插件”是指一个模块,它定义了一个模型(这是一个科学代码),它的存在足以在代码中的任何其他地方使用它。

当然,这些插件必须遵循一个模板,该模板使用我的代码中定义的一些模块/函数/类。这是我的代码相关部分的小sn-p:

# [In the code]
class AllModels():
    def __init__(self):
        """
        Init.
        """
        self.count = 0

    def register(self, name, model):
        """
        Adds a model to the code
        """
        setattr(self, name, model)
        self.count += 1
        return

class Model():
    def __init__(self, **kwargs):
        """
        Some constants that defines a model
        """
        self.a = kwargs.get("a", None)
        self.b = kwargs.get("b", None)
        # and so on...

    def function1(self, *args, **kwargs):
        """
        A function that all models will have, but which needs:
            - to have a default behavior (when the instance is created)
            - to be redefinable by the "plugin" (ie. the model)
        """
        # default code for the default behavior
        return

instance = AllModels()

这里是“插件”的相关部分:

# [in the plugin file]
from code import Model, instance
newmodel = Model(a="a name", b="some other stuff")

def function1(*args, **kwargs):
    """
    Work to do by this model
    """
    # some specific model-dependent work
    return

instance.register(newmodel)

其他信息和要求

  • function1 对于任何模型插件都有完全相同的签名,但是 通常为每个人做不同的工作。

  • 我想要function1 的默认行为,如果不是 由插件定义,我仍然可以做一些事情(尝试 不同的可能性,和/或引发警告/错误)。

  • 在插件中,function1 可能会使用一些仅在此插件中定义的其他功能。我之所以这么说是因为代码与多处理模块一起运行,并且我需要AllModelsinstance 实例才能在子进程中调用function1instance 定义在父进程和模型插件中,但会在不同的子进程中使用(但未对其进行修改)。

  • 如果function1 被插件“重新定义”后,能够访问Model 实例的属性(即self),那就太棒了。

问题

我已经阅读了许多不同的 python 文档来源和一些 SO 问题。我只看到这个问题的两个/三个可能的解决方案:

1) 不在Model 类中声明function1 方法,而是在插件创建它的新实例时将其设置为属性。

# [in the plugin file]
def function1(*args, **kwargs):
    # ....
    return
newmodel.function1 = function1

然后在需要时调用它。在这种情况下,对象Model 中的属性function1 可能会初始化为None。其中一个警告是function1 没有“默认行为”(它必须在代码中处理,例如测试if instance.function1 is None: ...),更大的一个是我无法访问self这样……

2) 以某种方式使用 python 装饰器。我从来没有使用过这个,而且我读过的文档并不是那么简单(我的意思是不直接,因为它的使用有很多可能性)。但这似乎是一个很好的解决方案。但是我担心它对性能的影响(我已经读过它可能会减慢装饰函数/方法的执行速度)。如果这个解决方案是最好的选择,那么我想知道如何使用它(可能是一个快速的 sn-p),以及是否可以使用 Model 类的属性:

# [in the plugin file]
@mydecorator
def function1(self, *args, **kwargs):
    """
    I'm not sure I can use *self*, but it would be great since some attributes of self are used for some other function similar to *function1*...
    """
    # some stuff using *self*, eg.:
    x = self.var **2 + 3.4
    # where self.var has been defined before, eg.: newmodel.var = 100.

3) 使用模块types 及其MethodType...我不确定这与我的情况是否相关...但我可能错了。

在这个长长的问题之后你可能会看到,我对这些python特性不是很熟悉,而且我现在对装饰器的理解真的很差。在继续阅读一些文档的同时,我认为可能值得在这里提出这个问题,因为我不确定解决问题的方向。

解决方案

Senderle 答案的美妙之处在于它非常简单明了......错过了它是一种耻辱。很抱歉用那个问题污染了 SO。

【问题讨论】:

  • 最好将 AllModels 中的模型保存在 dict 属性中,而不是类命名空间中。否则,例如,当有人称模型为“计数”时,您会遇到疯狂的错误
  • 如果这是 python2,如果你的类不从其他任何东西(如模型)继承,你需要使你的类成为新样式(从 object 继承)
  • @gnibbler 感谢您指出这一点!
  • @Daenyth 建议不要使用老式类吗?你能告诉我为什么吗?
  • @mhavel,新式类是真正的用户定义类型,但旧式类不是;旧式类的所有实例都属于同一类型,instance。这意味着新式类可以做旧式类不能做的各种好事。此外,旧式课程正在逐步淘汰。基本上,他们只是讨厌。

标签: python function plugins decorator redefinition


【解决方案1】:

好吧,除非我弄错了,否则你想子类化Model。这有点像创建Model 的实例并将其function1 属性替换为插件模块中定义的函数(即您的选项1);但它更干净,并为您处理所有细节:

# [in the plugin file]
from code import Model, instance

class MyModel(Model):
    def function1(*args, **kwargs):
        """
        Work to do by this model
        """
        # some specific model-dependent work
        return

newmodel = MyModel(a="a name", b="some other stuff")
instance.register(newmodel)

这样,所有其他方法(“附加”到Model 实例的函数)都继承自Model;它们的行为方式相同,但 function1 将被覆盖,并遵循您自定义的 function1 定义。

【讨论】:

    【解决方案2】:

    您能否在Model 类和raise 中编写一个虚拟function1() 函数NotImplementedError?这样,如果有人试图从Model 继承而不实现function1(),他们将在尝试运行代码时遇到异常。如果您正在为他们运行代码,则可以捕获该错误并向用户返回有用的错误消息。

    例如:

    class Model:
        #Your code
    
        def function1():
            raise NotImplementedError("You need to implement function1 
                when you inherit from Model")
    

    然后,您可以在运行代码时执行以下操作:

    try:
        modelObj.function1()
    except NotImplementedError as e:
        #Perform error handling here
    

    编辑:NotImplementedError 的官方 Python 文档指出:“在用户定义的基类中,抽象方法在需要派生类覆盖方法时应该引发此异常。”这似乎符合这里的要求。

    【讨论】:

    • 感谢您的贡献。我还不确定function1 的默认行为是什么,但确实有可能引发错误。
    【解决方案3】:

    您尝试做的事情可以通过非常简单的方式完成 - 只需使用面向对象的技术并利用 Python 中的函数也是普通对象 -

    要做的一件简单的事情就是让您的“模型”类接受“function1”作为参数,并将其存储为对象成员。

    像这样的一些代码,对您的代码进行的改动很少 - 尽管更有趣的事情肯定是可能的:

    # [In the code]
    
    class AllModels():
        def __init__(self):
            """
            Init.
            """
            self.count = 0
    
        def register(self, name, **kwargs):
            """
            Adds a model to the code
            """
            model = Model(**kwargs)
            setattr(self, name, model)
            self.count += 1
            return
    
    class Model():
        def __init__(self, **kwargs):
            """
            Some constants that defines a model
            """
            self.a = kwargs.get("a", None)
            self.b = kwargs.get("b", None)
            if "function1" in kwargs:
                self.real_function1 = kwargs["function1"]
                self.function1.__doc__ = kwargs["function1"].__doc__
            # and so on...
    
        def function1(self, *args, **kwargs):
            """
            A function that all models will have, but which needs:
                - to have a default behavior (when the instance is created)
                - to be redefinable by the "plugin" (ie. the model)
            """
            if self.real_function1:
                return self.real_function1(self, *args, **kwargs)
            # default code for the default behavior
            return
    
    instance = AllModels()
    

    # [in the plugin file]
    from code import Model, instance
    newmodel = Model(a="a name", b="some other stuff")
    
    def function1(self, *args, **kwargs):
        """
        Work to do by this model
        """
        # some specific model-dependent work
        return
    
    
    instance.register("name", function1 = function1, a="a name", b="some other stuff")
    

    【讨论】:

    • 有趣的解决方案。但是,它是否适用于类属性(即访问self)?从您的 sn-p 来看,情况似乎如此,这很好。感谢您的宝贵时间。
    • 是的,因为该函数将由 Model 类中的存根方法手动调用 - 在上面的建议中,此调用 wxplcily 在参数中添加了“self”。
    猜你喜欢
    • 1970-01-01
    • 2019-07-22
    • 2010-09-13
    • 2021-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-04
    • 2021-09-06
    相关资源
    最近更新 更多