Mako模板中重用的变量导致“UnboundLocalError:在赋值之前引用的局部变量'xyz'”

时间:2012-02-14 05:18:55

标签: python mako

我有这个“有趣”的问题。我知道很多地方都有这个错误信息,但我找不到与Mako有明显关系的信息。

在Mako模板中,我有(摘录):

<%inherit file="other.mako"/>
<%def name="my_method()">Search for ${label}</%def>
[...]
<h2>${label.capitalize()} found: ${len(l)}</h2>
...
<ol>
% for (label, field_name) in some_list:
    <li>${label}: ${field_name}</li>
% endfor
</ol>

我会得到错误:

UnboundLocalError: local variable 'label' referenced before assignment

奇怪的是,如果我不使用第二个${label.capitalize()},我不会收到任何错误,${label}中的<%def>值就是那个我想要的,不是来自for-loop的那个。如果我对<%def>中的变量有同样的错误,可能很清楚不重用相同的变量名,但在这种情况下,我很困惑这种事情发生了。

有谁可以告诉我除了在for-loop中重命名变量 label 之外我怎么能避免这种情况?如果我重命名for循环变量名称,问题就会消失。我从另一个没有这种错误的模板系统转移,所以这种类似的情况经常发生。

感谢您的任何指示,

d

为清晰起见编辑:

我使用以下方式调用我的模板:

renderers.render_to_response(my_mako_tmpl,
         {'label': 'value', 'somelist':[a,b,c], 'l':[]},
         request=request)

我的问题是:为什么我有% for (label, field_name) - 循环,变量label给我和${label.capitalize()}中的错误,其中as它没有给我Search for ${label}的任何错误。

如果我改变了我的for循环:

% for (label_name, field_name) in some_list:

我没有错误。

如果我不改变for循环,但我改变了:

<h2>${label.capitalize()} found: ${len(l)}</h2>

<h2>Items found: ${len(l)}</h2>

即使我的${label}使用了<%def>,我也没有收到任何错误。

另外,要添加模板使用情况的信息,我添加了<%inherit>,以下是other.mako的定义方式(摘要):

<%def name="my_method()">Default value</%def>
Value of my_method() = ${self.my_method()}

所以我不需要将任何值传递给my_method()

希望这会使问题更加明确。

2 个答案:

答案 0 :(得分:2)

最好将Mako模板视为类似于一段python代码。因此,就像在python中一样,如果您希望变量同时采用局部范围和全局(或嵌套范围),则会出现问题。

将模板视为一个接受变量的函数,并在其中定义各种其他函数,包括body()函数,那么您的问题实际上类似于以下

>>> def f(x):
...     def body():
...         print x
...         for x in range(5):
...             print x
...     body()
... 
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in f
  File "<stdin>", line 3, in body
UnboundLocalError: local variable 'x' referenced before assignment

您不应期望这在Python中有效,同样,您不应期望它在Mako模板中工作。

答案 1 :(得分:0)

我认为你需要在my_method参数中包含标签。

<%def name="my_method(label)">Search for ${label}</%def>
[...]
<h2>${label.capitalize()} found: ${len(l)}</h2>
...
<ol>
% for (label, field_name) in some_list:
    <li>${label}: ${field_name}</li>
% endfor
</ol>

<强>后续

据我所知,你必须改变循环变量名。它可能是我不完全理解的Mako或Python命名空间的细微差别,但这似乎是最安全的选择。在我看来,重复使用这样的变量名称无论如何都是糟糕的形式,导致意外行为,容易出错等。

从我的评估看来,这可能是mako在处理命名空间方面的一个特性。请考虑以下示例。第一个引发相同的UnboundLocalError异常。第二个结构相同,不会引起异常。

示例1引发UnboundLocalError:

from mako.template import Template

src = """
Text in "label": ${label}
Elements of "some_list": ${some_list}

Labels and names in "some_list"
% for (label,name) in some_list:
    label: name -> ${label}: ${name}
% endfor

${label.capitalize()}
"""

my_template = Template(src)

s = [('foo', 'bar'), ('spam', 'eggs')]
data = {'label': 'bogus', 'some_list':s, 'l':[1, 2, 3, 4]}

print(my_template.render(**data))

输出:

    Traceback (most recent call last):

      ...

      File "C:\Python26\ArcGIS10.0\Lib\site-packages\mako-0.6.2-py2.6.egg\mako\runtime.py", line 704, in _exec_template
        callable_(context, *args, **kwargs)
      File "memory:0xb5b6b0", line 22, in render_body
    UnboundLocalError: local variable 'label' referenced before assignment

示例2成功评估:

from mako.template import Template

src = """
Text in "label": ${label}
Elements of "some_list": ${some_list}

Labels and names in "some_list"
% for (loop_label,name) in some_list:
    label: name -> ${loop_label}: ${name}
% endfor

${label.capitalize()}
"""

my_template = Template(src)

s = [('foo', 'bar'), ('spam', 'eggs')]
data = {'label': 'bogus', 'some_list':s, 'l':[1, 2, 3, 4]}

print(my_template.render(**data))

输出:

    Text in "label": eggs
    Elements of "some_list": [('foo', 'bar'), ('spam', 'eggs')]

    Labels and names in "some_list"
        label: name -> foo: bar
        label: name -> spam: eggs

    Eggs

为了证明Mako没有按照您的预期评估模板,这里是一个类似结构的纯Python示例,评估得很好。

src = """
print('Text in "label": %s' % label)
print('Elements of "some_list": %s' % some_list)

print('')
print('Labels and names in "some_list"')
for (label,name) in some_list:
    print('    label: name -> %s:%s' % (label, name))

print('')
print('Caps "label": %s' % label.capitalize())
"""

code = compile(src, 'None', 'exec')

s = [('foo', 'bar'), ('spam', 'eggs')]
data = {'label': 'bogus', 'some_list':s, 'l':[1, 2, 3, 4]}

eval(code, {}, data)

输出:

    Text in "label": bogus
    Elements of "some_list": [('foo', 'bar'), ('spam', 'eggs')]

    Labels and names in "some_list"
        label: name -> foo:bar
        label: name -> spam:eggs

    Caps "label": Spam