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.
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 Enum
s 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
@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)
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.
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.
(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
x = 1 # integer
y = x
x += -1 # x becomes 0
# In Python, y is still 1. Is it 0 in Pascal, as in C?
>>> 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
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