Python auto increment variables
[ date: 2024-12-10 || tags: python,heresy ]Although everybody knows the python language does not provide the unary increment/decrement operators, I’d still like to use that for some specific cases. Let’s explore an unconventional way how to do that.
The Stackoverflow Increment and decrement operator question
provides some insights why ++x
works but does not do what one expects.
This is parsed as +(+(x))
. The question is now 15 years old and everybody
moved on. The unary +
operator is an identity.
Side quest: what does unary
+
do in C?
Let’s start with a quiz: what could be the following class useful for?
class X():
def __init__(self, value=0):
self.x = value
def __call__(self, *args):
if len(args) == 0:
return self.x
self.x = args[0]
def __int__(self):
return int(self.x)
def __str__(self):
return str(self.x)
def __pos__(self):
self.x += 1
return self.x - 1
def __neg__(self):
self.x -= 1
return self.x + 1
Auto sequence is sometimes useful. Why? I’ve used this in GUI programming, when dumping tabular data with custom formatting. If you write your software correctly on the first time, congratulations. I may not and resort to incremental development that requires lots of editing. An example in pseudo code for Tcl/Tk application that fills rows of a grid layout in a sequence.
widget1.grid(row=0, column=0)
widget2.grid(row=1, column=0)
widget3.grid(row=2, column=0)
widget4.grid(row=3, column=0)
widget5.grid(row=4, column=0)
The layout many need to be tuned so lines are swapped, reordered, then save, run, check and repeat until satisfied. In C++ and using e.g. Qt I’d do something like:
row = 1;
widget3->grid(row++, 0);
widget2->grid(row++, 0);
widget5->grid(row++, 0);
widget1->grid(row++, 0);
widget4->grid(row++, 0);
In any way I swap the lines, they’ll be always in the order as they’re in the source file and I don’t have to renumber the rows each time I need to save and run it. I could turn it to a fixed number sequence once I’m done with the tuning. Expecting future changes I would leave it as is, extending that in the future would be much easier.
But python religiously does not offer the ++
operators, neither pre-increment
nor post-increment. OTOH python offers other tuning methods that could be
hidden on the class level and abusedutilized as pure syntax repurposing.
Using the class from above, the python version can be rewritten like this, with the reordered lines:
row = X()
widget3.grid(+row, column=0)
widget2.grid(+row, column=0)
widget5.grid(+row, column=0)
widget1.grid(+row, column=0)
widget4.grid(+row, column=0)
The +
operator pos
, unary and returning the positive size of the value.
Overloading that to increment the value is at least a bit intuitive even for a
casual reader not familiar with the class X
.
As a discouragement the following works (python code):
++x
and (as implemented) increments x
by 1
, just once. But no surprise or magic
here, standard python accepts this for any integer value. This will be first
evaluated as +x
and then the unary +
applied on the result, as said before
+(+x)
so the leading +
does not involve x
. We can go crazy:
+x
++x
+++x
++++(++++x)
+++++++++++++++x
All of them do single post-increment. In standard python this works for integer-like objects too:
+1
++1
+++1
++++1
This is evaluated as 1. Actually we can go crazy even more, because the unary
-
and +
can be mixed freely:
+1
-1
-+1
+-1
-+-1
+-+1
-+-+1
+-+-1
And so on. I’ll leave that as an exercise what the final value of each line is. This also works in C, but I digress.
What I find really bad is that ++x
does not match the pre-increment semantics
but given the limitations of the operator overloading we can’t do anything
about that.
With a minor update the class X
, the bare value of such object instance
can be used in expressions like x+1
or x-1
:
def __add__(self, value):
return self.x + value
def __sub__(self, value):
return self.x - value
and similar for the remaining integer operators. This is non-standard extension best to be used within limited scope.
Heresy.