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.
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.
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.
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.
>>> 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
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.
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:
Default increment - just use x += 1
Increment during most expressions as a part of something like a while loop or map - use an iterator
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
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
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.
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
EDIT: of course if I wanted to use the my_instance elsewhere as a number (with math operations), I might need to further implement those operations in the class…