Python:如何重写类';方法,同时保留装饰器并调用原始的?
基于这种情况,如何重写类的方法?但是,我无法编辑solid.py并运行_me.py。在维护装饰的同时,能够调用原始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 - (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”
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
你是否需要一个
实体
来表现得像一个混凝土
而不是一个实体?如果是的话,你是如何陷入这种情况的?