2

Using python 3.6. I'm trying to write some code that will allow me to to place a unique identifier on class methods.

Here's an example:

def test(func):
    def wrap():
        pass
    wrap.__name__ += '_TEST'
    return wrap

class example:
    @test
    def method(self):pass

Console:

import inspect
Bar = example()
inspect.getmembers(Bar, predicate=inspect.ismethod)
Out[3]: 
[('method', # Name didn't change 
<bound method test.<locals>.wrap of <__main__.example object at 
0x000002670E073EF0>>)]
Bar.method.__name__
Out[4]: 'wrap_TEST'

I want to be able to loop over methods of a class and only execute the ones I have marked with my decorator. Can this be done?

James Schinner
  • 1,549
  • 18
  • 28
  • 1
    Method lookups don't return the actual functions back, they are itself a thin wrapper over the function. Your actual function has been renamed if you check: `Bar.method.__func__.__name_`. – Ashwini Chaudhary Aug 29 '17 at 11:37
  • could you explain more what the decorator is trying to achieve? It seems like it's a bit too complicated to do it that way. Does the decorator actually change how the function behaves or only change the name? – MSeifert Aug 29 '17 at 11:37
  • It's also due to how [`gemembers()` works](https://github.com/python/cpython/blob/3.6/Lib/inspect.py#L326), it uses `dir()` to get the members and that will always going to return `method` because that's what exists in the instance or class dictionary. – Ashwini Chaudhary Aug 29 '17 at 11:43
  • yeah ta, I'll remember that :) – James Schinner Aug 29 '17 at 12:24

2 Answers2

3

Rather than trying to change the name of the methods you have created, you can use the fact that functions and methods are first class objects to assign a property to the method itself.

In the following code, you can create a simple decorator that adds the property TEST to the methods you want to test.

def test(func):
    func.TEST = True
    return func


class example:
    @test
    def method(self):
        pass

ex = example()
ex.method.TEST
# returns:
True
James
  • 32,991
  • 4
  • 47
  • 70
2

You could use a boolean flag to mark the functions. Here is my suggestion:

import inspect

def mark(func):
    func.flag = True
    return func

class C:
    @mark
    def a(self):
        pass

    @mark
    def b(self):
        pass

    def c(self):
        pass

methods = [m for n, m in inspect.getmembers(C) if hasattr(m, 'flag')]
print([m.__name__ for m in methods]) #=> ['a', 'b']
Danil Speransky
  • 29,891
  • 5
  • 68
  • 79