【问题标题】:Unable to reference one particular variable declared outside a function无法引用在函数外部声明的特定变量
【发布时间】:2014-09-22 07:14:15
【问题描述】:

我正在尝试使用 Python 为射弹运动路径设置动画。为此,我使用了 matplotlib 的动画模块。我的完整脚本如下。

#!/usr/bin/env python

import math
import sys

import matplotlib.animation as anim
from matplotlib.pyplot import figure, show

# Gravitational acceleration in m/s/s
g = -9.81
# Starting velocity in m/s.
vel = 100
# Starting angle in radians.
ang = 45 * math.pi / 180
# Starting height in m.
y0 = 0
# Time settings.
t = 0
dt = 0.1
time = -vel**2 * math.sin(2 * ang) / g

def projectile():
    print g, vel, ang, y0, dt, time
    print t # Crashes here with UnboundLocalError: local variable 't' referenced before assignment
    t=0 # With this it works
    x = 0
    y = 0.1
    xc = []
    yc = []

    # Simulate the projectile.
    while t < time:
        x = vel * math.cos(ang) * t
        y = vel * math.sin(ang) * t + (g * t**2) / 2 + y0
        if y < 0:
            break
        t += dt
        xc.append(x)
        yc.append(y)
        yield x, y

def update_frame(sim):
    x, y = sim[0], sim[1]
    line.set_data(x, y)
    return line,

def init():
    line.set_data([], [])
    return line,

# Show the results.
fig = figure()
ax = fig.add_subplot(111)
ax.set_xlim([-5,1500])
ax.set_ylim([0,300])

# ax.plot returns a single-element tuple, hence the comma after line.
line, = ax.plot([], [], 'ro', ms=5)
ani = anim.FuncAnimation(fig=fig, func=update_frame, frames=projectile, blit=True, interval=20, init_func=init, repeat=False)
show()

我遇到的问题是我似乎能够使用除t 之外的所有变量。我这样做是为了创建一个停止条件,因此它只会运行一次,后来我发现了repeat=False,但我仍然对此感到好奇。为什么我不能在projectile 中使用t?我正在使用 Anaconda 1.9.1 包中的 Matplotlib 1.3.1 运行 Python 2.7.6。

【问题讨论】:

    标签: python python-2.7 matplotlib scope


    【解决方案1】:

    问题出现是因为您尝试重新分配全局变量t

    变量 g, vel, ang, y0, dt, time 您只能访问(无需重新分配它们),因此 python 尝试在本地和全局访问它们。但是,当您使用t += dt重新分配 t 时,您实际上是在告诉python 使用标识符t 创建一个局部变量并为其分配所需的值。因此,全局标识符 t 无法访问,因为您已经告诉 python t 是本地的,并且当您在分配之前尝试访问 t 时,会引发 UnboundLocalError

    要告诉 python 将 t 重新分配为全局变量,您只需将 global t 添加到函数的开头即可:

    t = 0
    (..)
    def projectile():
        print g, vel, ang, y0, dt, time
        global t # now t can be edited as a global variable
        print t #works
        x = 0
        y = 0.1
        xc = []
        yc = []
    
        while t < time:
            (..)
            t += dt
    

    编辑:

    正如 flebool 所指出的,只要不重新分配标识符,您实际上仍然可以更改全局变量。例如下面的代码是有效的:

    >>> l = [1,2,3]
    >>> def changelist():
    ...    l.append(4)
    ...
    >>> changelist()
    >>> l
    [1,2,3,4]
    

    【讨论】:

    • 这是非常具有误导性的。问题不在于更改变量t,而在于将标识符t 分配给其他东西。例如,如果您将t=numpy.array([0,3]) 定义在顶部,然后您尝试在projectile() 中更改t[1] 而不声明它global,它仍然可以工作。
    • @flebool 谢谢,你是对的。我已经相应地更新了我的答案。
    • 干得好。我会说链接中的引用仍然具有误导性,我会摆脱它,但这只是我的意见
    • @flebool 不,我同意 - 引用看起来很棒,所以我尽力保留它。 ;)
    猜你喜欢
    • 2015-11-09
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 2021-05-22
    • 1970-01-01
    • 2021-04-11
    相关资源
    最近更新 更多