python - 字符串模板 - 相同键不同值
我有一个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
第一个问题是,你没有把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()))
1
这里有一个使用“默认字典”(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);
一个defaultdict
是通过一个参数,也就是一个工厂函数来创建的。每当你在这个默认字典中查找一个未知的键时,它会调用这个函数,把生成的值绑定到这个键上,然后返回这个键。因此,它会给每个变量分配一个新的UUID。
主要的缺点是你不能使用单一的变量${uuid}
; 你需要为每个UUID定义一个新的变量。不过,从长远来看,这可能是更好的做法:这样一来,如果你需要再次访问模板中的特定UUID,你可以轻松找到。你甚至可以在做完模板替换后,直接从Python代码中访问这些UUID:
>>> 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
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);
正如提问者所建议的,可以创建一个自定义字典,每次查找时都会生成一个新的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);