PEP 211  Adding A New Outer Product Operator
PEP:  211 

Title:  Adding A New Outer Product Operator 
Author:  gvwilson at ddj.com (Greg Wilson) 
Status:  Rejected 
Type:  Standards Track 
Created:  15Jul2000 
PythonVersion:  2.1 
PostHistory:  
Resolution:  https://www.python.org/dev/peps/pep0465/#rejectedalternativestoaddinganewoperator 
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 multidimensional arrays to implement matrix multiplication.
Background
Numbercrunching 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, Fortran90, MATLAB, IDL, and Mathematica, therefore provide two forms of the common arithmetic operators. One form works elementbyelement, e.g. multiplies corresponding elements of its matrix arguments. The other implements the "mathematical" definition of that operation, e.g. performs rowcolumn matrix multiplication.
Zhu and Lielens have proposed doubling up Python's operators in this way [1] . Their proposal would create six new binary infix operators, and six new inplace operators.
The original version of this proposal was much more conservative. The author consulted the developers of GNU Octave [2] , 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 [3] :
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 [4] , the author of this proposal conducted an informal usability experiment [5] . The results showed that users are psychologically receptive to "crossproduct" 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 crossproduct 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

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.

@ 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.

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 nonrestartable inner iterators and cache their values.

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

Do nothing  keep Python simple.
This is always the default choice.

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 builtin sequence types would simplify expression of a very common idiom (nested loops).

Introduce prefixed forms of all existing operators, such as ~* and ~+ , as proposed in PEP 225 [1] .
Our objections to this are that there isn't enough demand to justify the additional complexity (see Rawlings' comments [3] ), 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]  ( 1 , 2 ) PEP 225 , Elementwise/Objectwise Operators, Zhu, Lielens http://www.python.org/dev/peps/pep0225/ 
[2]  http://bevo.che.wisc.edu/octave/ 
[3]  ( 1 , 2 ) http://www.egroups.com/message/pythonnumeric/4 
[4]  PEP 201 , Lockstep Iteration, Warsaw http://www.python.org/dev/peps/pep0201/ 
[5]  https://mail.python.org/pipermail/pythondev/2000July/006427.html 