How to extend Class instance

前端 未结 4 1214
礼貌的吻别
礼貌的吻别 2021-01-20 06:50

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 MyCla         


        
相关标签:
4条回答
  • 2021-01-20 06:53

    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.

    0 讨论(0)
  • 2021-01-20 07:00

    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
    
    0 讨论(0)
  • 2021-01-20 07:01

    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
    
    0 讨论(0)
  • 2021-01-20 07:16

    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)
    
    0 讨论(0)
提交回复
热议问题