无法引用在函数外声明的某个特定变量

4 投票
1 回答
1160 浏览
提问于 2025-04-18 15:21

我正在尝试用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呢?我使用的是Python 2.7.6和来自Anaconda 1.9.1包的Matplotlib 1.3.1。

1 个回答

4

这个问题出现的原因是你试图重新赋值全局变量t

你对变量g, vel, ang, y0, dt, time只是访问它们(没有重新赋值),所以Python会先在局部查找这些变量,如果找不到再去全局查找。然而,当你用t += dt这一行代码重新赋值t时,其实是在告诉Python要创建一个局部变量t并给它赋值。因此,原来的全局变量t就无法访问了,因为你已经告诉Pythont是局部的。当你在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]

撰写回答