Python:如何重写类';方法,同时保留装饰器并调用原始的?

Python:如何重写类';方法,同时保留装饰器并调用原始的?,python,python-2.7,decorator,descriptor,Python,Python 2.7,Decorator,Descriptor,基于这种情况,如何重写类的方法?但是,我无法编辑solid.py并运行_me.py。在维护装饰的同时,能够调用原始 # - solid.py - (no control) import http class Solid(object): _cp_path = '/pos' @http.jsonrequest def break_apart(self): return "to pieces!" ) ) 编辑 谢谢你的帮助,很抱歉不完整。我忘了添加sol

基于这种情况,如何重写类的方法?但是,我无法编辑solid.py并运行_me.py。在维护装饰的同时,能够调用原始

# - solid.py - (no control)
import http

class Solid(object):
    _cp_path = '/pos'

    @http.jsonrequest
    def break_apart(self):
        return "to pieces!"
)

)

编辑

谢谢你的帮助,很抱歉不完整。我忘了添加solid.py有decorators。但是,使用
monkeypatch
就像一种魅力:

  • 我把原来的装饰弄丢了
  • 我不能称之为原创

    AttributeError:“super”对象没有属性“break\u-spart”


由于Python的函数是一流的,因此将您的me.py设置为:

import solid

# replacement method
def break_apart(self):
    return "to sand!"

# override Solid.break_apart with the new method
solid.Solid.break_apart = break_apart
另外,由于您调用了pebble.break_spart(),这意味着solid.py应该是:

class Solid(object):
    def break_apart(self):
        return "to pieces!"

请注意,添加了self参数以将_分开。当您调用pebble.break_spare()(即self=pebble实例)时,这就暗示了这一点。首先,您实际上有什么样的类方法

它看起来像一个
staticmethod
,但您没有使用
staticmethod
装饰器

实例方法如下所示:

class C:
    def instance_method(self, *args, **kwargs):
        return self.foo
    @classmethod
    def class_method(cls, *args, **kwargs):
        return cls.foo
    @staticmethod
    def static_method(*args, **kwargs):
        return foo
类方法如下所示:

class C:
    def instance_method(self, *args, **kwargs):
        return self.foo
    @classmethod
    def class_method(cls, *args, **kwargs):
        return cls.foo
    @staticmethod
    def static_method(*args, **kwargs):
        return foo
静态方法如下所示:

class C:
    def instance_method(self, *args, **kwargs):
        return self.foo
    @classmethod
    def class_method(cls, *args, **kwargs):
        return cls.foo
    @staticmethod
    def static_method(*args, **kwargs):
        return foo
如果您无法更改
run_me.py
,则无法使用继承来更改此代码

相反,您可以简单地
monkeypatch
Solid类,并使用
break\u-parate
的兼容实现,如下所示:

import solid

def break_apart(self):
    return "whatever you want"

solid.Solid.break_apart = break_apart
当然,由于您的代码是首先导入的,所以实际上您可以替换整个类

因此,只需定义
Concrete
,然后在
solid
模块中对整个类进行猴子补丁:

import solid

class Concrete:
    ...

solid.Solid = Concrete

我认为有两种不同的方法可以用来重写一个类中的方法,而这个类不能编辑源代码

第一个是创建重写该方法的子类。这非常简单,但它只影响您使用子类的构造函数而不是原始类创建的对象:

me.py:

import http

import solid

class MySolid(solid.Solid):
    @http.jsonjequest
    def break_apart(self):
        return "to sand! and " + super(solid.Solid, self).break_apart()
import http

import solid

_orig_break_apart = solid.Solid.break_apart

@http.jsonrequest
def break_apart(self):
    return "to sand! and " + _orig_break_apart(self)

solid.Solid.break_apart = break_apart
实际上,我不知道http.jsonrequest装修器是做什么的,因此如果它以某种方式修改了调用签名,您可能需要将调用更改为原始值来使用它

另一种方法是对现有类进行修补。这意味着您将用自己的替代版本替换类中的方法实现。如果原始类的实例是由您也无法控制的其他代码创建的,那么这将非常有用。请注意,如果仍然需要访问原始方法实现,则需要自己保存对它的引用(
super
不处理这种情况)

me.py:

import http

import solid

class MySolid(solid.Solid):
    @http.jsonjequest
    def break_apart(self):
        return "to sand! and " + super(solid.Solid, self).break_apart()
import http

import solid

_orig_break_apart = solid.Solid.break_apart

@http.jsonrequest
def break_apart(self):
    return "to sand! and " + _orig_break_apart(self)

solid.Solid.break_apart = break_apart
在这里,如果装饰者更改了签名,您可能需要更改调用原始方法的方式


方法的修饰版本可能与未修饰版本有很大的不同(例如,它从根本上改变了调用签名或返回值)。如果是这样的话,可能不容易“反转”这个过程以获得原始方法实现,从而可以从新版本中调用它。在这种情况下,您可能需要将代码从现有实现复制到覆盖版本中(使用上述任一覆盖方法)。在示例代码中,您只需返回
“to sand!and to parties!”
,而不尝试调用原始方法。在实际代码中,这可能会更复杂,但同样的想法也适用。

站在stackoverflow巨人的肩膀上,这对我来说很有用。我知道这有点复杂,但我还没有发现更简单的东西。代码在Python3上工作

import functools
def log( fn ):
    @functools.wraps( fn )
    def wrapper( *args, **kwargs ):
        print( "log", fn.__name__, args[ 1: ] )
        return fn( *args, **kwargs )
    return wrapper

def prepare_class( clazz ):
    @classmethod
    def on_class_patcher( cls, func_name, context ):
        def patch_by_name( new_func) :
            original_func = getattr( cls, func_name )
            def patched_func( self, *args, **kwargs ):
                return new_func( self, original_func, context, *args, **kwargs )
            setattr( cls, func_name, patched_func )
        return patch_by_name

    setattr( clazz, "patch", on_class_patcher )

# --- Use like this ---
class Demo:
    @log
    def m1( self, x, y ):
        print( "Demo.m1" )
        print( x, y )

    def m2( self ):
        print( "Demo.m2" )

class Foo:
    def m3( self ):
        print( "Foo.m3" )

prepare_class( Demo )
foo = Foo()

@Demo.patch( "m1", context = { "foo": foo } )
def patched_m1( self, original_func, context, *args, **kwargs ):
    print( "Demo.m1_patched" )
    self.m2()
    context[ "foo" ].m3()
    x = args[ 0 ] * 2
    y = args[ 1 ] * 2
    return original_func( self, x, y )

demo = Demo()
demo.m1( 1, 2 )
结果:

Demo.m1_patched
Demo.m2
Foo.m3
log m1 (2, 4)
Demo.m1
2 4

你是否需要一个
实体
来表现得像一个
混凝土
而不是一个实体?如果是的话,你是如何陷入这种情况的?