NameError says variable is not defined, but only i

2019-08-18 03:10发布

问题:

I am trying to implement a keep-alive that sends some data every 30 seconds to keep a telnet connection open.

My code calls reinitScore every second. This function will sometimes call calculateWinner, which sends the data through telnet via stelnet.send(data).

The problem is, when I call stelnet.send(data) inside any function, it raises a NameError: global name 'stelnet' is not defined.

My questions is: why would stelnet.send(data) work in one place, and not another?

Here is the part of my code that concerns telnet transfer and function calling:

import socket, select, string, sys
import string
import threading

leftKeyCounter = 0
rightKeyCounter = 0
frontKeyCounter = 0
backKeyCounter = 0

# function called by reinitScore
def calculateWinner(d):
    scores = {}
    high_score = 0
    for key, value in d.items():
        try:
            scores[value].append(key)
        except KeyError:
            scores[value] = [key]
        if value > high_score:
            high_score = value
    results = scores[high_score]
    if len(results) == 1:
        print results[0]
        stelnet.send(results[0])
        return results[0]
    else:
        print 'TIE'
        return 'TIE', results

#called once and repeat itselfs every second
def reinitScore():
    threading.Timer(1, reinitScore).start()
    #globaling for changing the content
    global leftKeyCounter
    global rightKeyCounter
    global frontKeyCounter
    global backKeyCounter
    values = {'left' : leftKeyCounter, 'right' : rightKeyCounter, 'front' : frontKeyCounter, 'back' : backKeyCounter}
    if (leftKeyCounter != 0 or rightKeyCounter != 0 or frontKeyCounter != 0 or backKeyCounter != 0):
        calculateWinner(values)
        leftKeyCounter = 0
        rightKeyCounter = 0
        frontKeyCounter = 0
        backKeyCounter = 0
        print "back to 0"

reinitScore()
if __name__ == "__main__":

    if(len(sys.argv) < 3) :


    print 'Usage : python telnet.py hostname port'
    sys.exit()

    host = sys.argv[1]
    port = int(sys.argv[2]) 
    stelnet = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    stelnet.settimeout(2)

    # connect to remote host
    try :
        stelnet.connect((host, port))
    except :
        print 'Unable to connect'
        sys.exit()

    print 'Connected to remote host'

    while True:
       // ... Some code that has nothing to do with telnet 

    while 1:
       socket_list = [sys.stdin, stelnet]

        read_sockets, write_sockets, error_sockets = select.select(socket_list , [], [])

        for sock in read_sockets:
            if sock == stelnet:
                data = sock.recv(4096)
                if not data :
                    print 'Connection closed'
                    sys.exit()
                else :
                    sys.stdout.write(data)

            else :
                msg = sys.stdin.readline()
                stelnet.send(msg)

I tried to declare stelnet as a global variable at many places, but it doesn't change anything --- I always get the "not defined" NameError.

回答1:

In response to your updated code... The error message is still correct, because although you have defined stelnet at the module level, you've defined it too late. It's definition occurs after its use in the calculateWinner function.

Stripping your code down to a ridiculously minimal example, you are doing something like this:

def calculateWinner():
    # A leap of faith...  There is no `stelnet` defined
    # in this function.
    stelnet.send(results[0])

def reinitScore():
    # Indirectly depends on `stelnet` too.
    calculateWinner()

# But we haven't defined `stelnet` yet...
reinitScore() # Kaboom!

# These lines will never run, because the NameError has
# already happened.
if __name__ == '__main__':
    stelnet = ...  # Too late.

calculateWinner depends on a name that does not exist when the function is compiled. Whether it works or crashes will depend on whether some other code has defined stelnet 1) where calculateWinner can get at it, and 2) before calculateWinner is executed.

Suggestions

Functions that depend on global mutable state are hard to follow, let alone code correctly. It's not easy to tell what depends on which variables, or what's modifying them, or when. Also, coming up with an MCVE is more trouble than it should be, because functions that appear independent might not be.

Stuff as much of your module-level code as you can into a main function, and call it (and nothing else) from the body of if __name__ == '__main__': (since even that is actually at module level).

Consider something like this:

def reinit_score(output_socket, shared_scores):
    # Ensuring safe concurrent access to the `shared_scores`
    # dictionary is left as an exercise for the reader.
    winner = ...  # Determined from `shared_scores`.
    output_socket.send(winner)
    for key in shared_scores:
        shared_scores[key] = 0
    threading.Timer(
      interval=1,
      function=reinit_score,
      args=[output_socket, shared_scores],
      ).start()

def main():
    output_socket = ...  # This was `stelnet`.
    shared_scores = {...}  # A dictionary with 4 keys: L/R/U/D.
    reinit_score(output_socket, shared_scores)
    while True:
        play_game(shared_scores)
        # `play_game` mutates the `shared_scores` dictionary...

if __name__ == '__main__':
    main()

These functions are still connected by the shared dictionary that they pass around, but only functions that are explicitly passed that dictionary can change its contents.



回答2:

Your code is not working because you are not passing stelnet to your function.