68

Generally speaking, what should the unary + do in Python?

I'm asking because, so far, I have never seen a situation like this:

+obj != obj

Where obj is a generic object implementing __pos__().

So I'm wondering: why do + and __pos__() exist? Can you provide a real-world example where the expression above evaluates to True?

FThompson
  • 28,352
  • 13
  • 60
  • 93
user16538
  • 1,080
  • 1
  • 8
  • 17
  • 3
    Possibly a useful answer here: http://stackoverflow.com/questions/10748185/what-does-a-plus-sign-do-in-front-of-a-variable-in-python – ASGM May 29 '13 at 16:22
  • 2
    Related (different language, same query): [What does the unary plus operator do?](http://stackoverflow.com/questions/727516/what-does-the-unary-plus-operator-do) – Kevin May 29 '13 at 16:24
  • http://mail.python.org/pipermail/python-list/2004-September/274761.html – Josh Lee May 29 '13 at 17:43
  • 7
    If anyone which knows C/C++ expects that `++variable` increments the variable, he is in for a surprise. The best thing is that it is valid python code, so it will not throw an error. – smerlin Mar 02 '16 at 08:16

7 Answers7

52

Here's a "real-world" example from the decimal package:

>>> from decimal import Decimal
>>> obj = Decimal('3.1415926535897932384626433832795028841971')
>>> +obj != obj  # The __pos__ function rounds back to normal precision
True
>>> obj
Decimal('3.1415926535897932384626433832795028841971')
>>> +obj
Decimal('3.141592653589793238462643383')
Chris Taylor
  • 46,912
  • 15
  • 110
  • 154
  • 46
    I'm not Dutch, but does this seem like the obvious right way to round back to normal precision to anyone who is? – kojiro May 29 '13 at 16:31
  • 7
    @kojiro: [yes](http://stackoverflow.com/a/347749/4279). The reason is that decimals are immutable and therefore can carry more digits than the current precision allows so to take the current context into account, you need an explicit operation that uses it and may return a new decimal e.g., `0 + obj`, `1 * obj`, `obj ** 1`, etc. btw, `localcontext()` above is unnecessary: [`+Decimal('3.1415926535897932384626433832795028841971')`](http://ideone.com/Og4Brl) creates 2 decimals (1st corresponds to the given string, 2nd correspond to current precision (the result)). – jfs May 29 '13 at 17:00
  • 4
    The standard specification for decimal arithmetic specifies `plus` and `minus` functions that exhibit this behavior. In addition to providing those functions as part of a context, Python maps their behavior to unary `-` and `+`. See http://speleotrove.com/decimal/daops.html#refplusmin – casevh May 29 '13 at 17:03
  • @J.F.Sebastian Thanks for pointing that out; I edited to remove the references to `localcontext` – Chris Taylor May 29 '13 at 17:15
  • 12
    This looks like a bug in the `decimal` package more than anything. – Nick T Feb 10 '14 at 00:50
35

In Python 3.3 and above, collections.Counter uses the + operator to remove non-positive counts.

>>> from collections import Counter
>>> fruits = Counter({'apples': 0, 'pears': 4, 'oranges': -89})
>>> fruits
Counter({'pears': 4, 'apples': 0, 'oranges': -89})
>>> +fruits
Counter({'pears': 4})

So if you have negative or zero counts in a Counter, you have a situation where +obj != obj.

>>> obj = Counter({'a': 0})
>>> +obj != obj
True
flornquake
  • 3,156
  • 1
  • 21
  • 32
31

I believe that Python operators where inspired by C, where the + operator was introduced for symmetry (and also some useful hacks, see comments).

In weakly typed languages such as PHP or Javascript, + tells the runtime to coerce the value of the variable into a number. For example, in Javascript:

   +"2" + 1
=> 3
   "2" + 1
=> '21'

Python is strongly typed, so strings don't work as numbers, and, as such, don't implement an unary plus operator.

It is certainly possible to implement an object for which +obj != obj :

>>> class Foo(object):
...     def __pos__(self):
...        return "bar"
... 
>>> +Foo()
'bar'
>>> obj = Foo()
>>> +"a"

As for an example for which it actually makes sense, check out the surreal numbers. They are a superset of the reals which includes infinitesimal values (+ epsilon, - epsilon), where epsilon is a positive value which is smaller than any other positive number, but greater than 0; and infinite ones (+ infinity, - infinity).

You could define epsilon = +0, and -epsilon = -0.

While 1/0 is still undefined, 1/epsilon = 1/+0 is +infinity, and 1/-epsilon = -infinity. It is nothing more than taking limits of 1/x as x aproaches 0 from the right (+) or from the left (-).

As 0 and +0 behave differently, it makes sense that 0 != +0.

vlopez
  • 594
  • 4
  • 9
  • 5
    Actually, in C the unary `+` has a use beyond symmetry. It doesn't change the value, but its presence forces the implicit conversions that happen on arithmetic (which may change the type). –  May 29 '13 at 16:50
  • That's interesting. Could you post an example? – vlopez May 29 '13 at 17:25
  • @vklj: See e.g. http://www.verious.com/qa/does-the-unary-operator-have-any-practical-use (which also shows unary `+` being used to turn an l-value into an r-value). – Stuart Golodetz May 29 '13 at 17:28
14

A lot of examples here look more like bugs. This one is actually a feature, though:

The + operator implies a copy.

This is extremely useful when writing generic code for scalars and arrays.

For example:

def f(x, y):
    z = +x
    z += y
    return z

This function works on both scalars and NumPy arrays without making extra copies and without changing the type of the object and without requiring any external dependencies!

If you used numpy.positive or something like that, you would introduce a NumPy dependency, and you would force numbers to NumPy types, which can be undesired by the caller.

If you did z = x + y, your result would no longer necessarily be the same type as x. In many cases that's fine, but when it's not, it's not an option.

If you did z = --x, you would create an unnecessary copy, which is slow.

If you did z = 1 * x, you'd perform an unnecessary multiplication, which is also slow.

If you did copy.copy... I guess that'd work, but it's pretty cumbersome.

Unary + is a really great option for this.

user541686
  • 205,094
  • 128
  • 528
  • 886
  • 2
    Maybe to *you* it implies a copy. It's not at all obvious that is *should*, and it certainly isn't supported by the things people most often try to copy: lists, dicts, sets, etc. – chepner Jul 09 '20 at 13:22
  • @chepner: I've never seen something like `+dict` work in any language for you to dispute whether it implies a copy or not... it seems like you might be confused by what "implies" means? – user541686 Jul 09 '20 at 13:54
  • You are the one claiming that `+` implies a copy: on what basis? – chepner Jul 09 '20 at 13:58
  • @chepner: In a sane world `+` would do the same thing as `-` except that its return value has the opposite sign... it would be rather insane for `y = +(+x); z = -(-x); x += 1; assert(y == z)` not to hold true. Which... shouldn't exactly be shocking news. Surely you're aware that Python (and C++, etc.) as well numerous third-party libraries have been designed so that this **does** hold true. If that translates into "certainly not supported" by reality, I... don't know what to offer you. – user541686 Jul 09 '20 at 14:24
  • There is a big difference between `-` and `+`: `-` *always* changes the sign of its argument; `+` would be a no-op on a value that is already positive. I have no reason to think that `+(+x)` should make even one, let alone two, copies of `x` rather than simply returning `x` itself (which is exactly what `int.__pos__` does). – chepner Jul 09 '20 at 14:29
  • @chepner: Yes, it changes its sign. That says nothing about copying. `int` is immutable and wouldn't violate that in inequality. And it's patently false that you have "no reason" to believe `+(+x)` would make a copy. It literally does so for mutable cases and you're well-aware of this. That itself gives you reasons to believe this. The fact that you're ignoring reality doesn't change it I'm not interested in a pointless argument so this will be my last comment. – user541686 Jul 09 '20 at 14:43
  • Please provide an example of an existing type that makes use of this idiom. – chepner Jul 09 '20 at 14:48
8

For symmetry, because unary minus is an operator, unary plus must be too. In most arithmetic situations, it doesn't do anything, but keep in mind that users can define arbitrary classes and use these operators for anything they want, even if it isn't strictly algebraic.

I know it's an old thread, but I wanted to extend the existing answers to provide a broader set of examples:

  • + could assert for positivity and throw exception if it's not - very useful to detect corner cases.
  • The object may be multivalued (think ±sqrt(z) as a single object -- for solving quadratic equations, for multibranched analytical functions, anything where you can "collapse" a twovalued function into one branch with a sign. This includes the ±0 case mentioned by vlopez.
  • If you do lazy evaluation, this may create a function object that adds something to whatever it is applied to something else. For instance, if you are parsing arithmetics incrementally.
  • As an identity function to pass as an argument to some functional.
  • For algebraic structures where sign accumulates -- ladder operators and such. Sure, it could be done with other functions, but one could conceivably see something like y=+++---+++x. Even more, they don't have to commute. This constructs a free group of plus and minuses which could be useful. Even in formal grammar implementations.
  • Wild usage: it could "mark" a step in the calculation as "active" in some sense. reap/sow system -- every plus remembers the value and at the end, you can gather the collected intermediates... because why not?

That, plus all the typecasting reasons mentioned by others.

And after all... it's nice to have one more operator in case you need it.

orion
  • 226
  • 2
  • 9
5

__pos__() exists in Python to give programmers similar possibilities as in C++ language — to overload operators, in this case the unary operator +.

(Overloading operators means give them a different meaning for different objects, e. g. binary + behaves differently for numbers and for strings — numbers are added while strings are concatenated.)

Objects may implement (beside others) these emulating numeric types functions (methods):

    __pos__(self)             # called for unary +
    __neg__(self)             # called for unary -
    __invert__(self)          # called for unary ~

So +object means the same as object.__pos__() — they are interchangeable.

However, +object is more easy on the eye.

Creator of a particular object has free hands to implement these functions as he wants — as other people showed in their real world's examples.

And my contribution — as a joke: ++i != +i in C/C++.

MarianD
  • 13,096
  • 12
  • 42
  • 54
0

Unary + is actually the fastest way to see if a value is numeric or not (and raise an exception if it isn't)! It's a single instruction in bytecode, where something like isinstance(i, int) actually looks up and calls the isinstance function!

snoopyjc
  • 621
  • 4
  • 11