How to extend Class instance

不羁岁月 提交于 2019-12-20 02:01:43

问题


MyClass is defined in module.py. There is no way we can modify it. But we do know the Class definition looks like this:

class MyClass:
    def method(self, msg):
        print 'from method:', msg

I start my script with importing the module and then declaring an object's instance:

import module    
foo = module.MyClass()

Then I write my own function:

def function(msg):
    print 'from function:', msg

Now, every time foo.method('') is used I want to call function() so it prints the same message too.

Would this situation be referred as the monkey patching? How to achieve what is needed?


回答1:


Yes, it's called monkey-patching.

This is basically decoration, but done manually after the class is already defined.

from functools import wraps

def wrapper(f):
    @wraps(f)
    def wrapped(*args, **kwargs):
        myFunction()
        return f(*args, **kwargs)
    return wrapped

MyClass.printThis = wrapper(MyClass.printThis)

It will affect all instances of MyClass, even those that were created before the patch was applied.

If you don't need to dynamically modify runtime behaviour, avoid monkey-patching and prefer to use inheritance to customise behaviour, as suggested in the comments. It's less hacky.




回答2:


This is an alternative to wim's answer that also involves monkey-patching. However, it does it through functionality provided by unittest.mock. The advantage of this approach is that a context manager is used to automatically apply and remove the patch within a limited scope:

from unittest import mock

# This class would be defined in some third-party library
class MyClass:
    def method(self, msg):
        print('from method:', msg)


def function(msg):
    print('from function:', msg)


old_method = MyClass.method


def new_method(self, msg):
    old_method(self, msg)
    function(msg)


# The patch is only applied within this scope
with mock.patch.object(MyClass, 'method', new_method):
    foo = MyClass()
    foo.method('message with patched')

# By this point MyClass is "back to normal"
print('---')
foo.method('message with original')

Output

from method: message with patched
from function: message with patched
---
from method: message with original



回答3:


You could subclass it as well:

class MyClass:
    def method(self, msg):
        print 'from method:', msg

def function(msg):
    print 'from function:', msg

class MyNewClass(MyClass):
    def method(self, msg):
        function(msg)
        MyClass.method(self, msg)

And use it like:

>>> a = MyNewClass()
>>> a.method("test")
from function: test
from method: test

Or, if you want to make your class a "new-style" class (for Python 2 - judging by your print statements) - just have MyClass inherit from object and then you can user super:

class MyClass(object):  # object added here
    def method(self, msg):
        print 'from method:', msg

def function(msg):
    print 'from function:', msg

class MyNewClass(MyClass):
    def method(self, msg):
        function(msg)
        super(self.__class__, self).method(msg)  # super added here



回答4:


Ended up with this solution till there is better one posted...

class MyClass:
    def method(self, msg):
        print 'from method:', msg

def function(msg, callback):
    print 'from function:', msg
    callback(msg)

foo = MyClass()
foo.function = function
foo.function(msg='message', callback=foo.method)


来源:https://stackoverflow.com/questions/41904124/how-to-extend-class-instance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!