Python 夹层实用程序中的 lambda

Python lambda in mezzaninie utils

我无法理解这个 lambda 函数(渲染)。它来自夹层实用程序:

def send_mail_template(subject, template, addr_from, addr_to, context=None,
                   attachments=None, fail_silently=None, addr_bcc=None,
                   headers=None):
    """
    Send email rendering text and html versions for the specified
    template name using the context dictionary passed in.

    EDITED FOR SIMPLICITY
    """

    context.update(context_settings())

    # Loads a template passing in vars as context.
    render = lambda type: loader.get_template("%s.%s" %
                      (template, type)).render(Context(context))

    # Create and send email.
    msg = EmailMultiAlternatives(subject, render("txt"),
                             addr_from, addr_to, addr_bcc,
                             headers=headers)
    msg.attach_alternative(render("html"), "text/html")
    msg.send(fail_silently=fail_silently)

我正在尝试将模板列表传递到我自己的函数版本中。所以参数 "template" 是模板路径的字符串,成为路径字符串的列表(模板)。然后我需要遍历列表并应用 lambda 中发生的任何事情,然后在其上调用 .render(Context(context))。

这是有史以来最丑陋的代码,但它显示了我需要的最终结果:

render = lambda type: loader.get_template("%s.%s" %
                  (templates[0], type))

render2 = lambda type: loader.get_template("%s.%s" %
                  (templates[1], type))

# Create and send email.
msg = EmailMultiAlternatives(subject,(render("txt")+render2('txt')).render(Context(context)),
                         addr_from, addr_to, addr_bcc,
                         headers=headers)
msg.attach_alternative((render("html")+render2('html').render(Context(context)), "text/html")
msg.send(fail_silently=fail_silently)

以上确实有效,但显然令人厌恶,而且列表的长度未知。

拜托!任何人都可以解构 lambda 函数吗?

Answer with help from Arthur Tacca

我正在尝试通过将模板堆叠在一起来构建电子邮件正文,因此需要传入将呈现为 txt 和 html 正文的模板列表。这是有效的方法:

def render_templates(types,templates,context):
    template_body = ''
    for template in templates:
        template_body += loader.get_template("%s.%s" % (template, types)).render(Context(context))
    return template_body

def send_mail_template(subject, templates, addr_from, addr_to, context=None,
                   attachments=None, fail_silently=None, addr_bcc=None,
                   headers=None):
    """
    ...
    """
    context.update(context_settings())

    msg = EmailMultiAlternatives(subject, render_templates("txt",templates,context),
                             addr_from, to, [],
                             headers)
    msg.attach_alternative(render_templates("html",templates,context), "text/html")

    msg.send(fail_silently=fail_silently)

我不清楚你为什么要使用 function/lambda 而不仅仅是(对于第一个示例):

rendered = loader.get_template("%s.%s" % (template, "txt")).render(Context(context))

我想这种简化之所以有效,是因为您简化了执行此操作的上下文;也许在实际代码中,您需要将渲染函数传递到某个地方以便稍后调用。如果没有,您可以完全摆脱 lambda。

这是一个小问题,但它可能会让事情更清楚一点:

之间绝对没有区别
render = lambda type: loader.get_template("%s.%s" %
                  (templates[0], type))
render2 = lambda type: loader.get_template("%s.%s" %
                  (templates[1], type))

和:

def render(type):
    return loader.get_template("%s.%s" % (templates[0], type))
def render2(type):
    return loader.get_template("%s.%s" % (templates[1], type))

(好吧,一个细微的差别是,如果有异常,你会得到一个更清晰的堆栈跟踪。)事实上,第二种形式是更好的风格(也就是 "more Pythonic");使用 lambda 的唯一原因是函数太短以至于它甚至没有将其分配给变量就被传递给另一个函数。

在您的情况下,这意味着您可以根据需要使用迭代:

def render_all(types):
    result_list = []
    for type, template in zip(types, templates):
        result_list.append(loader.get_template("%s.%s" % (template, type)))
    return "".join(result_list)

列表理解/生成器已经成熟:

def render_all(types):
    return "".join(loader.get_template("%s.%s" % (template, type))
                   for type, template in zip(types, templates))