- cpython/Objects/funcobject.c
- cpython/Include/funcobject.h
- cpython/Objects/clinic/funcobject.c.h
everything is an object in python, including function, a function is defined as PyFunctionObject in the c level
def f(a, b=2):
print(a, b)
>>> type(f)
function
the type function indicates the user-defined method/classes, for builtin_function_or_method please refer to method
let's figure out the meaning of each field in the PyFunctionObject
func_code field stores an instance of PyCodeObject, which contains information of a code block
A code block must contain python virtual machine's instruction, argument number, argument body and etc
I will explain PyCodeObject in other article
>>> f.__code__
<code object f at 0x1078015d0, file "<stdin>", line 1>
the global namespace attached to the function object
>>> type(f.__globals__)
<class 'dict'>
>>> f.__globals__
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'f': <function f at 0x10296eac0>}
a tuple stores all the default argument of the function object
>>> f.__defaults__
(2,)
field func_kwdefaults is a python dictionary, which stores the keyword-only argument with default value
def f2(a, b=4, *x, key=111):
print(a, b, x, key)
>>> f2.__kwdefaults__
{'key': 111}
the func_closure field is a tuple, indicate all the enclosing level of the current function object
def wrapper(func):
def func_inside(*args, **kwargs):
print("calling func", func)
func(args, kwargs)
print("call done")
return func_inside
@wrapper
def my_func():
print("my func")
>>> my_func.__closure__
(<cell at 0x10911c928: function object at 0x1092b3c40>,)
>>> my_func
<function wrapper.<locals>.func_inside at 0x1092b3b40>
>>> wrapper
<function wrapper at 0x1092b3cc0>
let's see an example with more _closure_
def wrapper2(func1):
def func_inside1(func2):
def func_inside2(*args2, **kwargs2):
print("calling func1", func1)
r = func1(*args2, **kwargs2)
print("calling func2", func2)
r = func2(*args2, **kwargs2)
print("call done")
return r
print("func_inside2.__closure__", func_inside2.__closure__)
return func_inside2
print("func_inside1.__closure__", func_inside1.__closure__)
return func_inside1
@wrapper2
def my_func2():
print("my func2")
def func3():
print("func3")
# m = my_func()
inside2 = my_func2(func3)
print("----------------")
inside2()
# output
(<cell at 0x100e69eb8: function object at 0x100e6fea0>,)
func_inside1.__closure__ (<cell at 0x1087e9408: function object at 0x1087f1ae8>,)
func_inside2.__closure__ (<cell at 0x1087e9408: function object at 0x1087f1ae8>, <cell at 0x1087e9498: function object at 0x1087f1bf8>)
----------------
calling func1 <function my_func2 at 0x1087f1ae8>
my func2
calling func2 <function func3 at 0x1087f1bf8>
func3
call done
usually, it's an unicode object for explanation
def f():
"""
I am the document
"""
pass
print(f.__doc__)
the name of the PyFunctionObject object
def i_have_a_very_long_long_long_name():
pass
print(i_have_a_very_long_long_long_name.__name__)
# output
# i_have_a_very_long_long_long_name
func_dict field stores the attribute of the function object
>>> f.__dict__
{}
>>> f.a = 3
>>> f.__dict__
{'a': 3}
func_module field indicate the module which the PyFunctionObject attached to
>>> f.__module__
'__main__'
>>> from urllib.parse import urlencode
>>> urlencode.__module__
'urllib.parse'
you can read PEP 3107 -- Function Annotations for more detail
def a(x: "I am a int" = 3, y: "I am a float" = 4) -> "return a list":
pass
>>> a.__annotations__
{'x': 'I am a int', 'y': 'I am a float', 'return': 'return a list'}
it's used for nested class/function representation, it contains a dotted path leading to the object from the module top-level, refer PEP 3155 -- Qualified name for classes and functions for more detail
def f():
def g():
pass
return g
>>> f.__qualname__
'f'
>>> f().__qualname__
'f.<locals>.g'