772 lines
28 KiB
Plaintext
772 lines
28 KiB
Plaintext
PEP: 225
|
|
Title: Elementwise/Objectwise Operators
|
|
Author: Huaiyu Zhu <hzhu@users.sourceforge.net>,
|
|
Gregory Lielens <gregory.lielens@fft.be>
|
|
Status: Rejected
|
|
Type: Standards Track
|
|
Content-Type: text/x-rst
|
|
Created: 19-Sep-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 add new operators to Python which are useful
|
|
for distinguishing elementwise and objectwise operations, and summarizes
|
|
discussions in the news group comp.lang.python on this topic. See Credits and
|
|
Archives section at end. Issues discussed here include:
|
|
|
|
- Background.
|
|
- Description of proposed operators and implementation issues.
|
|
- Analysis of alternatives to new operators.
|
|
- Analysis of alternative forms.
|
|
- Compatibility issues
|
|
- Description of wider extensions and other related ideas.
|
|
|
|
A substantial portion of this PEP describes ideas that do not go into the
|
|
proposed extension. They are presented because the extension is essentially
|
|
syntactic sugar, so its adoption must be weighed against various possible
|
|
alternatives. While many alternatives may be better in some aspects, the
|
|
current proposal appears to be overall advantageous.
|
|
|
|
The issues concerning elementwise-objectwise operations extends to wider areas
|
|
than numerical computation. This document also describes how the current
|
|
proposal may be integrated with more general future extensions.
|
|
|
|
|
|
Background
|
|
==========
|
|
|
|
Python provides six binary infix math operators: ``+`` ``-`` ``*`` ``/`` ``%``
|
|
``**`` hereafter generically represented by ``op``. They can be overloaded
|
|
with new semantics for user-defined classes. However, for objects composed of
|
|
homogeneous elements, such as arrays, vectors and matrices in numerical
|
|
computation, there are two essentially distinct flavors of semantics. The
|
|
objectwise operations treat these objects as points in multidimensional spaces.
|
|
The elementwise operations treat them as collections of individual elements.
|
|
These two flavors of operations are often intermixed in the same formulas,
|
|
thereby requiring syntactical distinction.
|
|
|
|
Many numerical computation languages provide two sets of math operators. For
|
|
example, in MatLab, the ordinary ``op`` is used for objectwise operation while
|
|
``.op`` is used for elementwise operation. In R, ``op`` stands for elementwise
|
|
operation while ``%op%`` stands for objectwise operation.
|
|
|
|
In Python, there are other methods of representation, some of which already
|
|
used by available numerical packages, such as:
|
|
|
|
- function: mul(a,b)
|
|
- method: a.mul(b)
|
|
- casting: a.E*b
|
|
|
|
In several aspects these are not as adequate as infix operators. More details
|
|
will be shown later, but the key points are:
|
|
|
|
- Readability: Even for moderately complicated formulas, infix operators are
|
|
much cleaner than alternatives.
|
|
|
|
- Familiarity: Users are familiar with ordinary math operators.
|
|
|
|
- Implementation: New infix operators will not unduly clutter Python syntax.
|
|
They will greatly ease the implementation of numerical packages.
|
|
|
|
While it is possible to assign current math operators to one flavor of
|
|
semantics, there is simply not enough infix operators to overload for the other
|
|
flavor. It is also impossible to maintain visual symmetry between these two
|
|
flavors if one of them does not contain symbols for ordinary math operators.
|
|
|
|
|
|
Proposed extension
|
|
==================
|
|
|
|
- Six new binary infix operators ``~+`` ``~-`` ``~*`` ``~/`` ``~%`` ``~**`` are
|
|
added to core Python. They parallel the existing operators ``+`` ``-`` ``*``
|
|
``/`` ``%`` ``**``.
|
|
|
|
- Six augmented assignment operators ``~+=`` ``~-=`` ``~*=`` ``~/=`` ``~%=``
|
|
``~**=`` are added to core Python. They parallel the operators ``+=`` ``-=``
|
|
``*=`` ``/=`` ``%=`` ``**=`` available in Python 2.0.
|
|
|
|
- Operator ``~op`` retains the syntactical properties of operator ``op``,
|
|
including precedence.
|
|
|
|
- Operator ``~op`` retains the semantical properties of operator ``op`` on
|
|
built-in number types.
|
|
|
|
- Operator ``~op`` raise syntax error on non-number builtin types. This is
|
|
temporary until the proper behavior can be agreed upon.
|
|
|
|
- These operators are overloadable in classes with names that prepend *t* (for
|
|
tilde) to names of ordinary math operators. For example, ``__tadd__`` and
|
|
``__rtadd__`` work for ``~+`` just as ``__add__`` and ``__radd__`` work for
|
|
``+``.
|
|
|
|
- As with existing operators, the ``__r*__()`` methods are invoked when the
|
|
left operand does not provide the appropriate method.
|
|
|
|
It is intended that one set of ``op`` or ``~op`` is used for elementwise
|
|
operations, the other for objectwise operations, but it is not specified which
|
|
version of operators stands for elementwise or objectwise operations, leaving
|
|
the decision to applications.
|
|
|
|
The proposed implementation is to patch several files relating to the
|
|
tokenizer, parser, grammar and compiler to duplicate the functionality of
|
|
corresponding existing operators as necessary. All new semantics are to be
|
|
implemented in the classes that overload them.
|
|
|
|
The symbol ``~`` is already used in Python as the unary *bitwise not* operator.
|
|
Currently it is not allowed for binary operators. The new operators are
|
|
completely backward compatible.
|
|
|
|
|
|
Prototype Implementation
|
|
========================
|
|
|
|
Greg Lielens implemented the infix ``~op`` as a patch against Python 2.0b1
|
|
source [1]_.
|
|
|
|
To allow ``~`` to be part of binary operators, the tokenizer would treat ``~+``
|
|
as one token. This means that currently valid expression ``~+1`` would be
|
|
tokenized as ``~+`` ``1`` instead of ``~ + 1``. The parser would then treat ``~+``
|
|
as composite of ``~ +``. The effect is invisible to applications.
|
|
|
|
Notes about current patch:
|
|
|
|
- It does not include ``~op=`` operators yet.
|
|
|
|
- The ``~op`` behaves the same as ``op`` on lists, instead of raising
|
|
exceptions.
|
|
|
|
These should be fixed when the final version of this proposal is ready.
|
|
|
|
- It reserves ``xor`` as an infix operator with the semantics equivalent to::
|
|
|
|
def __xor__(a, b):
|
|
if not b: return a
|
|
elif not a: return b
|
|
else: 0
|
|
|
|
This preserves true value as much as possible, otherwise preserve left hand
|
|
side value if possible.
|
|
|
|
This is done so that bitwise operators could be regarded as elementwise
|
|
logical operators in the future (see below).
|
|
|
|
|
|
Alternatives to adding new operators
|
|
====================================
|
|
|
|
The discussions on comp.lang.python and python-dev mailing list explored many
|
|
alternatives. Some of the leading alternatives are listed here, using the
|
|
multiplication operator as an example.
|
|
|
|
1. Use function ``mul(a,b)``.
|
|
|
|
Advantage:
|
|
|
|
- No need for new operators.
|
|
|
|
Disadvantage:
|
|
|
|
- Prefix forms are cumbersome for composite formulas.
|
|
- Unfamiliar to the intended users.
|
|
- Too verbose for the intended users.
|
|
- Unable to use natural precedence rules.
|
|
|
|
2. Use method call ``a.mul(b)``.
|
|
|
|
Advantage:
|
|
|
|
- No need for new operators.
|
|
|
|
Disadvantage:
|
|
|
|
- Asymmetric for both operands.
|
|
- Unfamiliar to the intended users.
|
|
- Too verbose for the intended users.
|
|
- Unable to use natural precedence rules.
|
|
|
|
3. Use *shadow classes*. For matrix class define a shadow array class
|
|
accessible through a method ``.E``, so that for matrices *a* and *b*,
|
|
``a.E*b`` would be a matrix object that is ``elementwise_mul(a,b)``.
|
|
|
|
Likewise define a shadow matrix class for arrays accessible through a method
|
|
``.M`` so that for arrays *a* and *b*, ``a.M*b`` would be an array that is
|
|
``matrixwise_mul(a,b)``.
|
|
|
|
Advantage:
|
|
|
|
- No need for new operators.
|
|
- Benefits of infix operators with correct precedence rules.
|
|
- Clean formulas in applications.
|
|
|
|
Disadvantage:
|
|
|
|
- Hard to maintain in current Python because ordinary numbers cannot have
|
|
user defined class methods; i.e. ``a.E*b`` will fail if a is a pure
|
|
number.
|
|
- Difficult to implement, as this will interfere with existing method calls,
|
|
like ``.T`` for transpose, etc.
|
|
- Runtime overhead of object creation and method lookup.
|
|
- The shadowing class cannot replace a true class, because it does not
|
|
return its own type. So there need to be a ``M`` class with shadow ``E``
|
|
class, and an ``E`` class with shadow ``M`` class.
|
|
- Unnatural to mathematicians.
|
|
|
|
4. Implement matrixwise and elementwise classes with easy casting to the other
|
|
class. So matrixwise operations for arrays would be like ``a.M*b.M`` and
|
|
elementwise operations for matrices would be like ``a.E*b.E``. For error
|
|
detection ``a.E*b.M`` would raise exceptions.
|
|
|
|
Advantage:
|
|
|
|
- No need for new operators.
|
|
- Similar to infix notation with correct precedence rules.
|
|
|
|
Disadvantage:
|
|
|
|
- Similar difficulty due to lack of user-methods for pure numbers.
|
|
- Runtime overhead of object creation and method lookup.
|
|
- More cluttered formulas.
|
|
- Switching of flavor of objects to facilitate operators becomes persistent.
|
|
This introduces long range context dependencies in application code that
|
|
would be extremely hard to maintain.
|
|
|
|
5. Using mini parser to parse formulas written in arbitrary extension placed in
|
|
quoted strings.
|
|
|
|
Advantage:
|
|
|
|
- Pure Python, without new operators
|
|
|
|
Disadvantage:
|
|
|
|
- The actual syntax is within the quoted string, which does not resolve the
|
|
problem itself.
|
|
- Introducing zones of special syntax.
|
|
- Demanding on the mini-parser.
|
|
|
|
6. Introducing a single operator, such as ``@``, for matrix multiplication.
|
|
|
|
Advantage:
|
|
|
|
- Introduces less operators
|
|
|
|
Disadvantage:
|
|
|
|
- The distinctions for operators like ``+`` ``-`` ``**`` are equally
|
|
important. Their meaning in matrix or array-oriented packages would be
|
|
reversed (see below).
|
|
- The new operator occupies a special character.
|
|
- This does not work well with more general object-element issues.
|
|
|
|
Among these alternatives, the first and second are used in current applications
|
|
to some extent, but found inadequate. The third is the most favorite for
|
|
applications, but it will incur huge implementation complexity. The fourth
|
|
would make applications codes very context-sensitive and hard to maintain.
|
|
These two alternatives also share significant implementational difficulties due
|
|
to current type/class split. The fifth appears to create more problems than it
|
|
would solve. The sixth does not cover the same range of applications.
|
|
|
|
|
|
Alternative forms of infix operators
|
|
====================================
|
|
|
|
Two major forms and several minor variants of new infix operators were
|
|
discussed:
|
|
|
|
- Bracketed form::
|
|
|
|
(op)
|
|
[op]
|
|
{op}
|
|
<op>
|
|
:op:
|
|
~op~
|
|
%op%
|
|
|
|
- Meta character form::
|
|
|
|
.op
|
|
@op
|
|
~op
|
|
|
|
Alternatively the meta character is put after the operator.
|
|
|
|
- Less consistent variations of these themes. These are considered
|
|
unfavorably. For completeness some are listed here:
|
|
|
|
- Use ``@/`` and ``/@`` for left and right division
|
|
- Use ``[*]`` and ``(*)`` for outer and inner products
|
|
- Use a single operator ``@`` for multiplication.
|
|
|
|
- Use ``__call__`` to simulate multiplication::
|
|
|
|
a(b) or (a)(b)
|
|
|
|
Criteria for choosing among the representations include:
|
|
|
|
- No syntactical ambiguities with existing operators.
|
|
|
|
- Higher readability in actual formulas. This makes the bracketed forms
|
|
unfavorable. See examples below.
|
|
|
|
- Visually similar to existing math operators.
|
|
|
|
- Syntactically simple, without blocking possible future extensions.
|
|
|
|
With these criteria the overall winner in bracket form appear to be ``{op}``.
|
|
A clear winner in the meta character form is ``~op``. Comparing these it
|
|
appears that ``~op`` is the favorite among them all.
|
|
|
|
Some analysis are as follows:
|
|
|
|
- The ``.op`` form is ambiguous: ``1.+a`` would be different from ``1 .+a``.
|
|
- The bracket type operators are most favorable when standing alone, but
|
|
not in formulas, as they interfere with visual parsing of parentheses for
|
|
precedence and function argument. This is so for ``(op)`` and ``[op]``, and
|
|
somewhat less so for ``{op}`` and ``<op>``.
|
|
|
|
- The ``<op>`` form has the potential to be confused with ``<`` ``>`` and ``=``.
|
|
|
|
- The ``@op`` is not favored because ``@`` is visually heavy (dense, more like
|
|
a letter): ``a@+b`` is more readily read as ``a@ + b`` than ``a @+ b``.
|
|
|
|
- For choosing meta-characters: Most of existing ASCII symbols have already
|
|
been used. The only three unused are ``@`` ``$`` ``?``.
|
|
|
|
|
|
Semantics of new operators
|
|
==========================
|
|
|
|
There are convincing arguments for using either set of operators as objectwise
|
|
or elementwise. Some of them are listed here:
|
|
|
|
1. ``op`` for element, ``~op`` for object
|
|
|
|
- Consistent with current multiarray interface of Numeric package.
|
|
- Consistent with some other languages.
|
|
- Perception that elementwise operations are more natural.
|
|
- Perception that elementwise operations are used more frequently
|
|
|
|
2. ``op`` for object, ``~op`` for element
|
|
|
|
- Consistent with current linear algebra interface of MatPy package.
|
|
- Consistent with some other languages.
|
|
- Perception that objectwise operations are more natural.
|
|
- Perception that objectwise operations are used more frequently.
|
|
- Consistent with the current behavior of operators on lists.
|
|
- Allow ``~`` to be a general elementwise meta-character in future
|
|
extensions.
|
|
|
|
It is generally agreed upon that
|
|
|
|
- There is no absolute reason to favor one or the other.
|
|
- It is easy to cast from one representation to another in a sizable chunk of
|
|
code, so the other flavor of operators is always minority.
|
|
- There are other semantic differences that favor existence of array-oriented
|
|
and matrix-oriented packages, even if their operators are unified.
|
|
- Whatever the decision is taken, codes using existing interfaces should not be
|
|
broken for a very long time.
|
|
|
|
Therefore, not much is lost, and much flexibility retained, if the semantic
|
|
flavors of these two sets of operators are not dictated by the core language.
|
|
The application packages are responsible for making the most suitable choice.
|
|
This is already the case for NumPy and MatPy which use opposite semantics.
|
|
Adding new operators will not break this. See also observation after
|
|
subsection 2 in the Examples below.
|
|
|
|
The issue of numerical precision was raised, but if the semantics is left to
|
|
the applications, the actual precisions should also go there.
|
|
|
|
|
|
Examples
|
|
========
|
|
|
|
Following are examples of the actual formulas that will appear using various
|
|
operators or other representations described above.
|
|
|
|
1. The matrix inversion formula:
|
|
|
|
- Using ``op`` for object and ``~op`` for element::
|
|
|
|
b = a.I - a.I * u / (c.I + v/a*u) * v / a
|
|
|
|
b = a.I - a.I * u * (c.I + v*a.I*u).I * v * a.I
|
|
|
|
- Using ``op`` for element and ``~op`` for object::
|
|
|
|
b = a.I @- a.I @* u @/ (c.I @+ v@/a@*u) @* v @/ a
|
|
|
|
b = a.I ~- a.I ~* u ~/ (c.I ~+ v~/a~*u) ~* v ~/ a
|
|
|
|
b = a.I (-) a.I (*) u (/) (c.I (+) v(/)a(*)u) (*) v (/) a
|
|
|
|
b = a.I [-] a.I [*] u [/] (c.I [+] v[/]a[*]u) [*] v [/] a
|
|
|
|
b = a.I <-> a.I <*> u </> (c.I <+> v</>a<*>u) <*> v </> a
|
|
|
|
b = a.I {-} a.I {*} u {/} (c.I {+} v{/}a{*}u) {*} v {/} a
|
|
|
|
Observation: For linear algebra using ``op`` for object is preferable.
|
|
|
|
Observation: The ``~op`` type operators look better than ``(op)`` type in
|
|
complicated formulas.
|
|
|
|
- using named operators::
|
|
|
|
b = a.I @sub a.I @mul u @div (c.I @add v @div a @mul u) @mul v @div a
|
|
|
|
b = a.I ~sub a.I ~mul u ~div (c.I ~add v ~div a ~mul u) ~mul v ~div a
|
|
|
|
Observation: Named operators are not suitable for math formulas.
|
|
|
|
2. Plotting a 3d graph
|
|
|
|
- Using ``op`` for object and ``~op`` for element::
|
|
|
|
z = sin(x~**2 ~+ y~**2); plot(x,y,z)
|
|
|
|
- Using op for element and ~op for object::
|
|
|
|
z = sin(x**2 + y**2); plot(x,y,z)
|
|
|
|
Observation: Elementwise operations with broadcasting allows much more
|
|
efficient implementation than MatLab.
|
|
|
|
Observation: It is useful to have two related classes with the semantics of
|
|
``op`` and ``~op`` swapped. Using these the ``~op`` operators would only
|
|
need to appear in chunks of code where the other flavor dominates, while
|
|
maintaining consistent semantics of the code.
|
|
|
|
3. Using ``+`` and ``-`` with automatic broadcasting::
|
|
|
|
a = b - c; d = a.T*a
|
|
|
|
Observation: This would silently produce hard-to-trace bugs if one of *b* or
|
|
*c* is row vector while the other is column vector.
|
|
|
|
|
|
Miscellaneous issues
|
|
====================
|
|
|
|
- Need for the ``~+`` ``~-`` operators. The objectwise ``+`` ``-`` are
|
|
important because they provide important sanity checks as per linear algebra.
|
|
The elementwise ``+`` ``-`` are important because they allow broadcasting
|
|
that are very efficient in applications.
|
|
|
|
- Left division (solve). For matrix, ``a*x`` is not necessarily equal to
|
|
``x*a``. The solution of ``a*x==b``, denoted ``x=solve(a,b)``, is therefore
|
|
different from the solution of ``x*a==b``, denoted ``x=div(b,a)``. There are
|
|
discussions about finding a new symbol for solve. [Background: MatLab use
|
|
``b/a`` for ``div(b,a)`` and ``a\b`` for ``solve(a,b)``.]
|
|
|
|
It is recognized that Python provides a better solution without requiring a
|
|
new symbol: the ``inverse`` method ``.I`` can be made to be delayed so that
|
|
``a.I*b`` and ``b*a.I`` are equivalent to Matlab's ``a\b`` and ``b/a``. The
|
|
implementation is quite simple and the resulting application code clean.
|
|
|
|
- Power operator. Python's use of ``a**b`` as ``pow(a,b)`` has two perceived
|
|
disadvantages:
|
|
|
|
- Most mathematicians are more familiar with ``a^b`` for this purpose.
|
|
- It results in long augmented assignment operator ``~**=``.
|
|
|
|
However, this issue is distinct from the main issue here.
|
|
|
|
- Additional multiplication operators. Several forms of multiplications are
|
|
used in (multi-)linear algebra. Most can be seen as variations of
|
|
multiplication in linear algebra sense (such as Kronecker product). But two
|
|
forms appear to be more fundamental: outer product and inner product.
|
|
However, their specification includes indices, which can be either
|
|
|
|
- associated with the operator, or
|
|
- associated with the objects.
|
|
|
|
The latter (the Einstein notation) is used extensively on paper, and is also
|
|
the easier one to implement. By implementing a tensor-with-indices class, a
|
|
general form of multiplication would cover both outer and inner products, and
|
|
specialize to linear algebra multiplication as well. The index rule can be
|
|
defined as class methods, like::
|
|
|
|
a = b.i(1,2,-1,-2) * c.i(4,-2,3,-1) # a_ijkl = b_ijmn c_lnkm
|
|
|
|
Therefore, one objectwise multiplication is sufficient.
|
|
|
|
- Bitwise operators.
|
|
|
|
- The proposed new math operators use the symbol ~ that is *bitwise not*
|
|
operator. This poses no compatibility problem but somewhat complicates
|
|
implementation.
|
|
|
|
- The symbol ``^`` might be better used for ``pow`` than bitwise ``xor``. But
|
|
this depends on the future of bitwise operators. It does not immediately
|
|
impact on the proposed math operator.
|
|
|
|
- The symbol ``|`` was suggested to be used for matrix solve. But the new
|
|
solution of using delayed ``.I`` is better in several ways.
|
|
|
|
- The current proposal fits in a larger and more general extension that will
|
|
remove the need for special bitwise operators. (See elementization below.)
|
|
|
|
- Alternative to special operator names used in definition,
|
|
|
|
::
|
|
|
|
def "+"(a, b) in place of def __add__(a, b)
|
|
|
|
This appears to require greater syntactical change, and would only be useful
|
|
when arbitrary additional operators are allowed.
|
|
|
|
|
|
Impact on general elementization
|
|
================================
|
|
|
|
The distinction between objectwise and elementwise operations are meaningful in
|
|
other contexts as well, where an object can be conceptually regarded as a
|
|
collection of elements. It is important that the current proposal does not
|
|
preclude possible future extensions.
|
|
|
|
One general future extension is to use ``~`` as a meta operator to *elementize*
|
|
a given operator. Several examples are listed here:
|
|
|
|
1. Bitwise operators. Currently Python assigns six operators to bitwise
|
|
operations: and (``&``), or (``|``), xor (``^``), complement (``~``), left
|
|
shift (``<<``) and right shift (``>>``), with their own precedence levels.
|
|
|
|
Among them, the ``&`` ``|`` ``^`` ``~`` operators can be regarded as
|
|
elementwise versions of lattice operators applied to integers regarded as
|
|
bit strings.::
|
|
|
|
5 and 6 # 6
|
|
5 or 6 # 5
|
|
|
|
5 ~and 6 # 4
|
|
5 ~or 6 # 7
|
|
|
|
These can be regarded as general elementwise lattice operators, not
|
|
restricted to bits in integers.
|
|
|
|
In order to have named operators for ``xor`` ``~xor``, it is necessary to
|
|
make ``xor`` a reserved word.
|
|
|
|
2. List arithmetics.::
|
|
|
|
[1, 2] + [3, 4] # [1, 2, 3, 4]
|
|
[1, 2] ~+ [3, 4] # [4, 6]
|
|
|
|
['a', 'b'] * 2 # ['a', 'b', 'a', 'b']
|
|
'ab' * 2 # 'abab'
|
|
|
|
['a', 'b'] ~* 2 # ['aa', 'bb']
|
|
[1, 2] ~* 2 # [2, 4]
|
|
|
|
It is also consistent to Cartesian product::
|
|
|
|
[1,2]*[3,4] # [(1,3),(1,4),(2,3),(2,4)]
|
|
|
|
3. List comprehension.::
|
|
|
|
a = [1, 2]; b = [3, 4]
|
|
~f(a,b) # [f(x,y) for x, y in zip(a,b)]
|
|
~f(a*b) # [f(x,y) for x in a for y in b]
|
|
a ~+ b # [x + y for x, y in zip(a,b)]
|
|
|
|
4. Tuple generation (the zip function in Python 2.0)::
|
|
|
|
[1, 2, 3], [4, 5, 6] # ([1,2, 3], [4, 5, 6])
|
|
[1, 2, 3]~,[4, 5, 6] # [(1,4), (2, 5), (3,6)]
|
|
|
|
5. Using ``~`` as generic elementwise meta-character to replace map::
|
|
|
|
~f(a, b) # map(f, a, b)
|
|
~~f(a, b) # map(lambda *x:map(f, *x), a, b)
|
|
|
|
More generally,::
|
|
|
|
def ~f(*x): return map(f, *x)
|
|
def ~~f(*x): return map(~f, *x)
|
|
...
|
|
|
|
6. Elementwise format operator (with broadcasting)::
|
|
|
|
a = [1,2,3,4,5]
|
|
print ["%5d "] ~% a
|
|
a = [[1,2],[3,4]]
|
|
print ["%5d "] ~~% a
|
|
|
|
7. Rich comparison::
|
|
|
|
[1, 2, 3] ~< [3, 2, 1] # [1, 0, 0]
|
|
[1, 2, 3] ~== [3, 2, 1] # [0, 1, 0]
|
|
|
|
8. Rich indexing::
|
|
|
|
[a, b, c, d] ~[2, 3, 1] # [c, d, b]
|
|
|
|
9. Tuple flattening::
|
|
|
|
a = (1,2); b = (3,4)
|
|
f(~a, ~b) # f(1,2,3,4)
|
|
|
|
10. Copy operator::
|
|
|
|
a ~= b # a = b.copy()
|
|
|
|
There can be specific levels of deep copy::
|
|
|
|
a ~~= b # a = b.copy(2)
|
|
|
|
Notes
|
|
-----
|
|
|
|
1. There are probably many other similar situations. This general approach
|
|
seems well suited for most of them, in place of several separated extensions
|
|
for each of them (parallel and cross iteration, list comprehension, rich
|
|
comparison, etc).
|
|
|
|
2. The semantics of *elementwise* depends on applications. For example, an
|
|
element of matrix is two levels down from the list-of-list point of view.
|
|
This requires more fundamental change than the current proposal. In any
|
|
case, the current proposal will not negatively impact on future
|
|
possibilities of this nature.
|
|
|
|
Note that this section describes a type of future extensions that is consistent
|
|
with current proposal, but may present additional compatibility or other
|
|
problems. They are not tied to the current proposal.
|
|
|
|
|
|
Impact on named operators
|
|
=========================
|
|
|
|
The discussions made it generally clear that infix operators is a scarce
|
|
resource in Python, not only in numerical computation, but in other fields as
|
|
well. Several proposals and ideas were put forward that would allow infix
|
|
operators be introduced in ways similar to named functions. We show here that
|
|
the current extension does not negatively impact on future extensions in this
|
|
regard.
|
|
|
|
1. Named infix operators.
|
|
|
|
Choose a meta character, say ``@``, so that for any identifier ``opname``,
|
|
the combination ``@opname`` would be a binary infix operator, and::
|
|
|
|
a @opname b == opname(a,b)
|
|
|
|
Other representations mentioned include::
|
|
|
|
.name ~name~ :name: (.name) %name%
|
|
|
|
and similar variations. The pure bracket based operators cannot be used
|
|
this way.
|
|
|
|
This requires a change in the parser to recognize ``@opname``, and parse it
|
|
into the same structure as a function call. The precedence of all these
|
|
operators would have to be fixed at one level, so the implementation would
|
|
be different from additional math operators which keep the precedence of
|
|
existing math operators.
|
|
|
|
The current proposed extension do not limit possible future extensions of
|
|
such form in any way.
|
|
|
|
2. More general symbolic operators.
|
|
|
|
One additional form of future extension is to use meta character and
|
|
operator symbols (symbols that cannot be used in syntactical structures
|
|
other than operators). Suppose ``@`` is the meta character. Then::
|
|
|
|
a + b, a @+ b, a @@+ b, a @+- b
|
|
|
|
would all be operators with a hierarchy of precedence, defined by::
|
|
|
|
def "+"(a, b)
|
|
def "@+"(a, b)
|
|
def "@@+"(a, b)
|
|
def "@+-"(a, b)
|
|
|
|
One advantage compared with named operators is greater flexibility for
|
|
precedences based on either the meta character or the ordinary operator
|
|
symbols. This also allows operator composition. The disadvantage is that
|
|
they are more like *line noise*. In any case the current proposal does not
|
|
impact its future possibility.
|
|
|
|
These kinds of future extensions may not be necessary when Unicode becomes
|
|
generally available.
|
|
|
|
Note that this section discusses compatibility of the proposed extension
|
|
with possible future extensions. The desirability or compatibility of these
|
|
other extensions themselves are specifically not considered here.
|
|
|
|
|
|
Credits and archives
|
|
====================
|
|
|
|
The discussions mostly happened in July to August of 2000 on news group
|
|
comp.lang.python and the mailing list python-dev. There are altogether several
|
|
hundred postings, most can be retrieved from these two pages (and searching
|
|
word "operator"):
|
|
|
|
http://www.python.org/pipermail/python-list/2000-July/
|
|
http://www.python.org/pipermail/python-list/2000-August/
|
|
|
|
The names of contributors are too numerous to mention here, suffice to say that
|
|
a large proportion of ideas discussed here are not our own.
|
|
|
|
Several key postings (from our point of view) that may help to navigate the
|
|
discussions include:
|
|
|
|
http://www.python.org/pipermail/python-list/2000-July/108893.html
|
|
http://www.python.org/pipermail/python-list/2000-July/108777.html
|
|
http://www.python.org/pipermail/python-list/2000-July/108848.html
|
|
http://www.python.org/pipermail/python-list/2000-July/109237.html
|
|
http://www.python.org/pipermail/python-list/2000-July/109250.html
|
|
http://www.python.org/pipermail/python-list/2000-July/109310.html
|
|
http://www.python.org/pipermail/python-list/2000-July/109448.html
|
|
http://www.python.org/pipermail/python-list/2000-July/109491.html
|
|
http://www.python.org/pipermail/python-list/2000-July/109537.html
|
|
http://www.python.org/pipermail/python-list/2000-July/109607.html
|
|
http://www.python.org/pipermail/python-list/2000-July/109709.html
|
|
http://www.python.org/pipermail/python-list/2000-July/109804.html
|
|
http://www.python.org/pipermail/python-list/2000-July/109857.html
|
|
http://www.python.org/pipermail/python-list/2000-July/110061.html
|
|
http://www.python.org/pipermail/python-list/2000-July/110208.html
|
|
http://www.python.org/pipermail/python-list/2000-August/111427.html
|
|
http://www.python.org/pipermail/python-list/2000-August/111558.html
|
|
http://www.python.org/pipermail/python-list/2000-August/112551.html
|
|
http://www.python.org/pipermail/python-list/2000-August/112606.html
|
|
http://www.python.org/pipermail/python-list/2000-August/112758.html
|
|
|
|
http://www.python.org/pipermail/python-dev/2000-July/013243.html
|
|
http://www.python.org/pipermail/python-dev/2000-July/013364.html
|
|
http://www.python.org/pipermail/python-dev/2000-August/014940.html
|
|
|
|
These are earlier drafts of this PEP:
|
|
|
|
http://www.python.org/pipermail/python-list/2000-August/111785.html
|
|
http://www.python.org/pipermail/python-list/2000-August/112529.html
|
|
http://www.python.org/pipermail/python-dev/2000-August/014906.html
|
|
|
|
There is an alternative PEP (officially, :pep:`211`) by Greg Wilson, titled
|
|
"Adding New Linear Algebra Operators to Python".
|
|
|
|
Its first (and current) version is at:
|
|
|
|
http://www.python.org/pipermail/python-dev/2000-August/014876.html
|
|
:pep:`211`
|
|
|
|
|
|
Additional References
|
|
=====================
|
|
|
|
.. [1] http://MatPy.sourceforge.net/Misc/index.html
|