Python custom character/symbol/expression map to methods/functions [duplicate] - python

I would like to define my own operator. Does python support such a thing?

While technically you cannot define new operators in Python, this clever hack works around this limitation. It allows you to define infix operators like this:
# simple multiplication
x=Infix(lambda x,y: x*y)
print 2 |x| 4
# => 8
# class checking
isa=Infix(lambda x,y: x.__class__==y.__class__)
print [1,2,3] |isa| []
print [1,2,3] <<isa>> []
# => True

No, Python comes with a predefined, yet overridable, set of operators.

No, you can't create new operators. However, if you are just evaluating expressions, you could process the string yourself and calculate the results of the new operators.

Sage provides this functionality, essentially using the "clever hack" described by #Ayman Hourieh, but incorporated into a module as a decorator to give a cleaner appearance and additional functionality – you can choose the operator to overload and therefore the order of evaluation.
from sage.misc.decorators import infix_operator
#infix_operator('multiply')
def dot(a,b):
return a.dot_product(b)
u=vector([1,2,3])
v=vector([5,4,3])
print(u *dot* v)
# => 22
#infix_operator('or')
def plus(x,y):
return x*y
print(2 |plus| 4)
# => 6
See the Sage documentation and this enhancement tracking ticket for more information.

Python 3.5 introduces the symbol # for an extra operator.
PEP465 introduced this new operator for matrix multiplication, to simplify the notation of many numerical code. The operator will not be implemented for all types, but just for arrays-like-objects.
You can support the operator for your classes/objects by implementing __matmul__().
The PEP leaves space for a different usage of the operator for non-arrays-like objects.
Of course you can implement with # any sort of operation different from matrix multiplication also for arrays-like objects, but the user experience will be affected, because everybody will expect your data type to behave in a different way.

If you intend to apply the operation on a particular class of objects, you could just override the operator that matches your function the closest... for instance, overriding __eq__() will override the == operator to return whatever you want. This works for almost all the operators.

Related

Why use the functions in the operator module?

What is the point of python's operator module? There are many obviously redundant functions there and I don't understand why should one prefer to use these functions rather than other ways to do the same thing.
For example:
>>> import operator
>>> operator.truth(0)
False
>>> bool(0)
False
seem to do exactly the same thing.
Its sometimes useful to be able to access the functionality of an operator but as a function. For example to add two numbers together you could do.
>> print(1 + 2)
3
You could also do
>> import operator
>> print(operator.add(1, 2))
3
A use case for the function approach could be you need to write a calculator function which returns an answer given a simple formula.
import operator as _operator
operator_mapping = {
'+': _operator.add,
'-': _operator.sub,
'*': _operator.mul,
'/': _operator.truediv,
}
def calculate(formula):
x, operator, y = formula.split(' ')
# Convert x and y to floats so we can perform mathematical
# operations on them.
x, y = map(float, (x, y))
return operator_mapping[operator](x, y)
print(calculate('1 + 2')) # prints 3.0
For completeness and consistency. Because having all operators in one place lets you do dynamic lookups later on:
getattr(operator, opname)(*arguments)
Omitting some operations because they are redundant would defeat that purpose. And because Python names are just references, it is cheap and easy to add a name to the operator module that is simply another reference.
Given the existence of bool, it's hard to think of any use-case for operator.truth these days. Note that bool was new in 2.2.1, and operator predates that, so it may only exist now for historical reasons. There are also other useless functions in the operator module, such as operator.abs, which simply calls the built-in abs.
Not everything in operator is entirely useless, though - operator's C implementation, if available, can offer performance gains over pure Python implementations. The itemgetter, attrgetter and methodcaller functions are more readable and generally better performing utility functions for tasks which are often handled by lambda functions.

Working with abstract mathematical Operators/Objects in Sympy

I am wondering if there is an easy way to implement abstract mathematical Operators in Sympy. With operators I simply mean some objects that have 3 values as an input or 3 indices, something like "Operator(a,b,c)". Please note that I am refering to a mathematical operator (over a hilbert space) and not an operator in the context of programming. Depending on these values I want to teach Sympy how to multiply two Operators of this kind and how to multiply it with a float and so on. At some point of the calculation I want to replace these Operators with some others...
So far I couldn't figure out if sympy provides such an abstract calculation. Therefore I started to write a new Python-class for these objects, but this went beyond the scope of my limited knowledge in Python very fast... Is there a more easy way to implement that then creating a new class?
You may also want to look at SageMath, in addition to SymPy, since Sage goes to great lengths to come with prebuilt mathematical structures. However, it's been development more with eyes towards algebraic geometry, various areas of algebra, and combinatorics. I'm not sure to what extent it implements any operator algebra.
Yes, you can do this. Just create a subclass of sympy.Function. You can specify the number of arguments with nargs
class Operator(Function):
nargs = 3
If you want the function to evaluate for certain arguments, define the class function eval. It should return None for when it should remain unevaluated. For instance, to evaluate to 0 when all three arguments are 0, you might use
class Operator(Function):
#classmethod
def eval(cls, a, b, c):
if a == b == c == 0:
return Integer(0)
(note that nargs is not required if you define eval).

Python: what's the difference - abs and operator.abs

In python what is the difference between :
abs(a) and operator.abs(a)
They are the very same and they work alike. If they are the very same then why are two separate functions doing the same stuff are made??
If there is some specific functionality for any one of it - please do explain it.
There is no difference. The documentation even says so:
>>> import operator
>>> print(operator.abs.__doc__)
abs(a) -- Same as abs(a).
It is implemented as a wrapper just so the documentation can be updated:
from builtins import abs as _abs
# ...
def abs(a):
"Same as abs(a)."
return _abs(a)
(Note, the above Python implementation is only used if the C module itself can't be loaded).
It is there purely to complement the other (mathematical) operators; e.g. if you wanted to do dynamic operator lookups on that module you don't have to special-case abs().
No difference at all. You might wanna use operator.abs with functions like itertools.accumulate, just like you use operator.add for +. There is a performance differene though.
For example using operator.add is twice as fast as +(Beazly).

Difference between operators and methods

Is there any substantial difference between operators and methods?
The only difference I see is the way the are called, do they have other differences?
For example in Python concatenation, slicing, indexing are defined as operators, while (referring to strings) upper(), replace(), strip() and so on are methods.
If I understand question currectly...
In nutshell, everything is a method of object. You can find "expression operators" methods in python magic class methods, in the operators.
So, why python has "sexy" things like [x:y], [x], +, -? Because it is common things to most developers, even to unfamiliar with development people, so math functions like +, - will catch human eye and he will know what happens. Similar with indexing - it is common syntax in many languages.
But there is no special ways to express upper, replace, strip methods, so there is no "expression operators" for it.
So, what is different between "expression operators" and methods, I'd say just the way it looks.
Your question is rather broad. For your examples, concatenation, slicing, and indexing are defined on strings and lists using special syntax (e.g., []). But other types may do things differently.
In fact, the behavior of most (I think all) of the operators is constrolled by magic methods, so really when you write something like x + y a method is called under the hood.
From a practical perspective, one of the main differences is that the set of available syntactic operators is fixed and new ones cannot be added by your Python code. You can't write your own code to define a new operator called $ and then have x $ y work. On the other hand, you can define as many methods as you want. This means that you should choose carefully what behavior (if any) you assign to operators; since there are only a limited number of operators, you want to be sure that you don't "waste" them on uncommon operations.
Is there any substantial difference between operators and
methods?
Practically speaking, there is no difference because each operator is mapped to a specific Python special method. Moreover, whenever Python encounters the use of an operator, it calls its associated special method implicitly. For example:
1 + 2
implicitly calls int.__add__, which makes the above expression equivalent1 to:
(1).__add__(2)
Below is a demonstration:
>>> class Foo:
... def __add__(self, other):
... print("Foo.__add__ was called")
... return other + 10
...
>>> f = Foo()
>>> f + 1
Foo.__add__ was called
11
>>> f.__add__(1)
Foo.__add__ was called
11
>>>
Of course, actually using (1).__add__(2) in place of 1 + 2 would be inefficient (and ugly!) because it involves an unnecessary name lookup with the . operator.
That said, I do not see a problem with generally regarding the operator symbols (+, -, *, etc.) as simply shorthands for their associated method names (__add__, __sub__, __mul__, etc.). After all, they each end up doing the same thing by calling the same method.
1Well, roughly equivalent. As documented here, there is a set of special methods prefixed with the letter r that handle reflected operands. For example, the following expression:
A + B
may actually be equivalent to:
B.__radd__(A)
if A does not implement __add__ but B implements __radd__.

Avoiding Python sum default start arg behavior

I am working with a Python object that implements __add__, but does not subclass int. MyObj1 + MyObj2 works fine, but sum([MyObj1, MyObj2]) led to a TypeError, becausesum() first attempts 0 + MyObj. In order to use sum(), my object needs __radd__ to handle MyObj + 0 or I need to provide an empty object as the start parameter. The object in question is not designed to be empty.
Before anyone asks, the object is not list-like or string-like, so use of join() or itertools would not help.
Edit for details: the module has a SimpleLocation and a CompoundLocation. I'll abbreviate Location to Loc. A SimpleLoc contains one right-open interval, i.e. [start, end). Adding SimpleLoc yields a CompoundLoc, which contains a list of the intervals, e.g. [[3, 6), [10, 13)]. End uses include iterating through the union, e.g. [3, 4, 5, 10, 11, 12], checking length, and checking membership.
The numbers can be relatively large (say, smaller than 2^32 but commonly 2^20). The intervals probably won't be extremely long (100-2000, but could be longer). Currently, only the endpoints are stored. I am now tentatively thinking of attempting to subclass set such that the location is constructed as set(xrange(start, end)). However, adding sets will give Python (and mathematicians) fits.
Questions I've looked at:
python's sum() and non-integer values
why there's a start argument in python's built-in sum function
TypeError after overriding the __add__ method
I'm considering two solutions. One is to avoid sum() and use the loop offered in this comment. I don't understand why sum() begins by adding the 0th item of the iterable to 0 rather than adding the 0th and 1st items (like the loop in the linked comment); I hope there's an arcane integer optimization reason.
My other solution is as follows; while I don't like the hard-coded zero check, it's the only way I've been able to make sum() work.
# ...
def __radd__(self, other):
# This allows sum() to work (the default start value is zero)
if other == 0:
return self
return self.__add__(other)
In summary, is there another way to use sum() on objects that can neither be added to integers nor be empty?
Instead of sum, use:
import operator
from functools import reduce
reduce(operator.add, seq)
in Python 2 reduce was built-in so this looks like:
import operator
reduce(operator.add, seq)
Reduce is generally more flexible than sum - you can provide any binary function, not only add, and you can optionally provide an initial element while sum always uses one.
Also note: (Warning: maths rant ahead)
Providing support for add w/r/t objects that have no neutral element is a bit awkward from the algebraic points of view.
Note that all of:
naturals
reals
complex numbers
N-d vectors
NxM matrices
strings
together with addition form a Monoid - i.e. they are associative and have some kind of neutral element.
If your operation isn't associative and doesn't have a neutral element, then it doesn't "resemble" addition. Hence, don't expect it to work well with sum.
In such case, you might be better off with using a function or a method instead of an operator. This may be less confusing since the users of your class, seeing that it supports +, are likely to expect that it will behave in a monoidic way (as addition normally does).
Thanks for expanding, I'll refer to your particular module now:
There are 2 concepts here:
Simple locations,
Compound locations.
It indeed makes sense that simple locations could be added, but they don't form a monoid because their addition doesn't satisfy the basic property of closure - the sum of two SimpleLocs isn't a SimpleLoc. It's, generally, a CompoundLoc.
OTOH, CompoundLocs with addition looks like a monoid to me (a commutative monoid, while we're at it): A sum of those is a CompoundLoc too, and their addition is associative, commutative and the neutral element is an empty CompoundLoc that contains zero SimpleLocs.
If you agree with me (and the above matches your implementation), then you'll be able to use sum as following:
sum( [SimpleLoc1, SimpleLoc2, SimpleLoc3], start=ComplexLoc() )
Indeed, this appears to work.
I am now tentatively thinking of attempting to subclass set such that the location is constructed as set(xrange(start, end)). However, adding sets will give Python (and mathematicians) fits.
Well, locations are some sets of numbers, so it makes sense to throw a set-like interface on top of them (so __contains__, __iter__, __len__, perhaps __or__ as an alias of +, __and__ as the product, etc).
As for construction from xrange, do you really need it? If you know that you're storing sets of intervals, then you're likely to save space by sticking to your representation of [start, end) pairs. You could throw in an utility method that takes an arbitrary sequence of integers and translates it to an optimal SimpleLoc or CompoundLoc if you feel it's going to help.
I think that the best way to accomplish this is to provide the __radd__ method, or pass the start object to sum explicitly.
In case you really do not want to override __radd__ or provide a start object, how about redefining sum()?
>>> from __builtin__ import sum as builtin_sum
>>> def sum(iterable, startobj=MyCustomStartObject):
... return builtin_sum(iterable, startobj)
...
Preferably use a function with a name like my_sum(), but I guess that is one of the things you want to avoid (even though globally redefining builtin functions is probably something that a future maintainer will curse you for)
Actually, implementing __add__ without the concept of an "empty object" makes little sense. sum needs a start parameter to support the sums of empty and one-element sequences, and you have to decide what result you expect in these cases:
sum([o1, o2]) => o1 + o2 # obviously
sum([o1]) => o1 # But how should __add__ be called here? Not at all?
sum([]) => ? # What now?
You could use an object that's universally neutral wrt. addition:
class Neutral:
def __add__(self, other):
return other
print(sum("A BC D EFG".split(), Neutral())) # ABCDEFG
You could so something like:
from operator import add
try:
total = reduce(add, whatever) # or functools.reduce in Py3.x
except TypeError as e:
# I'm not 100% happy about branching on the exception text, but
# figure this msg isn't likely to be changed after so long...
if e.args[0] == 'reduce() of empty sequence with no initial value':
pass # do something appropriate here if necessary
else:
pass # Most likely that + isn't usable between objects...

Categories

Resources