Warm tip: This article is reproduced from stackoverflow.com, please click
python

Mimic ++ operator for bookkeeping variable

发布于 2020-03-30 21:15:16

For my application GUI (tkinter), I need a 'bookkeeping' variable that holds the current row number for a UI widget. I prefer not to hard code the row number because inserting a widget would involve renumbering all row numbers following it. In this situation, an increment operator (x++, similar to C++) would come in very handy, because incrementing the variable when necessary would require only two additional characters. However, Python does not support increment operators.

To avoid increment statement between elements that should go on different rows, I came up with the following solution but I am wondering what an alternative might be:

rownums = list(range(100))
rowstay = lambda: rownums[0]        # Return current row number and stay on row.
rowmove = lambda: rownums.pop(0)    # Return current row number and move on to next.

print(f'Two items: {rowstay()}, {rowmove()}')
print(f'One item: {rowmove()}')
print(f'Two items: {rowstay()}, {rowmove()}')

For example (tkinter):

# Create some buttons.
mybutton1 = tkinter.button(master, 'Button on row 0')
mybutton2 = tkinter.button(master, 'Button on row 0')
mybutton3 = tkinter.button(master, 'Button on row 1')


# Place buttons (use mimicked increment 'operator').
mybutton1.grid(row=rowstay(), column=0)
mybutton2.grid(row=rowmove(), column=1)
mybutton3.grid(row=rowmove(), column=0)

What alternative code would provide the same functionality?

Questioner
Jan-WillemL
Viewed
15
Jan-WillemL 2020-02-02 22:41

From the comments and answer, I learned that mutating with a lambda and assigning lambdas is not Pythonic. Several comments suggest using += 1 but this is exactly what I wanted to avoid because it requires an additional statement for each time the variable should be incremented. The solution with higher order functions (or a class) mimics ++x instead of x++.

My solution (second-best, see better solution under EDIT):

class BookkeepingVar():
    """ Variable with the ++-operator from C++ mimicked.

        Example:
        x = BookkeepingVar(0)
        y = x() + 1    # y = 1, x = 1 
        z = x(1) + 1   # z = 1, x = 2 
    """

    def __init__(self, start=0):
        self.var = [start]

    def __call__(self, incr=0):
        if incr:
            self.var.append(self.var[0] + incr)
            return self.var.pop(0)
        else:
            return self.var[0]

Example usage:

# Create some buttons.
mybutton1 = tkinter.button(master, 'Button on row 0')
mybutton2 = tkinter.button(master, 'Button on row 0')
mybutton3 = tkinter.button(master, 'Button on row 1')

# Place buttons.
row = BookkeepingVar(0)
mybutton1.grid(row=row(0), column=0)
mybutton2.grid(row=row(1), column=1)
mybutton3.grid(row=row(1), column=0)

Please note:

  1. There's no upper bound to the variable value (as in the lambda example).
  2. The list length never exceeds two, so I can't imagine inefficiencies of pop() to be problematic.
  3. The solution is slightly more general than mimicking x++ only because it also supports increments other than 1.
  4. The solution also supports decrements (use a negative argument).
  5. The __call__ method was implemented in order to make notation as short as possible.

EDIT:

A simpler and more elegant solution without list (same usage):

class BookkeepingVar():
    """ Variable with the ++-operator from C++ mimicked.

        Example:
        x = BookkeepingVar(0)
        y = x() + 1    # y = 1, x = 1 
        z = x(1) + 1   # z = 1, x = 2 
    """
    def __init__(self, start=0):
        self.var = start

    def __call__(self, incr=0):
        self.var += incr
        return self.var - incr

EDIT 2:
Remark about ++x vs. x++ (strikethrough).