NameError表示变量未定义,但仅限于某些地方

时间:2016-04-24 21:34:50

标签: python nameerror scoping

我正在尝试实现一个keep-alive,每隔30秒发送一些数据以保持telnet连接打开。

我的代码每秒都会调用reinitScore。此函数有时会调用calculateWinner,它通过stelnet.send(data)通过telnet发送数据。

问题是,当我在任何函数中调用stelnet.send(data)时,它会引发NameError: global name 'stelnet' is not defined

我的问题是:为什么stelnet.send(data)会在一个地方工作,而不是另一个地方?

以下是我的代码中涉及telnet传输和函数调用的部分:

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)

我尝试在许多地方将stelnet声明为global变量,但它不会改变任何内容 - 我总是得到“未定义”NameError

2 个答案:

答案 0 :(得分:1)

响应您更新的代码...错误消息正确,因为虽然您已在模块级别定义了stelnet,但您已将其定义为太晚了。在calculateWinner函数中使用之后,的定义就会出现。

将你的代码剥离到一个荒谬的最小例子,你正在做这样的事情:

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取决于不存在的名称。它是否有效或崩溃将取决于其他代码是否已定义stelnet 1)calculateWinner可以获取它,以及2)执行calculateWinner之前。

<强>建议

依赖于全局可变状态的函数很难遵循,更不用说正确编码了。要判断什么取决于哪些变量,或者修改它们是什么,或者何时修改它们并不容易。另外,提出一个MCVE比应该更麻烦,因为看起来独立的函数可能不是。

尽可能多地将模块级代码填入main函数,并从if __name__ == '__main__':的主体中调用它(而不是其他任何东西)(因为即使那个实际上是在模块级别。)

考虑这样的事情:

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()

这些函数仍然通过它们传递的共享字典连接,但只有显式传递该字典的函数才能更改其内容。

答案 1 :(得分:0)

您的代码无效,因为您没有将stelnet传递给您的函数。