forked from python/peps
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pep-0211.txt
194 lines (140 loc) · 6.34 KB
/
pep-0211.txt
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
PEP: 211
Title: Adding A New Outer Product Operator
Author: Greg Wilson <[email protected]>
Status: Rejected
Type: Standards Track
Content-Type: text/x-rst
Created: 15-Jul-2000
Python-Version: 2.1
Post-History:
.. note::
The approach in the later :pep:`465` was eventually accepted
in lieu of this PEP. The :pep:`Rejected Ideas
<465#rejected-alternatives-to-adding-a-new-operator>`
of that PEP explains the rationale in more detail.
Introduction
============
This PEP describes a proposal to define ``@`` (pronounced "across")
as a new outer product operator in Python 2.2. When applied to
sequences (or other iterable objects), this operator will combine
their iterators, so that::
for (i, j) in S @ T:
pass
will be equivalent to::
for i in S:
for j in T:
pass
Classes will be able to overload this operator using the special
methods ``__across__``, ``__racross__``, and ``__iacross__``. In
particular, the new Numeric module (:pep:`209`) will overload this
operator for multi-dimensional arrays to implement matrix
multiplication.
Background
==========
Number-crunching is now just a small part of computing, but many
programmers --- including many Python users --- still need to
express complex mathematical operations in code. Most numerical
languages, such as APL, Fortran-90, MATLAB, IDL, and Mathematica,
therefore provide two forms of the common arithmetic operators.
One form works element-by-element, e.g. multiplies corresponding
elements of its matrix arguments. The other implements the
"mathematical" definition of that operation, e.g. performs
row-column matrix multiplication.
Zhu and Lielens have :pep:`proposed <225>` doubling up Python's operators in
this way. Their proposal would create six new binary infix
operators, and six new in-place operators.
The original version of this proposal was much more conservative.
The author consulted the developers of GNU Octave [1]_, an open
source clone of MATLAB. Its developers agreed that providing an
infix operator for matrix multiplication was important: numerical
programmers really do care whether they have to write ``mmul(A,B)``
instead of ``A op B``.
On the other hand, when asked how important it was to have infix
operators for matrix solution and other operations, Prof. James
Rawlings replied [2]_:
I DON'T think it's a must have, and I do a lot of matrix
inversion. I cannot remember if its ``A\b`` or ``b\A`` so I always
write ``inv(A)*b`` instead. I recommend dropping ``\``.
Based on this discussion, and feedback from students at the US
national laboratories and elsewhere, we recommended adding only
one new operator, for matrix multiplication, to Python.
Iterators
=========
The planned addition of iterators to Python 2.2 opens up a broader
scope for this proposal. As part of the discussion of :pep:`201`,
Lockstep Iteration, the author of this proposal conducted an
informal usability experiment [3]_. The results showed that users
are psychologically receptive to "cross-product" loop syntax. For
example, most users expected::
S = [10, 20, 30]
T = [1, 2, 3]
for x in S; y in T:
print x+y,
to print ``11 12 13 21 22 23 31 32 33``. We believe that users will
have the same reaction to::
for (x, y) in S @ T:
print x+y
i.e. that they will naturally interpret this as a tidy way to
write loop nests.
This is where iterators come in. Actually constructing the
cross-product of two (or more) sequences before executing the loop
would be very expensive. On the other hand, ``@`` could be defined
to get its arguments' iterators, and then create an outer iterator
which returns tuples of the values returned by the inner
iterators.
Discussion
==========
1. Adding a named function "across" would have less impact on
Python than a new infix operator. However, this would not make
Python more appealing to numerical programmers, who really do
care whether they can write matrix multiplication using an
operator, or whether they have to write it as a function call.
2. ``@`` would have be chainable in the same way as comparison
operators, i.e.::
(1, 2) @ (3, 4) @ (5, 6)
would have to return ``(1, 3, 5) ... (2, 4, 6)``, and *not*
``((1, 3), 5) ... ((2, 4), 6)``. This should not require special
support from the parser, as the outer iterator created by the
first ``@`` could easily be taught how to combine itself with
ordinary iterators.
3. There would have to be some way to distinguish restartable
iterators from ones that couldn't be restarted. For example,
if ``S`` is an input stream (e.g. a file), and ``L`` is a list, then ``S
@ L`` is straightforward, but ``L @ S`` is not, since iteration
through the stream cannot be repeated. This could be treated
as an error, or by having the outer iterator detect
non-restartable inner iterators and cache their values.
4. Whiteboard testing of this proposal in front of three novice
Python users (all of them experienced programmers) indicates
that users will expect::
"ab" @ "cd"
to return four strings, not four tuples of pairs of
characters. Opinion was divided on what::
("a", "b") @ "cd"
ought to return...
Alternatives
============
1. Do nothing --- keep Python simple.
This is always the default choice.
2. Add a named function instead of an operator.
Python is not primarily a numerical language; it may not be worth
complexifying it for this special case. However, support for real
matrix multiplication *is* frequently requested, and the proposed
semantics for ``@`` for built-in sequence types would simplify
expression of a very common idiom (nested loops).
3. Introduce prefixed forms of all existing operators, such as
``~*`` and ``~+``, as proposed in :pep:`225`.
Our objections to this are that there isn't enough demand to
justify the additional complexity (see Rawlings' comments [2]_),
and that the proposed syntax fails the "low toner" readability
test.
Acknowledgments
===============
I am grateful to Huaiyu Zhu for initiating this discussion, and to
James Rawlings and students in various Python courses for their
discussions of what numerical programmers really care about.
References
==========
.. [1] http://bevo.che.wisc.edu/octave/
.. [2] http://www.egroups.com/message/python-numeric/4
.. [3] https://mail.python.org/pipermail/python-dev/2000-July/006427.html