Tuesday, December 10, 2013

mako二三事 -- 函数三

 八 、mako中def的一些特定参数

前面我们就提到过,mako的函数是一种增强性的函数,除了可以往函数中传递参数之外,还可以为该函数定义一些额外的参数。例如,

1. cache

我们可以为一个mako函数单独定义一个cache,设定cache过期时间。

<%def name="somedef()"cached="True" cache_timeout="60">
<%
    import time
%>
time = ${time.time()}
</%def>

${somedef()}


2. buffer

在写mako模板的时候,要牢牢记住的一点是:不论是否在函数内,模板内容都是直接写到输出的。也就是说,所有函数,不做特殊处理的话,它的返回值都是空字符串。这在平常可能不会注意到,但如果遇到类似如下的用法的时候就会有问题:

${" results " + somedef() + " more results"}

 上面的${}中是一个表达式,这在mako中是合法的。如果这里的somedef函数输出somedef's results字符串,那我们会得到如下结果:

somedef's results results more results

这是因为,在函数somedef运行时,其运行结果就已经写到 输出中了。然后,somedef返回'',最后" results " + ''+ " more results"结果求值之后输出,这就是上面这个结果。

有的时候,我们希望将somedef这个函数的运行结果先暂存起来,经过一定处理之后,再写入到输出中。因此,mako引入了buffered这个参数来解决这个问题。(注意,这里的buffer跟cache是完全不同的概念。)

<%def name="somedef()" buffered="True">
    somedef's results
</%def> 
${" results " + somedef() + " more results"} 

输出:

results somedef's results more results

我们来看一下这种方式生成的py文件:

def render_somedef(context):
    __M_caller = context.caller_stack._push_frame()
    try:
        context._push_buffer()
        __M_writer = context.writer()
        # SOURCE LINE 1
        __M_writer(u"\n    somedef's results\n")
    finally:
        __M_buf = context._pop_buffer()
        context.caller_stack._pop_frame()
    return __M_buf.getvalue() 

可以看到,该函数创建了一个暂存区,然后将其中的数据作为了return的返回值。

如果只是希望临时性的获取一下mako函数运行结果进行一些测试,而不是直接buffer起来,mako也提供了一个叫capture的方法。通过capture,可以截获一个普通的mako的运行结果,转成返回值。例如

<%def name="somedef(age=8, weight=100)">
    somedef's results ${age} ${weight}
</%def> 
${" results " + capture(somedef, 10, weight=120) + " more results"}  

输出:

results somedef's results 10 120 more results


3. filter

mako本身是一个模板,因此也就带了一系列的filter,通过这些filter的作用,可以很灵活的将数据转换成特定的格式。在mako定义函数时,也引入了filter概念。注意,因为需要filter,也就必须要截获函数的运行结果,因此,filter默认也带有buffer功能。例如

<%def name="foo()" filter="h, trim">
    this is bold
</%def>
${foo()}

输出为:

<b>this is bold</b> 

这里需要注意的是,filter是def的参数,不是foo的参数。这个filter仅仅作用于该函数的生成结果。


4. Decorating

除了filter之外,mako还提供了Decorating的功能,不过因为模板的特殊性,mako函数的Decorating跟python中的还是有很多区别的。例如

<%!
    def bar(fn):
        def decorate(context, *args, **kw):
            context.write("BAR")
            fn(*args, **kw)
            context.write("BAR")
            return 'mytest'
        return decorate
%>

<%def name="foo()" decorator="bar">
    this is foo
</%def>

${"abc"+foo()}

输出:

BAR
    this is foo
BARabcmytest 

注意:
a. mako里面所有的函数第一个参数都是context,所以 decorate函数的第一个参数必须是context。
b. 如果希望在decorate输出内容,可以使用context.write函数,这也是mako推荐的方式。
c. 这里的decorate应该返回'',像上示例中返回其他内容的话会加到输出结果中去,但要注意加入的位置。
d. 如果用template.get_def('somedef').render()之类的方法来渲染模板,则只会使用context.write的结果,抛弃return返回值。
>>> print template.get_def("foo").render()
BAR
    this is foo
BAR 

另外,capture函数也可以用于构建decorate,例如:
<%!
    def bar(fn):
        def decorate(context, *args, **kw):
            return "BAR" + runtime.capture(context, fn, *args, **kw) + "BAR"
        return decorate
%>

<%def name="foo()" decorator="bar">
    this is foo
</%def>

${foo()} 

No comments:

Post a Comment