Increment, decrement... Can we have inc(), dec() functions in Python? feature request :)

No. It is nothing like next.

I’ve already given code using Python syntax that shows how Pascal inc and dec work. Unfortunately you can’t run that code, because they are don’t work in Python, but you can surely follow the input and the expected result.

Let’s try again:

x = 15
inc(x)  # There is NO ASSIGNMENT HERE.
assert x == 16  # But like magic, x has a new value! How???

We can’t write a full implementation of inc because Python doesn’t work that way, but we can almost simulate a partial implementation:

def inc(name):  # Only works with globals :-(
    g = globals()
    g[name] += 1
    return None

x = 15
inc('x')  # We have to quote the name (use a string).
assert x == 16

But this is a pale shadow of the Pascal inc:

  • the real inc does not need you to quote the variable name;
  • nor does it limit you to just globals;
  • it works with any namespace or variable scope;
  • and of course any variable, not just “x”.

Pascal’s inc and dec are merely special cases of a more general technique which Pascal supports, but Python doesn’t: pass by reference.

Of course we can get the same effect. We just have to use assignment:

x = x + 1

So we’re not really losing any functionality, we just have to write it a different way.

Pardon my ADHD, but I have edited my original comment to give a more thoughtful response.

The answer is still, no, they are nothing alike.

Even if they are not alike per se, I struggle to conceive of a use case where inc can do something that an iterator cannot do already.

Pascal doesn’t have the range of assignment operators of Python, so having inc() and dec() may make sense for making the intention clearer. That all changes after the C language was published.

In Python the idioms for increment and decrement are clear enough:

x += n
x -= n

Maybe inc() and dec() are obsolete remnants of when many books about algorithms used a Pascal-like language for the examples.

2 Likes

For your consideration:

import ctypes
import sys

def inc(obj):
    mem = (ctypes.c_byte * sys.getsizeof(obj)).from_address(id(obj))
    new_obj = obj + 1
    new_mem = (ctypes.c_byte * sys.getsizeof(new_obj)).from_address(id(new_obj))

    for i in range(len(mem)):
        mem[i] = new_mem[i]

a = 28
inc(a)
print(a)
29
print(28)
29

:upside_down_face:

8 Likes

I know you’re kidding around, but beware that int() in Python 3 is an arbitrary-precision integer which is usually built with 30-bit ‘digits’. Thus this code will fail on a digit boundary such as inc(2**30 - 1), and it will probably cause a segfault (access violation) when it tries to write beyond the allocated memory of obj.

Anyway, inc() in Pascal has nothing to do with modifying the numerical value of an integer object. Pascal is working with primitive integers, for which the values and rules for signed and unsigned integers are implemented in the instruction set architecture and hardware. Python doesn’t have primitive types, but the int type is immutable to make it behave like a primitive.

What prevents implementing inc() from being implemented as a pure Python function is the lack of call by reference, such as the C++ function declaration void inc(int &x, unsigned int addend). Python uses names in namespaces instead of integer addresses in an address space. Thus a variable reference in Python would be the combination of a variable name and its f_locals namespace. To support call by reference in general, a frame’s f_locals would need to be implemented as a custom mapping object that supports modifying a frame’s fast locals and cell objects. It’s a moot point, however, because it’s unlikely that Python would ever support call by reference given it already supports lexical closures. Using a closure is more limited in regards to function parameters, but the lexical context makes it easier to understand when reading and debugging code.

1 Like

Are you trolling? I’ve demonstrated what inc does at least twice, and the OP linked to documentation for it. It has nothing to do with iteration.

Here is a use-case: you have a variable, line_width. You want to increase the width of the line by one pixel, by incrementing the value of line_width by 1. How do you do that with an iterator?

Traditionally, in Python, we would just write line_width = line_width + 1, and that’s fine. If the variable is in a different namespace, we might prefer this:

namespace.data.blob.ns.line_width += 1

because it avoids needing to type out the fully resolved variable name, but that’s essentially just syntactic sugar to save some typing.

Just because we might put x = x + 1 inside a loop doesn’t mean that incrementing by 1 is related to iteration. We can put import math inside a loop too, doesn’t mean that there’s nothing import can do that an iterator can’t do.

Neat hack. And by “neat” I mean truly awful.

Aside from it not working in alternative interpreters like Jython, even in CPython using this will silently break your interpeter:

>>> a = 28
>>> inc(a)
>>> print(a)
29
>>> 27 + 1
29
>>> 14*2
29

Well that’s not good. But it gets worse:

>>> b = [2, 3, 4]
>>> inc(b[0])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in inc
IndexError: invalid index
>>> b
[3, 3, 4]
>>> 9 - 7
3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: raw write() returned invalid length 3 (should have been between 0 and 2)
3
>>> print(1+1)
3
3

Yeah, let’s not do this.

Try incrementing zero.

Assuming line_width is an iterator:

next(line_width)

If line_width is not an iterator and there is no benefit in turning it into one (say, we’re outside a while loop), then again I see no benefit in not just using line_width += 1. An addition of inc bakes no bread.

Why would you assume that??

Okay. Let’s say that you have line_width and line_height. Depending on user input, you may want to increment OR decrement either of them. Show me how, under any circumstances, it makes sense for these to be iterators. Go ahead, I’ll wait.

Is it really worth adding a whole new built-in for absolute margin cases like these? Stdlib isn’t a kitchen sink:

  1. Default increment - just use x += 1­

  2. Increment during most expressions as a part of something like a while loop or map - use an iterator

  3. Something like 2, but you need flexibility for margin cases (say, decrement under some specific conditions as a part of a ternary expression) - use a walrus

Hi all,
after all I think we can stick to what we already have in python:
x += 1

Simple because it’s just shorter to type.
+=1 takes less characters than typing inc()
:slight_smile:
Anyways, thank you for discussion.
M.

2 Likes

I think the Python equivalent for the Pascal inc(x) and dec(x) procedures would be object methods such as x.inc() and x.dec(x).

These methods can not work for immutable objects, of course, simply because immutable objects, by definition, cannot be changed. However you could create a mutable class that wraps an immutable object and achieves the behaviour you want.

Something like this might work for integers

class UserInt:
    def __init__(self, value=0, base=10): 
        if isinstance(value, str): 
            self._value = int(x, base)
        else: 
            self._value = int(value)
    def __int__(self): return self._value
    def inc(self, increment=1): self._value +=int(increment)
    # __add__, etc. See emulating-numeric-types.

You would have to use x = UserInt(1213) everywhere you were using x = 1213 to create objects of this type instead of objects of int type, for both literal values and non-literal values.

You would have to duplicate this for each of the numeric types.

Personally, I think it would be easier to adapt to using the C/C++ style increment and decrement operators. I recall transitioning from Fortran to Pascal to C to Python, with assembler, Pilot, Perl, SQL, Go, Rust along the way. It gets easier.

What you’re saying is: it’s easier to learn Python than to try to force Pascal idioms to work inside Python :slight_smile:

Personally, I found it minorly irksome that I couldn’t use x++ in Python, but it turns out, there are actually not that many situations where post-incrementing is necessary. It would be kinda nice for some algorithms, but it’s far from crucial. Other forms of incrementing are even less important - a stand-alone statement x++; or ++x; can be written as x += 1 without much loss of clarity, and pre-incrementing isn’t really all that common.

2 Likes

Isn’t x += 1, x -= 1 more elegant? :wink:

1 Like

just a follow-up, this is interesting:

In the standard library module operator, there are some “in place operators” that potentially change the value of a variable “in place”.
For example: operator.iadd(x, y)
see: https://docs.python.org/3/library/operator.html#in-place-operators

However since integers are immutable, it only works for lists. (documentation mentions also dictionaries, but I can’t reproduce it with dictionaries).

just a follow-up, this is interesting:

In the standard library module operator, there are some “in place operators” that potentially change the value of a variable “in place”.
For example: operator.iadd(x, y)
see: https://docs.python.org/3/library/operator.html#in-place-operators

However since integers are immutable, it only works for lists.
(documentation mentions also dictionaries, but I can’t reproduce it
with dictionaries).

It can work with anything with internal mutable structure via the
__iadd__ dunder method. As you say, it doesn’t work for ints because
they are immutable. But you can add it to classes of your own. Here’s an
example method from a progress tracker class of my own:

 def __iadd__(self, delta):
   ''' Operator += form of advance().

           >>> P = Progress()
           >>> P.position
           0
           >>> P += 4
           >>> P.position
           4
           >>> P += 4
           >>> P.position
           8
   '''
   self.advance(delta)
   return self

Cheers,
Cameron Simpson cs@cskk.id.au