0

I am currently working on my first program on python, however, when I've encountered a problem concerning the scope of definition of my variables. Here is the problematic sample of my code :

def listen(topLeft, bottomRight):

    timeless = 0
    # The key combination to check
    COMBINATIONS = [
        {keyboard.Key.shift, keyboard.KeyCode(char='b')}
    ]

    # The currently active modifiers
    current = set()

    def execute():
        global topLeft,bottomRight,timeless
        print("Do Something")

        if timeless == 0:
            topLeft = mouse.position
            timeless += 1
        elif timeless == 1:
            bottomRight = mouse.position
            timeless += 1
        elif timeless >= 2:
            return False

    def on_press(key):
        global timeless
        if any([key in COMBO for COMBO in COMBINATIONS]):
            current.add(key)
            if any(all(k in current for k in COMBO) for COMBO in              COMBINATIONS):
                execute()

    def on_release(key):
        if any([key in COMBO for COMBO in COMBINATIONS]):
            current.remove(key)

    with keyboard.Listener(on_press=on_press, on_release=on_release) as     listener:
        listener.join()

Basically, once the Listener calls on_press, execute is called. In order to pass the timeless variable, I use the global tag. However, once the program comes to executing the execute function, I receive the following error code : NameError: name 'timeless' is not defined. Any help would be appreciated, as I have tried pretty much everything I could

8
  • The global timeless statement has no effect on execute; it just makes assignment to timeless in on_press refer to the global, rather than a local, variable. Commented Apr 3, 2019 at 19:05
  • However, timeless is not a global; it's a local variable of the listen function. Fix your indentation so we get a clearer idea of how your code is actually structured. Commented Apr 3, 2019 at 19:06
  • 1
    It is perfectly fine to ask about problems global variables. However, you will do yourself a favour if you never use them. Use other mechanisms for passing values around. Commented Apr 3, 2019 at 19:09
  • @chepner Fixed the indentation. I see where you're coming from, however, as execute is called everytime on_press is called, I cannot use a local variable, as it will re-declare it everytime (and it is the listener that calls on_press). Commented Apr 3, 2019 at 19:34
  • @zvone Which other mecanisms are you thinking of? Because currently, I am restricted by the fact that I am not "personnally" calling the function, but it is the listener that does so. Is my only way out "changing" the listener's implementation directly in the library? Commented Apr 3, 2019 at 19:36

3 Answers 3

1

Since timeless is actually a local variable in the function listen, it's not a global variable, and in fact your error comes from the fact that there is no global variable named timeless. What execute needs is to declare timeless (and probably topLeft and bottomRight as well) as nonlocal, so that it refers to the first binding in the stack of containing lexical scopes.

def listen(topLeft, bottomRight):

    timeless = 0
    # The key combination to check
    COMBINATIONS = [
        {keyboard.Key.shift, keyboard.KeyCode(char='b')}
    ]

    # The currently active modifiers
    current = set()

    def execute():
        # modify the local variables in listen
        nonlocal topLeft,bottomRight,timeless
        print("Do Something")

        if timeless == 0:
            topLeft = mouse.position
            timeless += 1
        elif timeless == 1:
            bottomRight = mouse.position
            timeless += 1
        elif timeless >= 2:
            return False

    ...

In a function defined at the global scope, global and nonlocal are identical, as the first containing scope is the global scope. In a nested function, global jumps straight to the global scope, while nonlocal walks up the nesting scopes one at a time.

Sign up to request clarification or add additional context in comments.

Comments

1

You declare a global variable outside of any function. You can reference a global variable inside a function without using the keyword. You only need to use the keyword global when you are assigning a new value to the variable, sort of as a way of specifying that you want to assign the value to the global variable and not to a new local variable that shares the same name. See this for examples. For your example, first define your globals outside of any function, then use them in the functions, using the global keyword when you want to modify:

# Global variable definitions outside of any function.
timeless = 0
topLeft = 0
bottomRight = 0

# Define the function that uses the variables.
def execute():
    print("Do Something")
    global timeless
    if timeless == 0:
        global topLeft  # modifying the global variable with this name!
        topLeft = mouse.position
        timeless += 1
    elif timeless == 1:
        global bottomRight  # modifying the global variable!
        bottomRight = mouse.position
        timeless += 1
    elif timeless >= 2:
        return False

All of that said, it's probably best to avoid globals in general. You could define a class instead and use a member property for those things that need to be modified and referenced in multiple methods. For example, something like:

class Listener:
    def __init__(self):
        self.timeless = 0
        self.top_left = 0
        self.bottom_right = 0

    def execute(self, mouse):  # pass some mouse object in, otherwise what's "mouse"?
        print('Do something')
        if self.timeless == 0:
            self.top_left = mouse.position
            self.timeless += 1
        elif self.timeless == 1:
            ...

7 Comments

I also don't want to use global variables, however it is the only way I've found to make my function not re-declare timeless every time it is called. Concerning your code, I just tried it, and it now tells me : name 'timeless' is used prior to global declaration (as the if is before the global tag i think)
Did you move the timeless = 0 declaration outside of all functions?
Yes I did : i moved right underneath the listen definition. This error code popped up, so I moved it right underneath all the import tags : same problem
Huh, weird. Try it with global timeless before the if statements. I would still recommend converting everything to a class though and just use declare self.timeless etc. for your reused variables in the __init__ method and use as needed in other defs in the class. I updated with an example of how to start that class.
Thanks for the feedback! For now I will stick onto using nonlocal instead of global, as it, somehow, works. However, I will definitely try your method next time, as global variables are way too much of a struggle
|
0
timeless = 0
topLeft = 0
bottomRight = 0
current = set()

def listen(topLeft, bottomRight):
    timeless = 0
    # The key combination to check
    COMBINATIONS = [
        {keyboard.Key.shift, keyboard.KeyCode(char='b')}
    ]
    # The currently active modifiers
    current = set()

def execute():
    print("Do Something")
    if timeless == 0:
        topLeft = mouse.position
        timeless += 1
    elif timeless == 1:
        bottomRight = mouse.position
        timeless += 1
    elif timeless >= 2:
        return False


def on_press(key):
    if any([key in COMBO for COMBO in COMBINATIONS]):
        current.add(key)
        if any(all(k in current for k in COMBO) for COMBO in COMBINATIONS):
            execute()


def on_release(key):
    if any([key in COMBO for COMBO in COMBINATIONS]):
        current.remove(key)

1 Comment

You have to right your main function which starts execution in the last at same level not inside any function

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.