Functions as objects in Python: what exactly is stored in memory?

I have been using Python for practical tasks for a long time, but I still do not have a correct theoretical understanding of what is happening behind the hood. For example, I'm trying to understand how Python manages to treat functions as objects. I know that functions are objects of the class 'function', with the method 'call', and I know that I can make my classes created by the user as functions by writing a “call method” for them. But I can’t understand what exactly is stored in memory when creating new functions and how to access information that will be saved.

To experiment, I wrote a little script that creates many function objects and stores them in a list. I noticed that this program has a lot of memory.

funct_list = []
for i in range(10000000):
    def funct(n):
        return n + i
    funct_list.append(funct)

My questions:

  • What exactly is stored in RAM when I define a new function object? Am I keeping details of how the function should be implemented?

  • If so, does the object of my function have attributes or methods that allow me to “check” (or perhaps even “change retrospectively”) how the function works?

  • Perhaps my previous question is round because the methods of a function object are eigenfunctions ...

  • In my code above, part of the RAM is used simply to store "pointers" to my function objects in a list. The rest of the RAM seems to be used to store interesting stuff about how my function objects work. Roughly, how is RAM allocated between these two goals?

  • , , . RAM? ( . , 1000 , .)

. Google, , !

+6
2

. , , , , , , , .

, , -. , , . , :

>>> def fib(i):
...     x, y = 0, 1
...     for _ in range(i):
...         x, y = y, x+y
...     return x
... 
>>> fib.__code__.co_code
b'd\x03\\\x02}\x01}\x02x\x1et\x00|\x00\x83\x01D\x00]\x12}\x03|\x02|\x01|\x02\x17\x00\x02\x00}\x01}\x02q\x1
2W\x00|\x01S\x00'

... .

- Python , . dis, - :

>>> import dis
>>> dis.dis(fib)
  2           0 LOAD_CONST               3 ((0, 1))
              2 UNPACK_SEQUENCE          2
              4 STORE_FAST               1 (x)
              6 STORE_FAST               2 (y)

  3           8 SETUP_LOOP              30 (to 40)
             10 LOAD_GLOBAL              0 (range)
             12 LOAD_FAST                0 (i)
             14 CALL_FUNCTION            1
             16 GET_ITER
        >>   18 FOR_ITER                18 (to 38)
             20 STORE_FAST               3 (_)
  4          22 LOAD_FAST                2 (y)
             24 LOAD_FAST                1 (x)
             26 LOAD_FAST                2 (y)
             28 BINARY_ADD
             30 ROT_TWO
             32 STORE_FAST               1 (x)
             34 STORE_FAST               2 (y)
             36 JUMP_ABSOLUTE           18
        >>   38 POP_BLOCK
  5     >>   40 LOAD_FAST                1 (x)
             42 RETURN_VALUE

, , ALL_CAPS .

ALL_CAPS -. , LOAD_CONST , BINARY_ADD - +. -. , LOAD_CONST 3 , 3 . , - -. , , , 3 LOAD_CONST 3 (0, 1) 1 STORE_FAST 1 x, -; .


- , , dict, , , , , __name__.

Python 3.6 C:

typedef struct {
    PyObject_HEAD
    PyObject *func_code;    /* A code object, the __code__ attribute */
    PyObject *func_globals; /* A dictionary (other mappings won't do) */
    PyObject *func_defaults;    /* NULL or a tuple */
    PyObject *func_kwdefaults;  /* NULL or a dict */
    PyObject *func_closure; /* NULL or a tuple of cell objects */
    PyObject *func_doc;     /* The __doc__ attribute, can be anything */
    PyObject *func_name;    /* The __name__ attribute, a string object */
    PyObject *func_dict;    /* The __dict__ attribute, a dict or NULL */
    PyObject *func_weakreflist; /* List of weak references */
    PyObject *func_module;  /* The __module__ attribute, can be anything */
    PyObject *func_annotations; /* Annotations, a dict or NULL */
    PyObject *func_qualname;    /* The qualified name */

    /* Invariant:
     *     func_closure contains the bindings for func_code->co_freevars, so
     *     PyTuple_Size(func_closure) == PyCode_GetNumFree(func_code)
     *     (func_closure may be NULL if PyCode_GetNumFree(func_code) == 0).
     */
} PyFunctionObject;

, ,

  • dict,
  • ,
  • ,
  • ,
  • docstring,
  • ,
  • __dict__,
  • ,
  • __module__,
  • __qualname__,

PyObject_HEAD refcount/GC.

C, - dir , Python, , , .

, , , . .

bytestring - Python. . , , , , , , . - , . , , , , co_flags ( ) co_stacksize ( , ), .

+6

- , : ( ). type(f), f types (types.FunctionType).

, Python . def, , - .

, Python , . , , , , , .

inspect . , (, ) .

, , Python. dis.

help() , , . , - , ..

help(type(lambda: 0))
help(type((lambda: 0).__code__))

compile(), .

, __call__(), . , __call__(). . , __call__(), __call__(), ad nauseam, ad infinitum.

? Python __call__ __call__, C, Python __call__. , (lambda: 0).__call__ method-wrapper, C.

+6

All Articles