背景
我正在尝试为Raspberry Pi Stackexchange.上的问题编写解决方案。该人正在创建一个自助服务终端,并希望Midori(Raspbian Wheezy上的浏览器)在网络断开然后重新连接时重新加载。为了检查互联网连接,我决定让该程序尝试连接到Google。我使用变量up
,根据连接的状态分配给0(向下)或1(向上)。
我在向线程发送新的重新分配的up
变量时遇到问题。我从我的线程中获得了永久性的“Up is not reassigned”。
代码
up = -1 #Undefined state
test_net = ("www.google.com", 80)
#Import modules
import subprocess as sub
from time import sleep
import socket
import threading
def reloader(up):
print "A new thread is born." #Debug
while True:
if up == 1:
print "The system is up."
elif up == 0:
print "The system is down."
#sub.call(["midori", "-e", "Reload"])
else:
print "Up has not been reassigned."
sleep(5)
#Check if internet is up
threading.Thread(target = reloader, args = (up,)).start()
while True:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect(test_net)
up = 1
print up
except socket.error:
up = 0
print up
s.close()
sleep(10)
答案 0 :(得分:0)
这里的问题是对如何将参数传递给Python中的函数的误解。当你做
时,你期待着up = 1
或
up = 0
在全局范围内,reloader
函数将在其范围内看到up
变量中反映的更改,因为您将该变量作为参数传递给它。这不是它的工作原理。
您将reloader
函数传递给全局作用域中的up
名称作为参数的对象,然后您分配了一个新对象< / em>到全局范围内的该名称。 up
范围中的reloader
名称仍指向最初传递的对象,即整数-1
。
您可以在此处选择两种方法来获取所需的行为:
<强> 1。通过闭包而不是通过传递参数来共享状态
如果您对代码进行以下两项更改,则一切都会按照您的预期进行:
def reloader(up):
替换为def reloader():
threading.Thread(target = reloader, args = (up,)).start()
替换为threading.Thread(target = reloader, args = ()).start()
为什么呢?因为Python中的函数可以引用其父作用域中定义的所有名称。当您要求reloader
接受名为up
的参数时,您在up
中定义了一个局部变量reloader
,从而将同名变量隐藏在父作用域中。但是如果您只是删除了参数,那么当您尝试从up
中读取reloader
时,Python会看到没有这样的 local 变量并查看父作用域代替。因此,在up
内可以看到外部范围中reloader
的分配。
或强> 的
<强> 2。通过改变作为参数传递的对象来共享状态
不是将整数-1
作为参数传递给reloader
,而是传递包含-1
的可变对象,然后修改当前将新对象分配给{的对象。 {1}}。你会不时看到的一个黑客(通常是当人们希望对函数中的变量进行的赋值从外部作用域中可见时;异常地,你会遇到相反的问题,这只能在使用线程时发生)是将变量包装在列表中(即,将up
初始化为up
而不是[-1]
,然后始终引用-1
而不是up[0]
除外当它作为函数的参数传递时,这将适用于这种情况。
或者,一个不太丑陋的基于可变对象的解决方案可能是使用Value
class from the multiprocessing module,它正是为了这个目的而存在的。 (请注意,通过闭包来共享进程之间的状态是不可能的,因为在使用多处理时,使用可变对象来共享状态更为必要。也许这就是多处理模块具有的原因。 up
类,但线程模块没有 - 据我所知 - 有类似的东西。)
使用这种方法的工作代码如下所示:
Value