19

I have a decorator that logs the name of the decorated function (among other things).

Is it safe to access the __name__ attribute of the decorated function? Or is there some callable type that doesn't have name that I have not run into yet?

alejandro
  • 1,234
  • 1
  • 9
  • 19

2 Answers2

23

Answering my own question with a summary of other answers and comments.

Not all callables have __name__.

  • Instances of classes that define __call__ may not have a __name__ attribute.

  • Objects created with functools.partial do not have a __name__ attribute.

A workaround is to use the three argument getattr:

name = getattr(callable, '__name__', 'Unknown')

Or use repr(callable) instead of 'Unknown', which would include more information about the callable:

name = getattr(callable, '__name__', repr(callable))
alejandro
  • 1,234
  • 1
  • 9
  • 19
10

It is pretty safe with functions:

def d():
    pass

l = lambda: None
print(d.__name__)
print(l.__name__)

Output:

> d 
> <lambda>

You can even safely modify it (for debuging purposes as an example):

l.__name__ = 'Empty function'
print(l.__name__)  # Empty function

But beware of classes with __call__ method, they don't have a __name__ attribute.

class C(object):
    def __call__(self):
        print('call')

print(C().__name__)  # AttributeError: 'C' object has no attribute '__name__'

Updated

As @felipsmartins pointed out in comments, functools.partial also doesn't have __name__ attribute. Partial Objects refer to the original function with the member variable func which let's you get the original function's name like so:my_partial.func.__name__.

Related:

What is a "callable"?

cbare
  • 12,060
  • 8
  • 56
  • 63
sobolevn
  • 16,714
  • 6
  • 62
  • 60
  • 1
    Also, note partial objects not creates `__name__` attribute. – felipsmartins Nov 07 '15 at 00:22
  • 3
    Of course, you can just ignore the issue by using three-arg `getattr`: `getattr(callable, '__name__', 'Unknown')` will avoid exceptions and give you some output. Alternatively, `getattr(callable, '__name__', repr(callable))` gets you a more tailored `repr` version (which should help with instances whose class implements `__call__`. – ShadowRanger Nov 07 '15 at 00:59