Python 3. 'exec' 函数的混淆问题
Python 3. Confusing issue with 'exec' function
我一直在使用 tkinter for gui 开发我的贪吃蛇游戏。问题出在其中一个函数中。
该函数应该分别使用 Canvas().create_rectangle 和 Canvas().create_oval 绘制 body 个碎片和水果。因此,我没有为每种情况编写单独的代码,而是决定编写一次并使用 'shape' 参数修改它,该参数可以是 'rectangle' 或 'oval'。函数还必须 return 绘制元素的 id 以用于其自身目的。最初那部分代码看起来像这样:
exec(
"""
segment_id = self.grid.create_{}(coords,
[coord + self.pxSize for coord in coords],
fill=color, tag=tag, width=0)
""".format(shape))
return segment_id
我得到的不是普通 NameError: name 'segment_id' is not defined
,而是 NameError: name 'self' is not defined
。
谷歌搜索后我只找到了这个:
ldict = {}
exec('var = something', globals(), ldict)
var = ldict['var']
其中解决了 NameError: name 'segment_id' is not defined
,但没有解决另一个问题。因此,使用科学戳方法,我通过将 locals() 传递给它的 'globals' 参数来修复它。它有效,现在我更加困惑了。
代码如下:
class Game(Tk):
def __init__(self):
...
# ...
def drawSegment(self, position, color, tag, shape, id_=None):
coords = self.field.from_1D_to_2D(position)
coords = [i * self.pxSize for i in coords]
# id > 1, otherwise deletes background
if id_ and id_ > 1:
self.grid.delete(id_)
# ???
ldict = {}
exec(
"""
segment_id = self.grid.create_{}(coords,
[coord + self.pxSize for coord in coords],
fill=color, tag=tag, width=0)
""".format(shape), locals(), ldict)
segment_id = ldict['segment_id']
return segment_id
# ...
我需要的是关于为什么它有效以及发生了什么的答案甚至。
像这样使用 exec 是不必要的,而且当您 运行 进入时,非常混乱;所以我有两个不同的答案给你。
怎么回事?
当你将globals=globals(), locals=ldict
传递给exec
时,它会在一个只能看到globals
和ldict
的范围内执行代码;所以特别是,它不会看到 drawSegment
方法局部范围内的任何局部变量。由于 self
仅在此局部范围内定义,为了从 exec
调用内部引用 self
,您需要传入 locals()
,而不仅仅是 globals()
.
你应该怎么做?
无需根据 shape
的值动态执行整个代码块,您只需根据 shape
:
动态查找所需的 create_* 方法即可
creator = getattr(self.grid, 'create_{}'.format(shape))
segment_id = creator(coords, [coord + self.pxSize for coord in coords],
fill=color, tag=tag, width=0)
如果您知道形状只有两种可能性,根据个人品味,您可能会更加明显:
creator = self.grid.create_oval if shape == 'oval' else self.grid.create_rectangle
我一直在使用 tkinter for gui 开发我的贪吃蛇游戏。问题出在其中一个函数中。
该函数应该分别使用 Canvas().create_rectangle 和 Canvas().create_oval 绘制 body 个碎片和水果。因此,我没有为每种情况编写单独的代码,而是决定编写一次并使用 'shape' 参数修改它,该参数可以是 'rectangle' 或 'oval'。函数还必须 return 绘制元素的 id 以用于其自身目的。最初那部分代码看起来像这样:
exec(
"""
segment_id = self.grid.create_{}(coords,
[coord + self.pxSize for coord in coords],
fill=color, tag=tag, width=0)
""".format(shape))
return segment_id
我得到的不是普通 NameError: name 'segment_id' is not defined
,而是 NameError: name 'self' is not defined
。
谷歌搜索后我只找到了这个:
ldict = {}
exec('var = something', globals(), ldict)
var = ldict['var']
其中解决了 NameError: name 'segment_id' is not defined
,但没有解决另一个问题。因此,使用科学戳方法,我通过将 locals() 传递给它的 'globals' 参数来修复它。它有效,现在我更加困惑了。
代码如下:
class Game(Tk):
def __init__(self):
...
# ...
def drawSegment(self, position, color, tag, shape, id_=None):
coords = self.field.from_1D_to_2D(position)
coords = [i * self.pxSize for i in coords]
# id > 1, otherwise deletes background
if id_ and id_ > 1:
self.grid.delete(id_)
# ???
ldict = {}
exec(
"""
segment_id = self.grid.create_{}(coords,
[coord + self.pxSize for coord in coords],
fill=color, tag=tag, width=0)
""".format(shape), locals(), ldict)
segment_id = ldict['segment_id']
return segment_id
# ...
我需要的是关于为什么它有效以及发生了什么的答案甚至。
像这样使用 exec 是不必要的,而且当您 运行 进入时,非常混乱;所以我有两个不同的答案给你。
怎么回事?
当你将globals=globals(), locals=ldict
传递给exec
时,它会在一个只能看到globals
和ldict
的范围内执行代码;所以特别是,它不会看到 drawSegment
方法局部范围内的任何局部变量。由于 self
仅在此局部范围内定义,为了从 exec
调用内部引用 self
,您需要传入 locals()
,而不仅仅是 globals()
.
你应该怎么做?
无需根据 shape
的值动态执行整个代码块,您只需根据 shape
:
creator = getattr(self.grid, 'create_{}'.format(shape))
segment_id = creator(coords, [coord + self.pxSize for coord in coords],
fill=color, tag=tag, width=0)
如果您知道形状只有两种可能性,根据个人品味,您可能会更加明显:
creator = self.grid.create_oval if shape == 'oval' else self.grid.create_rectangle