Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Viper: problem updating nonlocal variables #8086

Open
@peterhinch

Description

@peterhinch

I am trying to write a Viper function which retains state between calls, preferably using a closure. The following produces an unexpected result (all testing on a Pyboard 1.1 running V1.17):

def foo():
    x : int = 0
    @micropython.viper
    def inner() -> int:
        nonlocal x
        q : int = int(x)
        q += 1
        x = q  # x never increments
        return int(x)
    return inner

bar = foo()
bar()  # 0
bar()  # 0

Oddly similar code using a global works:

x = 0
@micropython.viper
def foo() -> int:
    global x
    q : int = int(x)
    q += 1
    x = q
    return int(x)

foo()  # 1
foo()  # 2
foo()  # 3

Attempting this with a class produces a type error

class Foo:
    def __init__(self):
        self.x : int = 0

    @micropython.viper
    def bar(self) -> int:
        q : int = int(self.x)
        self.x += q  # ViperTypeError: can't do binary op between 'object' and 'int'
        return int(self.x)

foo = Foo()

To date my "best" solution is to use a closure with an integer array to contain state:

from array import array
def foo():
    x = array('i', (0,))
    @micropython.viper
    def inner() -> int:
        i = ptr32(x)
        r = i[0]
        i[0] += 1
        return r
    return inner

bar = foo()
bar()  # 0
bar()  # 1
bar()  # 2

It would be good if nonlocal worked as expected and if there were a solution to the problem of accessing integer bound variables.

Incidentally the speedup using Viper in my actual application is phenomenal :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    py-coreRelates to py/ directory in source

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions