为什么 class 中的 global 在 class 位于函数内时表现不同?

why does global in a class behave differently when the class is within a function?

我正在尝试使用 ginput 来记录地图上的点击,并想使用 matplotlib 小部件添加操作按钮。在下面的代码中,我可以通过将 action 的值声明为 global 将其传回主代码。如果我点击地图,action=0,如果我点击按钮,action=1,根据需要。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button

class Index:
    def test(self, event):
        global action
        action=1

# fake data
x=np.arange(30)
y=x**2
fig,ax=plt.subplots()
ax.plot(x,y)
callback = Index()
buttonname=['test']
colors=['white']
idx=[0.2]
bax,buttons={},{}

# set up list of buttons.
for i,col,button in zip(idx,colors,buttonname):
    bax[button] = plt.axes([0.92, i, 0.07, 0.07])
    buttons[button] = Button(bax[button],button,color=col,hovercolor='green')
    buttons[button].on_clicked(getattr(callback,button))

# register click on plot
while True:
     pts=plt.ginput(1)
     plt.pause(0.5)
     print("action is ",action)
     action=0 # reset 

但我的困惑是,如果我采用完全相同的代码并将其放在 def 块中,则 action 的值不再传回,action 始终为零。

def subtest():
    class Index:
        def test(self, event):
            global action
            action=1

    # fake data
    action=0
    x=np.arange(30)
    y=x**2
    fig,ax=plt.subplots()
    ax.plot(x,y)
    callback = Index()
    buttonname=['test']
    colors=['white']
    idx=[0.2]
    bax,buttons={},{}

    # set up list of buttons.
    for i,col,button in zip(idx,colors,buttonname):
        bax[button] = plt.axes([0.92, i, 0.07, 0.07])
        buttons[button] = Button(bax[button],button,color=col,hovercolor='green')
        buttons[button].on_clicked(getattr(callback,button))

    # register click on plot
    while True:
         pts=plt.ginput(1)
         plt.pause(0.5)
         print("action is ",action)
         action=0 # reset

res=subtest()

我很困惑为什么会这样。我尝试将 class 定义移到主代码中,但这没有帮助。我对任何类型的解决方案都很满意(例如,通过参数传递操作,我不明白如何使用小部件),因为我认为 global 的使用经常不受欢迎。但基于 global 的解决方案也很好。

action inside subtest 是局部的子测试,而 action inside Index.test 是全局的。在 subtest 中声明 action 全局,或者在 Index.test 中使用 nonlocal

(我怀疑没有全局变量可能会有更好的解决方案,但由于我不熟悉 GUI 工具包,所以我会把它留给其他人。)

您想查看这篇文章 on closures and this other article on closures。我认为您需要在 class 和函数之外声明操作,然后在 class.

中使用全局引用它

我认为您不需要使用 class - 您可以通过围绕函数传递变量、布尔值、字典来实现相同的目的。

您可以使用“on_click”事件来执行您想要的操作,请参阅此小部件教程widget tutorial and this on matplotlib event handling

这是一段使用“全局”影响状态变化的示例代码。最好传递一个变量或使用事件来触发您希望状态生效的任何内容。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button

state = False

def change_state(btn_action):
    #do something
    btn_action= not(btn_action)
    return btn_action

def grid(val):
    global state
    #do something
    state =change_state(state)
    print(state)

    ax.grid()
    fig.canvas.draw()

    #reset value
    state =change_state(state)
    print(f'resetting state to {state}')

# fake data
x=np.arange(30)
y=x**2

#create fig, ax
fig =plt.figure()
ax= fig.subplots()
p, = ax.plot(x,y)

# set up list of buttons.

buttonname=['test']
colors=['white']
idx=[0.2]
bax,buttons={},{}

for i,col,button in zip(idx,colors,buttonname):
    bax[button] = plt.axes([0.92, i, 0.07, 0.07])
    buttons[button] = Button(bax[button],button,color=col,hovercolor='green')
    buttons[button].on_clicked(grid)

#show plot
fig.canvas.draw()
plt.show()