python - шаблон строки - разные значения для одного и того же ключа - PullRequest
1 голос
/ 28 ноября 2011

У меня есть шаблон SQL, где мне нужно заменить случайные UUID.

from string import Template
import uuid

def gen_uuid():
    return str(uuid.uuid4())

s = """
insert ... values('foo', ${uuid});
insert ... values('bar', ${uuid});
insert ... values('baz', ${uuid});
"""

mappings = {
    "uuid": gen_uuid  # how??
}

template = Template(s)
print template.safe_substitute(mappings)

Как связать разные значения UUID с одним и тем же ключом шаблона?

Обновление

Хорошо ... Я закончил переопределять getitem. Не идеально, но работает ..

class UUIDDict(dict):
    def __init__(self, *args, **kwargs):
        self.update(*args, **kwargs)

    def __getitem__(self, key):
        if key == 'uuid':
            return str(uuid.uuid4())

        return super(UUIDDict, self).__getitem__(key)

Ответы [ 3 ]

1 голос
/ 28 ноября 2011

string.Template () не предназначен для замены нескольких значений одним ключом. Другие системы шаблонов могут поддерживать это, но string.Template должен быть очень простым.

При этом не сложно сделать то, что вы хотите в цикле, а затем объединить результаты, используя str.join :

>>> from string import Template
>>> import uuid
>>> def gen_uuid():
        return str(uuid.uuid4())

>>> s = Template("insert ... values('$name', ${uuid});")
>>> t = [s.substitute(name=name, uuid=gen_uuid()) for name in ('foo', 'bar', 'baz')]
>>> print '\n'.join(t)
insert ... values('foo', ee1b1b21-c022-4de5-8f7c-0a4d0554ae49);
insert ... values('bar', 0b96872f-ab0e-48cf-a025-f997f7976a0e);
insert ... values('baz', 25165bb6-8b7b-4c87-9ce1-ca274fc51bc8);

Как предполагает OP, можно создать собственный словарь, который будет строить новый uuid () при каждом поиске:

>>> class UUID_Dict(dict):
        def __missing__(self, key):
            if key == 'uuid':
                 return str(uuid.uuid4())
            raise KeyError(key)


>>> s = Template("""
insert ... values('foo', ${uuid});
insert ... values('bar', ${uuid});
insert ... values('baz', ${uuid});
""")
>>> print s.substitute(UUID_Dict())

insert ... values('foo', eabc65b6-1294-43b7-9506-61a4e324e0f2);
insert ... values('bar', 93e4f0b7-7fa1-4e88-9696-e361da31358f);
insert ... values('baz', 503d34a3-17a4-4513-8ce0-b5efb70c94cc);
1 голос
/ 28 ноября 2011

Вот решение, в котором используется «словарь по умолчанию» (defaultdict):

from collections import defaultdict
from string import Template
import uuid

def gen_uuid():
    return str(uuid.uuid4())

s = """
insert ... values('foo', ${foo_uuid});
insert ... values('bar', ${bar_uuid});
insert ... values('baz', ${baz_uuid});
"""

mappings = defaultdict(gen_uid)
template = Template(s)
print template.safe_substitute(mappings)

Результат будет примерно таким:

insert ... values('foo', aedd5fb6-40da-45bd-bcf7-c7390ef8f13e);
insert ... values('bar', c7ea3090-2a0f-49aa-ace9-c5ef67b7888b);
insert ... values('baz', bf2d86b7-c13e-4498-8d4a-27e76c2e72ab);

A defaultdict создается с одним аргументом, заводской функцией.Каждый раз, когда неизвестный ключ ищется в словаре по умолчанию, он вызывает эту функцию, связывает это значение с ключом, а затем возвращает ключ.Таким образом, каждой переменной будет назначен новый UUID.

Основным недостатком является то, что вы не можете использовать одну переменную ${uuid};Вы должны будете определить новую переменную для каждого uuid.Опять же, это может быть лучше в долгосрочной перспективе: таким образом, вы можете получить доступ к определенному UUID для каждого значения в шаблоне, если оно вам понадобится снова.Вы даже можете получить доступ к UUID напрямую из кода Python после замены шаблона:

>>> for k, v in mappings.items():
...     print k, v
... 
baz_uuid bf2d86b7-c13e-4498-8d4a-27e76c2e72ab
bar_uuid c7ea3090-2a0f-49aa-ace9-c5ef67b7888b
foo_uuid aedd5fb6-40da-45bd-bcf7-c7390ef8f13e
1 голос
/ 28 ноября 2011

Первая проблема: вы не передаете результат gen_uuid в шаблон, а ссылаетесь на сам объект функции.Это должно быть:

mappings = {
    "uuid": gen_uuid()
}

При текущем подходе функция gen_uuid будет оцениваться только один раз, поэтому для каждого оператора будет использоваться одно и то же значение.

Почему бы не сделать что-то подобное вместо этого?:

names = ['foo', 'bar', 'baz']

for name in names:
    c.execute("INSERT into foo (a, b) VALUES (%s, %s)", (name, gen_uuid()))
...