###
2.1.7.3 Functions

Function objects are created by function definitions. The only operation on a function object is to call it: `func(argument-list)`.

There are really two flavors of function objects: built-in functions and user-defined functions. Both support the same operation (to call the function), but the implementation is different, hence the different object types.

The implementation adds two special read-only attributes: `f.func_code` is a function's code object (see below) and `f.func_globals` is the dictionary used as the function's global namespace (this is the same as `m.__dict__` where m is the module in which the function f was defined).

Function objects also support getting and setting arbitrary attributes, which can be used to, e.g. attach metadata to functions. Regular attribute dot-notation is used to get and set such attributes. Note that the current implementation only supports function attributes on functions written in Python. Function attributes on built-ins may be supported in the future.

Functions have another special attribute `f.__dict__` (a.k.a. `f.func_dict`) which contains the namespace used to support function attributes. `__dict__` can be accessed directly, set to a dictionary object, or `None`. It can also be deleted (but the following two lines are equivalent):

      del func.__dict__
      func.__dict__ = None