Function and Its Attributes

Python function with data attributes.

Table of Contents

Function Attributes


Functions in Python are objects of a specific class. Like any other object, they have attributes that can be accessed via dot notation: object.name.

def foo(a: int, b: int = 2,*, c: bool = True) -> int:
    """Docstring"""
    if c:
        return a + b
    return a - b

print(foo.__name__, foo.__class__)
# foo <class 'function'>
print(foo.__doc__)
# Docstring
print(foo.__module__)
# __main__
print(foo.__annotations__)
# {'a': <class 'int'>, 'b': <class 'int'>, 'c': <class 'bool'>, 'return': <class 'int'>}
print(foo.__defaults__) # positional
# (2,)
print(foo.__kwdefaults__) # keyword-only
# {'c': True}

Built-in functions also have attributes.

print(print.__name__, print.__class__)
# print <class 'builtin_function_or_method'>
print(print.__doc__)
# Prints the values to a stream, or to sys.stdout by default. ...
print(print.__module__)
# builtins
print(print.__text_signature__)
# ($module, /, *args, sep=' ', end='\n', file=None, flush=False)

Adding Attributes to a Function


User-defined functions also allow getting and setting arbitrary attributes. We can add and modify attributes in the function body or later when we use it at runtime.

def foo(x):
    foo.attr1 = x

foo(0)
print(foo.attr1)
# 0
foo.attr1 = 1
print(foo.attr1)
# 1
foo.attr2 = 2
print(foo.attr2)
# 2

We can use other Python objects as function attributes.

def foo(x):
    def f1():
        return x
    foo.x = x
    foo.f1 = f1

foo(3)
print(foo.x)
# 3
print(foo.f1())
# 3
foo.x = 8
print(foo.x)
# 8
print(foo.f1())
# 3
print(foo.__dict__)
# {'x': 8, 'f1': <function foo.<locals>.f1 at 0x000002361F03CB80>}

Functions and AttributeError


1. CPython does not support arbitrary function attributes for built-in functions.

sorted.attr = 1
# AttributeError: 'builtin_function_or_method' object has no attribute 'attr'

2. A function attribute defined inside a function does not exist until that function is called.

def foo():
    foo.attr = 1
    
print(foo.attr)
# AttributeError: 'function' object has no attribute 'attr'

3. Like function objects, bound method objects support getting attributes. But to set a method attribute, you need to explicitly set it on the underlying function object.

class A:
    def method(self):
        pass

a = A()

print(A.method)
# <function A.method at 0x00000247307ECB80>

print(a.method)
# <bound method A.method of <__main__.A object at 0x000002472DF4AC50>>

# underlying function object
print(a.method.__func__)
# <function A.method at 0x00000247307ECB80>

a.method.attr = 1
# AttributeError: 'method' object has no attribute 'attr'

# set attribute
a.method.__func__.attr = 1

# get attribute
print(a.method.attr)
# 1

References:

  1. Docs: Function attributes
  2. Docs: Method attributes
  3. PEP 232 - Function Attributes