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

hello,
I’ve seen these functions in Pascal ( Inc and Dec - Free Pascal wiki )
Why not in Python?
I find them elegant.

(Or could a user write these functions by himself?
I was trying to do it for myself, but stucked.)

Thank you.
M.

It might be possible to use Enums to do this. Instead of inc or dec, just add or subtract 1.

Not really too hard to implement yourself if you wanted:

def inc(x):
    return x + 1
1 Like

@ndc86430
not really, because if you try it:

n=10
inc(n) # of course the result is 11, but it won't get assigned to 'n'
print(n) # 'n' would be still 10

I don’t know how to implement a function like inc(variable, addend=1) using the current introspection capabilities. There might be changes coming down the pike that enable modification of a frame’s fast locals.

Instead, one can use augmented assignment, such as x += 1. There’s no equivalent of augmented assignment using the walrus operator in an expression, so incrementing x in an expression would have to use x := x + 1. For example:

>>> n = 10
>>> print(n := n + 1)
11
>>> n
11

It is not possible to write such a function. The reason is that integer objects in Python are immutable, i.e. an int object has no method which modifies its underlying value.
Therefore you have to reassign:

def inc(x):
    return x + 1

x = 1
x = inc(x)
1 Like

In theory it might be possible, but it might be safer to force the variable to be a nonlocal. But it doesn’t really make sense with Python’s execution model to have something that, when passed a variable, rebinds that variable in the enclosing scope. I mean, what would this do?

inc(3)

Or this?

inc(x ** 2)

Or this?

inc(x + y)

If you look at all other languages where they have a meaningful inc(x) operation, they will disagree on the meanings of the above expressions. Some will say “nah, can’t do that, only increment a variable” (which means that inc isn’t a function in the same sense that it is in Python). Others define argument replacement by text, so if you say inc(x + y) it translates into x + y = x + y + 1 which will most likely fail. And some will quite happily let you change the value of 3, with all the entertaining effects that that would have.

1 Like

Chris, I forgot to add the disclaimer that, even if you could write a function to modify the fast locals of the calling frame, which I don’t think is possible with current introspecting capabilities in pure Python code, the signature would have to change to pass the variable name as a string. The alternative would be to search the calling scope for a matching object to find the variable to update, but that has poorly defined behavior.

Fair enough. Since you mentioned introspection, I assumed you’d be doing some kind of black-magic parsing to try to deduce the variable to increment.

In any case, it’s not really something that makes any sense, unless you also pass a namespace to mutate - and in that case, all you’re really doing is ns[varname] += 1 or perhaps the equivalent with getattr/setattr.

To the OP: Anything is possible in a sufficiently-powerful language. But not everything is advisable. Instead of trying to write Pascal code in Python, learn about Python’s idioms and how to use them. Your code will be infinitely better for it.

1 Like

(To be clear, I’m not saying “Pascal sucks and Python’s way is so much better”. I would give equivalent advice for any pair of languages - don’t try to write Python code in Lisp, for instance.)

The namespace to mutate would be the calling frame’s locals, e.g. inspect.currentframe().f_back.f_locals. However, modifying a frame’s f_locals doesn’t update its fast locals. There are a couple of PEPs, if I recall correctly, that propose to support this.

You may be interested in GitHub - dankeyy/incdec.py: for all your ++ -- needs

2 Likes
  1. Because we already have a generalized near-equivalent in Python as augmented assignment rather than as a function. Python also combines many other operators with assignment and its version applies to any class that defines the appropriate special methods. An expanded version of the closest possible equivalent of the example in your free pascal link is
x = 1  # integer
y = x
x += -1  # x becomes 0
# In Python, y is still 1.  Is it 0 in Pascal, as in C?
  1. The exact equivalent is not possible in Python because the meanings of ‘variable’ and ‘value’ are, AFAIK, importantly different in Pascal and Python. To make changes in place, that also affect aliases, one must use mutable objects.
>>> x = [1]
>>> y = x
>>> x[0] += 1
>>> x == y == [2]
True

In many languages a variable is often implemented simply as a memory address on the stack or heap. The equivalent of an address in Python would be the combination of a variable name and namespace (e.g. a name in frame’s f_locals). I doubt the community would embrace builtin support for C++ style ‘call by reference’ (e.g. if a callable is a function that flags the parameter as by-reference, then pass a reference to the variable, i.e. the variable name and its namespace, instead of a reference to its value). This would make the setup for a call more expensive at runtime, plus I think using lexical closures is cleaner for debugging issues and understanding code while reading it (notwithstanding help from an IDE).

It might not always be a variable name; with nonlocals, the nearest you’ll get to an “address” is a closure and a cell reference. But that’s specific to CPython, and other implementations may handle closures quite differently.

The f_locals mapping actually includes both locals and nonlocals.

import inspect

def f():
   x = 1
   def g():
       x
       return inspect.currentframe().f_locals
   return g()
>>> f()
{'x': 1}

Currently it’s a snapshot for a function scope, but there are proposals to change this when f_locals is accessed directly instead of via builtin locals(). Modifying the custom f_locals mapping would update fast locals and cell objects.

That is nothing like Pascal’s inc and dec procedures, aside from the mere incidental fact that they both increment or decrement something.

Unlike C’s ++ and -- operators, inc and dec aren’t functions with a return result, they are procedures and they operate purely by side-effect.

You cannot write Pascal-style inc and dec functions in Python, because they require call-by-reference argument passing semantics, which is not supported by the Python execution model.

The best you could do is write a function that takes a variable name as a string, and a namespace to operate in:

inc('x', globals)  # equivalent to globals()['x'] += 1

but of course that’s nothing near as elegant as Pascal’s:

inc(x);

Note that in Pascal there is no quoting required, or any need to specify the scope of the variable x, because the compiler already knows that.

That trick with the encoding to change syntax is clever, but all you can do with it is change syntax. You can’t change the execution model of the language with it :slight_smile:

1 Like

inc and dec are procedures, not functions. Python already implements inc and dec with itertools.count iterator:

v = itertools.count(1)
next(v)

Are you sure you understand what Pascal’s inc and dec do? Because they are nothing like itertools.count.

If we write inc in Python syntax instead of Pascal syntax, we would get this:

x = 15
inc(x)  # Notice that we do NOT use assignment.
assert x == 16

some_module.y = 23
dec(some_module.y)
assert some_module.y == 22

You can’t do that with itertools.count. Actually, you can’t do that in Python at all. You either have to use assignment, or you have to quote the variable name.

Isn’t that just next with a different name?

import itertools

x = itertools.count(15)
next(x)  # This is annoying, I know
assert next(x) == 16

y = itertools.count(23, -1)
next(y)
assert next(y) == 22