Mako 模板中重复使用的变量会导致“UnboundLocalError:局部变量‘xyz’”赋值前引用”

发布于 2025-01-05 10:12:17 字数 1965 浏览 1 评论 0原文

我有这个“有趣”的问题。我知道这个错误消息在很多地方都可以找到,但我找不到与 Mako 明确相关的错误消息。

在 Mako 模板中,我有 (snippet):

<%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()},我不没有收到任何错误,并且 <%def> 中的 ${label} 的值是我想要的值,而不是 for 循环中的值。如果我对 <%def> 中的变量有同样的错误,那么很明显不重用相同的变量名,但在这种情况下,我对发生这种事情感到非常困惑。

谁能告诉我除了在 for 循环中重命名变量 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()} 中的错误,因为它没有给我任何搜索 ${label} 时出错。

如果我将 for 循环更改为:

% for (label_name, field_name) in some_list:

我不会收到错误。

如果我不更改 for 循环,但更改为:

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

即使

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

在我的 <%def> 中使用 ${label} ,我也不会收到错误。

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

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

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

希望这能让问题更清楚。

I have this "funny" issue. I know this error message is found in a lot of places, but I couldn't find one explicitely related to Mako.

In a Mako template, I have (snippet):

<%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>

and I would get the error:

UnboundLocalError: local variable 'label' referenced before assignment

The weird part is that if I just don't use the second ${label.capitalize()}, I don't get any error, and the value of ${label} in the <%def> is the one I want, not the one from the for-loop. If I had the same error with the variable in <%def>, it may be clear to not reuse the same variable name, but in this case, I'm quite puzzled that such thing happens.

Can anyone tell me how I can avoid this beside renaming the variable label in the for-loop? If I rename the for-loop variable name, the problem disappear. I am transferring from another templating system that did not have this sort of error, so this similar scenario happens quite often.

Thanks for any pointers,

D.

EDIT for clarity:

I am calling my template using:

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

My question is: Why the fact that I have the % for (label, field_name)-loop, the variable label is giving me and error in ${label.capitalize()}, where as it does not give me any error for Search for ${label}.

If I change my for-loop for:

% for (label_name, field_name) in some_list:

I get no error.

If I don't change the for-loop, but I change:

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

to

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

I get no error even if ${label} is used in my <%def>.

Also, to add info for the template usage, I added the <%inherit> and here is how the other.mako is defined (snippet):

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

So I don't need to pass any value to my_method().

Hope this make the question more clear.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蓝眼泪 2025-01-12 10:12:17

最好将 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 模板中工作。

It is best to think of a Mako template as being similar to a piece of python code. Thus, as in python, you will have issues if you expect a variable to take on both local scope and global (or nested scope).

Thinking of a template as a function that takes variables, and defines within it various other functions including a body() function, then your problem is effectively similar to the following

>>> 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

You shouldn't expect this to work in Python, and similarly, you shouldn't expect it to work in a Mako template.

望她远 2025-01-12 10:12:17

我认为您需要在 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

I think you need to include label in my_method arguments.

<%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>

Follow-up

As far as I can tell you'll have to change the loop variable name. It may be a nuance of Mako or Python namespaces that I don't completely understand, but this appears to be the safest option. In my opinion reusing variable names like this is bad form anyway and leads to unexpected behaviour, is error prone, etc.

It appears from my evaluation that this may be a feature of mako in the way it handles namespaces. Consider the following examples. The first raises the same UnboundLocalError exception. The second is identical in structure and does not raise exception.

Example 1 raises 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))

Outputs:

    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

Example 2 evaluates successfully:

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))

Output:

    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

To demonstrate that Mako is not evaluating the template as you might expect, here is a similarly structured pure Python example that evaluates just fine.

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)

Outputs:

    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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文