• Mako 模板系统文档翻译(3) 函数定义


    翻译:木野狐 http://rchen.cnblogs.com/
    原文:http://www.makotemplates.org/docs/defs.html
    译文:

    Version: 0.1.5 Last Updated: 05/01/07 20:21:35

    Defs

    def 是一个简单的标签,用于给任意文本块或代码块定界。它在模板最终生成的 Python 代码里是一个可调用的函数。

    <%def name="hello()">
        hello world
    </%def>
    

    通常我们可以通过表达式来调用它们:

    the def:  ${hello()}
    

    如果 <%def> 没有嵌套定义在另一个 <%def> 中,则称为顶层的 def, 它可以在模板的任何地方被使用,甚至可以在定义它的位置之前。

    所有的 defs, 不管是不是顶层的,都可以访问当前的上下文名称空间,和模板的访问权限完全一样。设想下列模板在执行时被指定了一个包含 username 和 accountdata 变量的上下文:

    Hello there ${username}, how are ya.  Lets see what your account says:
    
    ${account()}
    
    <%def name="account()">
        Account for ${username}:<br/>
    
        % for row in accountdata:
            Value: ${row}<br/>
        % endfor
    </%def>
    

    usernameaccountdata 两个变量将会显示在主模板的 body 中,同样也会显示在 account() def 的 body 中。

    既然 defs 不过就是 python 函数,你自然也能够定义和传递参数了:

    ${account(accountname='john')}
    
    <%def name="account(accountname, type='regular')">
        account name: ${accountname}, type ${type}
    </%def>
    

    当你为 def 定义参数时,他们需要遵从 Python 的规定(比如,除了提供了默认值的关键字参数之外的所有参数,都需要提供)。这和上下文层次的变量不同,后者对不存在的名称会估算为 UNDEFINED 而不是出错。

    从其他文件调用 defs

    顶层的 <%defs> 被编译到模板对应的模块中,并且可以被从外部调用;包括从其他模板,或是普通的 Python 代码来调用。从其他模板中调用 <%def> 有点像使用 <%include> —— 差别在于,你是在调用模板中的一个函数,而不是整个模板。

    远程的 <%def> 调用也有点类似于 Python 中调用另一个模块中的函数的情形。需要有一个“导入”的步骤,以便从其他模板中萃取出名称,添加到你自己的模板中;然后这些名称的函数才可以被调用。

    要导入另一个模板,使用 <%namespace> 标签:

    <%namespace name="mystuff" file="mystuff.html"/>
    

    上面的标签添加了一个局部变量 "mystuff" 到当前范围中。

    然后,只要调用 mystuff 中的函数即可:

    ${mystuff.somedef(x=5,y=7)}
    

    <%namespace> 标签还支持类似 Python import 语句的一些其他的语义,包括将名称提取到局部变量空间中,或使用 * 来代表所有名称,使用 import 属性:

    <%namespace file="mystuff.html" import="foo, bar"/>
    

    这里只是对名称空间的概念做了一个简单的介绍,名称空间是 Mako 的一个核心概念,在文档中有独立的章节介绍。更多的细节和例子,见 Namespaces

    Defs 中的 Defs

    def 模型遵循 Python 中关于闭包的一些规则。在一个 <%def> 中定义另一个 <%def>,会将它定义在父 def 的外围环境(enclosing scope) 中:

    <%def name="mydef()">
        <%def name="subdef()">
            a sub def
        </%def>
    
        im the def, and the subcomopnent is ${subdef()}
    </%def>
    

    就象 Python 中一样,定义在内嵌 <%def> 之外的名称,在其内部一样存在:

    <%
        x = 12
    %>
    <%def name="outer()">
        <%
            y = 15
        %>
        <%def name="inner()">
            inner, x is ${x}, y is ${y}
        </%def>
    
        outer, x is ${x}, y is ${y}
    </%def>
    

    在 def 内的赋值语句会创建一个 def 范围内的局部变量(再一次的和 Python 自身语法吻合)。比如下面的代码就会引发错误:

    <%
        x = 10
    %>
    <%def name="somedef()">
        # error !
        somedef, x is ${x}  
        <%
            x = 27  
        %>
    </%def>
    

    ...因为对 x 的赋值将 x 定义为了 somedef 范围内的局部变量,而试图输出“外部”版本的 x 是访问不到的。

    调用方自带内容或内嵌函数的方式调用 def

    def 的另一个方面是它可以带内容的进行调用。也就是,当你调用 def 时,同时定义一个块的内容(或多个块),这些块将提供给你要调用的 def. 这种调用方式的主要目的是为了创建自定义的,可嵌套的标签,就象其他模板语言的自定义标签生成系统 —— 外部的标签控制内嵌标签的执行,并且可以和它们沟通状态信息。只有在 Mako 中,你才不需要使用任何外部的 Python 模块,你可以直接在你的模板中定义可任意内嵌的标签。

    为了达到这个目标,需要通过 <%call> 标签而不是常规的 ${} 语法来调用目标 def. 这样,目标 def 就会在其上下文中获得一个 caller 变量,其中包含一个名称空间,在此名称空间中包含了调用者的内容部分(body), 以及 <%call> 标签中定义的其他 defs. 而调用者的内容(body) 可通过 body() 方法来取得:

    <%def name="buildtable()">
        <table>
            <tr><td>
                ${caller.body()}
            </td></tr>
        </table>
    </%def>
    
    <%call expr="buildtable">
        I am the table body.
    </%call>
    

    这会产生下列输出 (空白已格式化):

    <table>
        <tr><td>
            I am the table body.
        </td></tr>
    </table>
    

    body() 可以被执行多次,或根本不执行。这意味着你可以使用带内容的 def 调用(def-call-with-content)来创建迭代器(iterators),条件语句等:

    <%def name="lister(count)">
        % for x in range(1,count):
            ${caller.body()}
        % endfor
    </%def>
    
    <%call expr="lister(3)">
        hi
    </%call>
    

    输出:

    hi
    hi
    hi
    

    一个自定义的“条件”标签:

    <%def name="conditional(expr)">
        % if expr:
            ${caller.body()}
        % endif
    </%def>
    
    <%call expr="conditional(4==4)">
        im the result
    </%call>
    

    输出:

    im the result
    

    但还有更精彩的。body() 函数还可以传递参数:

    <%def name="layoutdata(somedata)">
        <table>
        % for item in somedata:
            <tr>
            % for col in item:
                <td>${caller.body(col=col)}</td>\
            % endfor
            </tr>
        % endfor
        </table>
    </%def>
    
    <%call expr="layoutdata([[1,2,3],[4,5,6],[7,8,9]])" args="col">
        Body data: ${col}
    </%call>
    

    输出(空白以格式化):[译注:似乎有误!应该是3行3列表格]

    <table>
        <tr>
            <td>Body data: 1</td><td>Body data: 2</td><td>Body data: 3</td>
            <td>Body data: 4</td><td>Body data: 5</td><td>Body data: 6</td>
            <td>Body data: 7</td><td>Body data: 8</td><td>Body data: 9</td>
        </tr>
    </table>
    

    你不用仅盯着调用 body() 函数,在调用方可以定义任意多个 callables,使得 <%call> 标签可定制所有布局:

    <%def name="layout()">
        # a layout def
        <div class="mainlayout">
            <div class="header">
                ${caller.header()}
            </div>
            <div class="sidebar">
                ${caller.sidebar()}
            </div>
            <div class="content">
                ${caller.body()}
            </div>
        </div>
    </%def>
    
    # calls the layout def
    <%call expr="layout">
        <%def name="header()">
            I am the header
        </%def>
        <%def name="sidebar()">
            <ul>
                <li>sidebar 1</li>
                <li>sidebar 2</li>
            </ul>
        </%def>
    
            this is the body
    </%call>
    

    上述代码会输出(空白已格式化):

    <div class="mainlayout">
        <div class="header">
            I am the header
        </div>
        <div class="sidebar">
            <ul>
                <li>sidebar 1</li>
                <li>sidebar 2</li>
            </ul>
        </div>
        <div class="content">
            this is the body
        </div>
    </div>
    

    利用 <%call> 你可以做很多事情。可以创建表单控件库(form widget libraries),比如一个自包含的 <form> 标签,以及一组内嵌的 HTML input 元素,或者用 <div> 或其他元素创建可移植的包装控件,你可以创建标签来解释数据行,比如从数据库中得到的数据,然后将行的每一列传递给 body() 的一个可调用函数,这样就可以对数据行进行任何你想要的排版。基本上,你在其他系统中通过“自定义标签”或标签库想做的事情,Mako 中都可以通过 <%def> 或通过用 <%call> 调用 Python 函数的方式来实现。

    上一节: 语法 | 下一节: 运行时环境
  • 相关阅读:
    java,jenkins
    docker compose,link,Odoo
    nginx,docker反向代理
    centos7上安装docker-ce社区版
    Install Rancher server
    docker公司测试环境搭建总结
    ansible+docker
    桥接物理网卡,pipwork指定ip,外网连接,研究salt+docker
    20170605
    20170602
  • 原文地址:https://www.cnblogs.com/RChen/p/mako_doc_translation_3.html
Copyright © 2020-2023  润新知