【问题标题】:Monkey patch __del__ to new function猴子补丁 __del__ 到新功能
【发布时间】:2011-08-26 00:00:46
【问题描述】:

出于特定调试目的,我想包装任意对象的 del 函数以执行额外任务,例如将对象的最后一个值写入文件。

理想情况下我想写 猴子(x) 它应该意味着当x被删除时,x的最终值被打印出来

现在我发现 del 是一个类方法。所以下面是一个开始:

class Test:
    def __str__(self):
        return "Test"

def p(self):
    print(str(self))

def monkey(x):
    x.__class__.__del__=p

a=Test()
monkey(a)
del a

但是,如果我只想猴子特定的对象,我想我需要动态地将它们的类重写为一个新的类?!此外,无论如何我都需要这样做,因为我无法访问 del 的内置类型?

有人知道如何实现吗?

【问题讨论】:

    标签: python monkeypatching


    【解决方案1】:

    编辑:这实际上是行不通的,我把它留在这里主要是为了警告其他人。

    您可以对单个对象进行猴子修补。 self 不会以这种方式传递给你猴子修补的函数,但这很容易用 functools.partial 解决。

    例子:

    def monkey_class(x):
        x.__class__.__del__ = p
    
    def monkey_object(x):
        x.__del__ = functools.partial(p, x)
    

    【讨论】:

    • 你试过了吗?不确定我是否犯了一些错误,但显然 x.__del__ 是无效的。可能是因为 del 严格来说是一个类方法?!不知何故,我无法让它工作:(而且我觉得 .partial 保留对 x 的引用,因此它不会被垃圾收集?!
    • 它不起作用,因为特殊方法只能用类 dict 调用。此解决方案仅适用于“常规”方法。
    • @Boaz Yaniv:哎呀,你每天都学到新东西。我将这个答案留在这里作为对其他人的警告。 :-)
    【解决方案2】:

    del a 从命名空间中删除名称“a”,但不删除该名称引用的对象。看到这个:

    >>> x = 7
    >>> y = x
    >>> del x
    >>> print y
    7
    

    另外,some_object.__del__is not guaranteed to be called at all

    另外,我已经回答了你的问题here(德语)。

    【讨论】:

      【解决方案3】:

      虽然__del____str____repr__ 等特殊的“双下划线”方法可以在实例级别进行猴子补丁,但它们将被忽略,除非直接调用它们 (例如,如果您接受 Omnifarious 的回答:del a 不会打印任何东西,但 a.__del__() 会)。

      如果您仍想在运行时对类A单个实例 a 进行猴子补丁,解决方案是动态创建一个派生自A 的类A1,然后将a 的类更改为新创建的A1。是的,这是可能的,a 将表现得好像什么都没有改变——除了现在它包含了你的猴子补丁方法。

      这是一个基于我为另一个问题编写的通用函数的解决方案: Python method resolution mystery

      def override(p, methods):
          oldType = type(p)
          newType = type(oldType.__name__ + "_Override", (oldType,), methods)
          p.__class__ = newType
      
      
      class Test(object):
          def __str__(self):
              return "Test"
      
      def p(self):
          print(str(self))
      
      def monkey(x):
          override(x, {"__del__": p})
      
      a=Test()
      b=Test()
      monkey(a)
      print "Deleting a:"
      del a
      print "Deleting b:"
      del b
      

      【讨论】:

      • 这种方法有两个问题:首先,它不适用于 int 或 list 等堆类型,因为您无法重新分配它们的 __class__。其次,类型检查将导致不同的结果,具体取决于对象是否被猴子()处理过。这可能会导致非常混乱的错误。
      • 在我上面的评论中有一个错字:它应该是“非堆类型”。
      • 我认为没有非堆类型的解决方案。至于 direct(即非派生的)类型检查,对于像 Python 这样严重依赖鸭子类型的语言来说,这是真的不应该做的事情。可能在极少数情况下它有用,但实际上,类型检查应该被认为比动态派生更不安全。
      • 新类是从旧类派生的——所以是的,它已经有了相同的超类。只要您使用isinstance(x, C) 而不是type(x) == C(通常不应该这样做)检查课程,一切都会好起来的。
      • @naxa:然后直接覆盖类上的方法,如问题中给出的示例。如果你想在类级别上覆盖,你不需要做任何特别的事情:它就可以工作!
      【解决方案4】:

      您还可以从某些基类继承并覆盖__del__ 方法(那么您只需要在构造对象时覆盖类)。 或者你可以使用super内置方法。

      【讨论】:

      • 这个技巧适用于一些临时调试,所以我宁愿不定义真正的基类。此外,我需要记住从具有 del 函数的任何派生类调用 super。最后,这不适用于内置插件。我希望有一种方法也可以猴子正常列表?!
      • stackoverflow.com/questions/192649/… - 这里的第二个答案是一个很好的解释
      猜你喜欢
      • 2016-09-01
      • 2012-09-16
      • 2012-12-18
      • 2020-01-09
      • 1970-01-01
      • 1970-01-01
      • 2010-09-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多