0
def decorator(*args, **kwargs):
    def inner(f):
        print(kwargs['here'])
        return f
    return inner

class Test:
    def test(self, a, b):
        @decorator(here='access/change this value')
        def add(a, b):
            print(a + b)
        add(a, b)
        return

if __name__ == '__main__':
    t = Test()
    t.test(1, 2)

How to use "t" to extract or change the value "access/change this value" from a decorator's args on a subfunction. getattribute can only help get function(self) not subfunction, am I right?

For example, I would like to get the value "access/change this value" without running function test. Or change arg here = [original str] + "a new str"

kjjin
  • 11
  • 3

2 Answers2

0

As the decorator is applied to a function defined inside the Test.test method (hence a new one is created each time the function runs), we can't target the add function, but we can mess with the decorator reference.

from unittest import mock as mocking

if __name__ == '__main__':
    t = Test()
    with mocking.patch("__main__.decorator") as decorator_mock:
        # create another version of the decorator, the one we want
        def my_other_dorator(*args, **kwargs):
            def inner(f):
                print(f"HELLO {args=!r} {kwargs=!r}")
                return f
            return inner
        # and make that it is this one that gets called when `@decorator(...)` is executed
        decorator_mock.side_effect = my_other_dorator
        # now that all is in place, let it run !
        t.test(1, 2)
HELLO args=() kwargs={'here': 'access/change this value'}
3

What happened is that we replaced the decorator definition by another, so that we can do whatever we want.
The sublibrary mock comes from the standard library unittest because mocks are very useful for testing, but as monkey patching is part of the Python philosophy, I don't see a problem with using it. But it may lead to very surprising behaviors, so I would not suggest it as a best practice though.

Lenormju
  • 4,078
  • 2
  • 8
  • 22
0

If you change class Test slightly, you can do this in a reasonable simple way

def decorator(*args, **kwargs):
    def inner(f):
        print(kwargs['here'])
        return f
    return inner

class Test:
    here = "access/change this value" # 1. Add this class member
    def test(self, a, b):
        @decorator(here=self.here) # 2. Use the class member as a decorator kwarg
        def add(a, b):
            print(a + b)
        add(a, b)

if __name__ == '__main__':
    t = Test()
    t.here = "a new value"
    t.test(1, 2)

I believe this lets you do what you want to do with the decorator, and it doesn't require deep diving into the innards of python objects like in this answer (which might be of interest if you actually did want to play surgeon on python function objects).

JJ Hassan
  • 395
  • 3
  • 11