Skip to content

Commit 8c45639

Browse files
mariocj89brettcannon
authored andcommitted
PEP 570: Positional-Only Parameters (#564)
1 parent d084110 commit 8c45639

File tree

1 file changed

+322
-0
lines changed

1 file changed

+322
-0
lines changed

pep-0570.rst

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
PEP: 570
2+
Title: Python Positional-Only Parameters
3+
Version: $Revision$
4+
Last-Modified: $Date$
5+
Author: Larry Hastings <[email protected]>,
6+
Pablo Galindo <[email protected]>,
7+
Mario Corchero <[email protected]>
8+
Discussions-To: Python-Dev <[email protected]>
9+
Status: Draft
10+
Type: Standards Track
11+
Content-Type: text/x-rst
12+
Created: 20-Jan-2018
13+
14+
15+
========
16+
Overview
17+
========
18+
19+
This PEP proposes a syntax for positional-only parameters in Python.
20+
Positional-only parameters are parameters without an externally-usable
21+
name; when a function accepting positional-only parameters is called,
22+
positional arguments are mapped to these parameters based solely on
23+
their position.
24+
25+
=========
26+
Rationale
27+
=========
28+
29+
Python has always supported positional-only parameters.
30+
Early versions of Python lacked the concept of specifying
31+
parameters by name, so naturally all parameters were
32+
positional-only. This changed around Python 1.0, when
33+
all parameters suddenly became positional-or-keyword.
34+
This allowed users to provide arguments to a function both
35+
positionally or referencing the keyword used in the definition
36+
of it. But, this is not always desired nor even available as
37+
even in current versions of Python, many CPython
38+
"builtin" functions still only accept positional-only arguments.
39+
40+
Even if positional arguments only in a function can be achieved
41+
via using ``*args`` parameters and extracting them one by one,
42+
the solution is far from ideal and not as expressive as the one
43+
proposed in this PEP, which targets to provide syntax to specify
44+
accepting a specific number of positional-only parameters.
45+
Additionally, this will bridge the gap we currently find between
46+
builtin functions that today allows to specify positional-only
47+
parameters and pure Python implementations that lack the
48+
syntax for it.
49+
50+
-----------------------------------------------------
51+
Positional-Only Parameter Semantics In Current Python
52+
-----------------------------------------------------
53+
54+
There are many, many examples of builtins that only
55+
accept positional-only parameters. The resulting
56+
semantics are easily experienced by the Python
57+
programmer--just try calling one, specifying its
58+
arguments by name::
59+
60+
61+
>>> help(pow)
62+
...
63+
pow(x, y, z=None, /)
64+
...
65+
>>> pow(x=5, y=3)
66+
Traceback (most recent call last):
67+
File "<stdin>", line 1, in <module>
68+
TypeError: pow() takes no keyword arguments
69+
70+
Pow clearly expresses that its arguments are only positional
71+
via the ``/`` marker, but this is at the moment only documentational,
72+
Python developers cannot write such syntax.
73+
74+
In addition, there are some functions with particularly
75+
interesting semantics:
76+
77+
* ``range()``, which accepts an optional parameter
78+
to the *left* of its required parameter. [#RANGE]_
79+
80+
* ``dict()``, whose mapping/iterator parameter is optional and
81+
semantically must be positional-only. Any externally
82+
visible name for this parameter would occlude
83+
that name going into the ``**kwarg`` keyword variadic
84+
parameter dict! [#DICT]_
85+
86+
Obviously one can simulate any of these in pure Python code
87+
by accepting ``(*args, **kwargs)`` and parsing the arguments
88+
by hand. But this results in a disconnect between the
89+
Python function signature and what it actually accepts,
90+
not to mention the work of implementing said argument parsing.
91+
92+
==========
93+
Motivation
94+
==========
95+
96+
The new syntax will allow developers to further control how their
97+
API can be consumed. It will allow restricting the usage of keyword
98+
Specify arguments by adding the new type of positional-only ones.
99+
100+
A similar PEP with a broader scope (PEP 457) was proposed
101+
to define the syntax. This PEP builds on top of part of it
102+
to define and provide an implementation for the ``/`` syntax on
103+
function signatures.
104+
105+
=================================================================
106+
The Current State Of Documentation For Positional-Only Parameters
107+
=================================================================
108+
109+
The documentation for positional-only parameters is incomplete
110+
and inconsistent:
111+
112+
* Some functions denote optional groups of positional-only arguments
113+
by enclosing them in nested square brackets. [#BORDER]_
114+
115+
* Some functions denote optional groups of positional-only arguments
116+
by presenting multiple prototypes with varying numbers of
117+
arguments. [#SENDFILE]_
118+
119+
* Some functions use *both* of the above approaches. [#RANGE]_ [#ADDCH]_
120+
121+
One more important idea to consider: currently in the documentation
122+
there's no way to tell whether a function takes positional-only
123+
parameters. ``open()`` accepts keyword arguments, ``ord()`` does
124+
not, but there is no way of telling just by reading the
125+
documentation that this is true.
126+
127+
====================
128+
Syntax And Semantics
129+
====================
130+
131+
From the "ten-thousand foot view", and ignoring ``*args`` and ``**kwargs``
132+
for now, the grammar for a function definition currently looks like this::
133+
134+
def name(positional_or_keyword_parameters, *, keyword_only_parameters):
135+
136+
Building on that perspective, the new syntax for functions would look
137+
like this::
138+
139+
def name(positional_only_parameters, /, positional_or_keyword_parameters,
140+
*, keyword_only_parameters):
141+
142+
All parameters before the ``/`` are positional-only. If ``/`` is
143+
not specified in a function signature, that function does not
144+
accept any positional-only parameters.
145+
The logic around optional values for positional-only argument
146+
Remains the same as the one for positional-or-keyword. Once
147+
a positional-only argument is provided with a default,
148+
the following positional-only and positional-or-keyword argument
149+
need to have a default as well. Positional-only parameters that
150+
don’t have a default value are "required" positional-only parameters.
151+
Therefore the following are valid signatures::
152+
153+
def name(p1, p2, /, p_or_kw, *, kw):
154+
def name(p1, p2=None, /, p_or_kw=None, *, kw):
155+
def name(p1, p2=None, /, *, kw):
156+
def name(p1, p2=None, /):
157+
def name(p1, p2, /, p_or_kw):
158+
def name(p1, p2, /):
159+
160+
Whilst the followings are not::
161+
162+
def name(p1, p2=None, /, p_or_kw, *, kw):
163+
def name(p1=None, p2, /, p_or_kw=None, *, kw):
164+
def name(p1=None, p2, /):
165+
166+
==========================
167+
Full grammar specification
168+
==========================
169+
170+
A draft of the proposed grammar specification is::
171+
172+
new_typedargslist:
173+
tfpdef (',' tfpdef)* ',' '/' [',' [typedargslist]] | typedargslist
174+
175+
new_varargslist:
176+
vfpdef (',' vfpdef)* ',' '/' [',' [varargslist]] | varargslist
177+
178+
It will be added to the actual typedargslist and varargslist
179+
but for easier discussion is presented as new_typedargslist and new_varargslist
180+
181+
182+
===================
183+
Implementation Plan
184+
===================
185+
186+
The implementation will involve a full change of the Grammar. This will
187+
involve following the steps outlined in PEP 306 [#PEP306]_. In addition, other
188+
steps are needed including:
189+
190+
* Modifying the code object and the function object to be aware of positional
191+
only arguments.
192+
193+
* Modifiying ``ceval.c`` (``PyEval_EvalCodeEx``, ``PyEval_EvalFrameEx``...)
194+
to correctly handle positional-only arguments.
195+
196+
* Modifying ``marshal.c`` to account for the modifications of the code object.
197+
198+
199+
This does not intend to be a guide or a comprehensive recipe on how to implement
200+
this but a rough outline of the changes this will make to the codebase.
201+
202+
The advantages of this implementation involve speed, consistency with the
203+
implementation of keyword-only parameters as in PEP 3102 and a simpler implementation
204+
of all the tools and modules that will be impacted by this change.
205+
206+
==============
207+
Rejected Ideas
208+
==============
209+
210+
----------
211+
Do Nothing
212+
----------
213+
214+
Always an option, just not adding it. It was considered
215+
though that the benefits of adding it is worth the complexity
216+
it adds to the language.
217+
218+
---------------------
219+
After marker proposal
220+
---------------------
221+
222+
A complaint against the proposal is the fact that the modifier of
223+
the signature impacts the "already passed" tokens.
224+
225+
This might make confusing to "human parsers" to read functions
226+
with many arguments. Example::
227+
228+
def really_bad_example_of_a_python_function(fist_long_argument, second_long_argument,
229+
third_long_argument, /):
230+
231+
It is not until you reach the end of the signature that the reader
232+
realized the ``/`` and therefore the fact that the arguments are
233+
position-only. This deviates from how the keyword-only marker works.
234+
235+
That said we could not find an implementation that would modify the
236+
arguments after the marker, as that will force the one before the
237+
marker to be position only as well. Example::
238+
239+
def (x, y, /, z):
240+
241+
If we define that ``/`` makes only z position-only it won't be possible
242+
to call x and y via keyword argument. Finding a way to work around it
243+
will add confusion given that at the moment keyword arguments cannot be
244+
followed by positional arguments. ``/`` will therefore make both the
245+
preceding and following position-only.
246+
247+
-------------------
248+
Per-argument marker
249+
-------------------
250+
251+
Using a per argument marker might be an option as well. The approach
252+
basically adds a token to each of the arguments that are position only
253+
and requires those to be placed together. Example::
254+
255+
def (.arg1, .arg2, arg3):
256+
257+
Note the dot on arg1 and arg2. Even if this approach might look easier
258+
to read it has been discarded as ``/`` goes further inline with the
259+
keyword-only approach and is less error prone.
260+
261+
262+
----------------
263+
Using decorators
264+
----------------
265+
266+
It has been suggested on python-ideas [#python-ideas-decorator-based]_ to provide
267+
a decorator written in Python as an implementation for this feature. This approach
268+
has the advantage that keeps parameter declaration more easy to read but also
269+
introduces an asymmetry on how parameter behaviour is declared. Also, as the ``/``
270+
syntax is already introduced for C functions, this inconsistency will make more
271+
difficult to implement all tools and modules that deal with this syntax including
272+
but not limited to, the argument clinic, the inspect module and the ast module.
273+
Another disadvantage of this approach is that calling the decorated functions
274+
will be slower than the functions generated if the feature was implemented directly
275+
in C.
276+
277+
======
278+
Thanks
279+
======
280+
281+
Credit for most of the content of this PEP is contained in Larry Hastings’s PEP 457.
282+
283+
Credit for the use of '/' as the separator between positional-only and positional-or-keyword
284+
parameters go to Guido van Rossum, in a proposal from 2012. [#GUIDO]_
285+
286+
Credit for discussion about the simplification of the grammar goes to
287+
Braulio Valdivieso.
288+
289+
.. [#DICT]
290+
http://docs.python.org/3/library/stdtypes.html#dict
291+
292+
.. [#RANGE]
293+
http://docs.python.org/3/library/functions.html#func-range
294+
295+
.. [#BORDER]
296+
http://docs.python.org/3/library/curses.html#curses.window.border
297+
298+
.. [#SENDFILE]
299+
http://docs.python.org/3/library/os.html#os.sendfile
300+
301+
.. [#ADDCH]
302+
http://docs.python.org/3/library/curses.html#curses.window.addch
303+
304+
.. [#GUIDO]
305+
Guido van Rossum, posting to python-ideas, March 2012:
306+
https://mail.python.org/pipermail/python-ideas/2012-March/014364.html
307+
and
308+
https://mail.python.org/pipermail/python-ideas/2012-March/014378.html
309+
and
310+
https://mail.python.org/pipermail/python-ideas/2012-March/014417.html
311+
312+
.. [#PEP306]
313+
https://www.python.org/dev/peps/pep-0306/
314+
315+
.. [#python-ideas-decorator-based]
316+
https://mail.python.org/pipermail/python-ideas/2017-February/044888.html
317+
318+
=========
319+
Copyright
320+
=========
321+
322+
This document has been placed in the public domain.

0 commit comments

Comments
 (0)