forked from zpoint/CPython-Internals
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmethod.md
More file actions
131 lines (88 loc) · 4.74 KB
/
Copy pathmethod.md
File metadata and controls
131 lines (88 loc) · 4.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# method
# contents
* [related file](#related-file)
* [memory layout](#memory-layout)
* [example](#example)
* [print](#print)
* [fields in PyMethodDef](#fields-in-PyMethodDef)
* [free_list](#free_list)
* [classmethod](#classmethod)
* [staticmethod](#staticmethod)
# related file
* cpython/Objects/methodobject.c
* cpython/Include/methodobject.h
* cpython/Python/bltinmodule.c
* cpython/Objects/call.c
# memory layout
There's a type named **builtin_function_or_method** in python, as the type name described, all the builtin function or method defined in the c level belong to **builtin_function_or_method**
```python3
>>> print
<built-in function print>
>>> type(print)
<class 'builtin_function_or_method'>
```

# example
## print
let's read code snippet first
```c
#define PyCFunction_Check(op) (Py_TYPE(op) == &PyCFunction_Type)
typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
typedef PyObject *(*_PyCFunctionFast) (PyObject *, PyObject *const *, Py_ssize_t);
typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *,
PyObject *);
typedef PyObject *(*_PyCFunctionFastWithKeywords) (PyObject *,
PyObject *const *, Py_ssize_t,
PyObject *);
typedef PyObject *(*PyNoArgsFunction)(PyObject *);
```
The **PyCFunction** is a type in c, any c function with signature(accept two PyObject* as parameters and return a PyObject *) can be cast to type **PyCFunction**
```c
// a c function named builtin_print
static PyObject *
builtin_print(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames);
// name "print" is defined inside a c array named builtin_methods, and defined s type PyMethodDef
static PyMethodDef builtin_methods[] = {
...
{"print", (PyCFunction)(void(*)(void))builtin_print, METH_FASTCALL | METH_KEYWORDS, print_doc},
...
}
```

a **PyMethodDef** should be attached to a **module** object, and a **self** arugment, and then a **PyCFunctionObject** will be generated
what user really interactive with is **PyCFunctionObject**

let's check for more detail of each field
the type in **m_self** field is **module**, and type in **m_module** field is **str**

# fields in PyMethodDef
## ml_name
as you can see in the above picture, field **ml_name** is the name of the built-in method, it's a c style null terminated string "print"
## ml_meth
the real c function pointer that does the job
## ml_flags
bit flag indicates how the c function's behave in the python level
the function in **call.c** begin with **_PyMethodDef_** will delegate the work to the **PyCFunction**, but with different calling behaviour according to different **ml_flags**
for more detail please refer to [c-api Common Object Structures](https://docs.python.org/3/c-api/structures.html)
| flag name | flag value | meaning |
| - | :-: | -: |
| METH_VARARGS | 0x0001| methods type PyCFunction, expects two PyObject* values |
| METH_KEYWORDS | 0x0002 | methods type PyCFunctionWithKeywords, expects three parameters: self, args, and a dictionary of all the keyword arguments |
| METH_NOARGS | 0x0004 | methods without parameters, need to be of type PyCFunction |
| METH_O | 0x0008 | methods with a single object argument, have the type PyCFunction |
| METH_CLASS | 0x0010 | the type object will be passed as first parameter, what @classmethod do |
| METH_STATIC | 0x0020 | null will passed as first parameter, what @staticmethod do |
| METH_COEXIST | 0x0040 | replace existing defination instead of skip |
# free_list
```c
static PyCFunctionObject *free_list = NULL;
static int numfree = 0;
#ifndef PyCFunction_MAXFREELIST
#define PyCFunction_MAXFREELIST 256
#endif
```
cpython use a buffer pool with size 256 to reuse the deallocated **PyCFunctionObject** object, free_list is a single linked list, all elements are chained by the **m_self** field
the similar technique appears in float object, the float object chained through the **ob_type** field, I will not draw again, user who need the graph representation please click the link [float(free_list)](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/float/float.md#free_list)
# classmethod
# staticmethod
they're more related in **classobject**, I will talk about them later in [class object](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/class/class.md)