2010-08-14 4 views
12

У меня есть два для петель, оба одинаково достойны. Я бы хотел, чтобы счетчик увеличивался во время каждой внутренней итерации.Как я могу сделать простой счетчик с шаблонами Jinja2?

Для примера рассмотрим этот шаблон:

from jinja2 import Template 

print Template(""" 
{% set count = 0 -%} 
{% for i in 'a', 'b', 'c' -%} 
    {% for j in 'x', 'y', 'z' -%} 
    i={{i}}, j={{j}}, count={{count}} 
    {% set count = count + 1 -%} 
    {% endfor -%} 
{% endfor -%} 
""").render() 

В случае, если это не печать count=0 через count=8? Нет, это не так.

i=a, j=x, count=0 
i=a, j=y, count=1 
i=a, j=z, count=2 
i=b, j=x, count=0 
i=b, j=y, count=1 
i=b, j=z, count=2 
i=c, j=x, count=0 
i=c, j=y, count=1 
i=c, j=z, count=2 

Что дает?

Примечание: Я не могу просто сохранить внешний loop переменного для вычисления счетчика, потому что, в моем программном обеспечении, количество внутренних итераций является переменным.

+1

Может быть, я испытываю эту ошибку, открыл 12 часов назад: http://dev.pocoo.org/projects/jinja/ticket/389 –

+0

Я сообщил, что ошибка. Пример в нем может быть более ясным. Я могу придумать способы избежать проблемы, но я все еще думаю, что это противоречит интуиции и раздражает. –

ответ

14

С различными внутренними размерами группы, это будет работать:

from jinja2 import Template 

items = [ 
    ['foo', 'bar'], 
    ['bax', 'quux', 'ketchup', 'mustard'], 
    ['bacon', 'eggs'], 
    ] 

print Template(""" 
{% set counter = 0 -%} 
{% for group in items -%} 
    {% for item in group -%} 
    item={{ item }}, count={{ counter + loop.index0 }} 
    {% endfor -%} 
    {% set counter = counter + group|length %} 
{% endfor -%} 
""").render(items=items) 

... который печатает:

item=foo, count=0 
    item=bar, count=1 

item=bax, count=2 
    item=quux, count=3 
    item=ketchup, count=4 
    item=mustard, count=5 

item=bacon, count=6 
    item=eggs, count=7 

Я предполагаю переменные, объявленные вне более чем одного уровня охвата ca не назначаться или что-то еще.

3

Это похоже на ошибку, но как насчет перемещения некоторых из этих вычислений вне шаблона?

from jinja2 import Template 

outer_items = list(enumerate("a b c".split())) 
inner_items = list(enumerate("x y z".split())) 

print Template(""" 
{% for outer, i in outer_items -%} 
    {% for inner, j in inner_items -%} 
    {% set count = outer * num_outer + inner -%} 
    i={{i}}, j={{j}}, count={{count}} 
    {% endfor -%} 
{% endfor -%} 
""").render(outer_items=outer_items, 
      inner_items=inner_items, 
      num_outer=len(outer_items)) 

Выход:

i=a, j=x, count=0 
    i=a, j=y, count=1 
    i=a, j=z, count=2 
    i=b, j=x, count=3 
    i=b, j=y, count=4 
    i=b, j=z, count=5 
    i=c, j=x, count=6 
    i=c, j=y, count=7 
    i=c, j=z, count=8 
+0

Это хорошая идея, но я упомянул, что заранее не знаю содержимого внутреннего цикла. См. Мой ответ .... –

+0

Значит, вы не знаете этого, когда находитесь в коде Python? Откуда берется внутренняя петля? Но, во всяком случае, я думаю, что ответ платного кретина решает проблему. –

3

Чтобы решить такие варианты использования, как этот, я написал небольшой фильтр среды, который учитывает вхождения ключа.

Вот-де-код (с помощью теста док) из myfilters.py:

#coding: utf-8 
from collections import defaultdict 

from jinja2 import environmentfilter 
from jinja2.utils import soft_unicode 

@environmentfilter 
def inc_filter(env, key, value=1, result='value', reset=False): 
    """ 
    Count ocurrences of key. 
    Stores the counter on Jinja's environment. 
     >>> class Env: pass 
     >>> env = Env() 
     >>> inc_filter(env, 'x') 
     1 
     >>> inc_filter(env, 'x') 
     2 
     >>> inc_filter(env, 'y') 
     1 
     >>> inc_filter(env, 'x') 
     3 
     >>> inc_filter(env, 'x', reset=True) 
     1 
     >>> inc_filter(env, 'x') 
     2 
     >>> inc_filter(env, 'x', value=0, reset=True) 
     0 
     >>> inc_filter(env, 'x', result=None) 
     >>> inc_filter(env, 'x', result=False) 
     u'' 
     >>> inc_filter(env, 'x', result='key') 
     'x' 
     >>> inc_filter(env, 'x') 
     4 
    """ 
    if not hasattr(env, 'counters'): 
     env.counters = defaultdict(int) 

    if reset: 
     env.counters[key] = 0 

    env.counters[key] += value 

    if result == 'key': 
     return key 
    elif result == 'value': 
     return env.counters[key] 
    elif result == None: 
     return None 
    else: 
     return soft_unicode('') 


## Module doctest 
if __name__ == '__main__': 
    import doctest 
    doctest.testmod()  

Настройка вашей среды регистрации наш пользовательский фильтр:

#coding: utf-8 
from jinja2 import Environment, FileSystemLoader 
from myfilters import inc_filter 

env = Environment(loader=loader=FileSystemLoader('path')) 
env.filters['inc'] = inc_filter 

t = env.get_template('yourtemplate.txt') 

items = [ 
    ['foo', 'bar'], 
    ['bax', 'quux', 'ketchup', 'mustard'], 
    ['bacon', 'eggs'], 
    ] 

res = t.render(items=items) 

И на шаблоне, использовать его как это:

{% for group in items -%} 
    {% for item in group -%} 
    item={{ item }}, count={{ 'an_identifier'|inc }} 
    {% endfor -%} 
{% endfor -%} 

... который печатает:

item=foo, count=0 
    item=bar, count=1 

item=bax, count=2 
    item=quux, count=3 
    item=ketchup, count=4 
    item=mustard, count=5 

item=bacon, count=6 
    item=eggs, count=7 
+0

Это действительно круто, спасибо! – imiric

1

Существует встроенная глобальная функция cycler(), обеспечивающая циклическое циклическое значение значения.Используя ту же идею, вы можете определить свою собственную counter() функцию следующим образом:

env=Environment(...) # create environment 
env.globals['counter']=_Counter # define global function 
env.get_template(...).render(...) # render template 

Вот класс, который реализует функцию:

class _Counter(object): 
    def __init__(self, start_value=1): 
    self.value=start_value 

    def current(self): 
    return self.value 

    def next(self): 
    v=self.value 
    self.value+=1 
    return v 

А вот как его использовать:

{% set cnt=counter(5) %} 
item #{{ cnt.next() }} 
item #{{ cnt.next() }} 
item #{{ cnt.next() }} 
item #{{ cnt.next() }} 

Он будет отображать:

item #5 
item #6 
item #7 
item #8 
0

Не нужно добавлять счетчик. Вы можете получить доступ к индексу внешнего цикла как это:

{% for i in 'a', 'b', 'c' -%} 
    {% set outerloop = loop %} 
    {% for j in 'x', 'y', 'z' -%} 
    i={{i}}, j={{j}}, count={{outerloop.index0 * loop|length + loop.index0}} 
    {% endfor -%} 
{% endfor -%} 
Смежные вопросы