1 Invocations in classic Python:
2
3 f(1, 2, 3) # positional
4 f(1, 2) # positional with defaults
5 f(1, 2, c=3) # keywords
6 f(1, c=3) # keywords with defaults
7 f(1, 2, 3, 4) # extra positional arguments
8 f(1, 2, 3, d=4) # extra keyword arguments
9 f(1, 2, *args) # positional bundles (possibly with defaults)
10 f(1, 2, **kw) # keyword bundles (possibly with defaults)
11
12 Note that f is never fixed before run-time in Python.
13
14 Comparison to invocations in C:
15
16 f(1, 2, 3) # positional, f known at compile-time
17 f(1, 2, 3) # positional, f is appropriate function pointer
18 # ie. (*f)(A, B, C)
19
20 Least expensive cases:
21
22 f(1, 2, 3) # put arguments in frame
23 # if f is not known, add arguments vs. parameters check
24 f(1, 2) # to handle defaults, introduce default "filling" where
25 # not enough arguments are given
26 # if f is not known, this is obviously done at run-time
27
28 More expensive cases:
29
30 f(1, 2, c=3) # prepare frame using parameter details
31 # (provided c is a known parameter)
32 # if f is not known, this is obviously done at run-time
33 f(1, c=3) # as with the previous case, with default "filling" done
34 # where not enough arguments are given
35 # if f is not known, this is obviously done at run-time
36 # but with all defaults copied in before keywords are
37 # assigned (since their positions and thus the positions
38 # of missing parameters cannot be known)
39
40 Awkward cases:
41
42 f(1, 2, 3, 4) # extra positional arguments
43 f(1, 2, 3, d=4) # extra keyword arguments
44 f(1, 2, *args) # positional bundles (possibly with defaults)
45 f(1, 2, **kw) # keyword bundles (possibly with defaults)
46
47 These cases require additional structures to be created, potentially at
48 run-time.
49
50 Methods vs. functions:
51
52 f(obj, 1, 2) # f known as function at compile-time:
53 # f(obj, 1, 2)
54 # f known as C.m at compile-time:
55 # m(obj "assert isinstance(obj, C)", 1, 2)
56 # f not known at compile-time:
57 # f(<context>, obj, 1, 2) for instance-accessed methods
58 # f(obj, 1, 2) for class-accessed methods
59 # f(obj, 1, 2) for functions
60
61 (Could either have universal context usage even for functions, which would
62 ignore them, or attempt to remove contexts when functions are called.)
63
64 Argument lists for functions:
65
66 f(obj, 1, 2) # f known as function at compile-time
67
68 f -> don't get any context information
69 obj -> argument #1
70 1 -> argument #2
71 2 -> argument #3
72
73 Argument lists for methods:
74
75 f(obj, 1, 2) # f known as C.m at compile-time (context is C)
76
77 f -> C.m - don't get any context information
78 obj -> argument #1
79 1 -> argument #2
80 2 -> argument #3
81
82 Argument lists for methods:
83
84 f(obj, 1, 2) # f known as C.m at compile-time (context is an instance)
85
86 f -> C.m
87 -> context is argument #1
88 obj -> argument #2
89 1 -> argument #3
90 2 -> argument #4
91
92 Argument lists for classes:
93
94 f(obj, 1, 2) # f known as C at compile-time
95
96 f -> C.__init__
97 -> new instance is argument #1
98 obj -> argument #2
99 1 -> argument #3
100 2 -> argument #4
101
102 The new instance must be manually provided as the result after the call.
103
104 Argument lists for unknown callables:
105
106 f(obj, 1, 2) # f not known at compile-time
107
108 f -> f
109 -> load context for argument #1
110 obj -> argument #2
111 1 -> argument #3
112 2 -> argument #4
113
114 Then, check the context and shift the frame if necessary:
115
116 <context> is module or class:
117 (<context>, obj, 1, 2) -> (obj, 1, 2)
118
119 <context> is instance: no change
120
121 Argument lists in instantiators:
122
123 f(obj, 1, 2) # f not known at compile-time
124
125 f -> C.__new__ (known and called at run-time)
126
127 Need to call C.__init__(<instance>, obj, 1, 2), preferably with the existing
128 frame:
129
130 <insert instance before received arguments>
131 obj -> argument #1
132 1 -> argument #2
133 2 -> argument #3
134
135 Then jump without switching frames.
136 It should be possible to replace the old, tentative context information in the
137 frame.
138
139 Defaults for unknown callables:
140
141 f(obj) # f not known at compile-time
142
143 f -> f
144 -> load context for argument #1
145 obj -> argument #2
146
147 Then, check the number of arguments and the availability of defaults against
148 the details provided by the callable's structure.
149
150 Checking defaults for unknown callables:
151
152 Approach #1 - pre-fill defaults, add arguments, check frame
153
154 Approach #2 - add arguments, add defaults while checking frame
155
156 Functions as methods:
157
158 def f(x, y, z): ...
159 class C:
160 m = f
161 c = C()
162 ...
163 f(obj, 1, 2) # no restrictions on obj
164 obj.m(1, 2) # f(obj, 1, 2)
165 C.m(obj, 1, 2) # f(obj "assert isinstance(obj, C)", 1, 2)
166
167 Context propagation:
168
169 fn = C.m # has context C
170 fn(obj, 1, 2) # non-instance context -> explicit context required
171 # must perform isinstance(obj, C)
172 fn = c.m # table entry for m on C -> replace context
173 # gives context c
174 fn(1, 2) # instance context -> no explicit context required
175 # context c inserted in call