跟python一样,在mako中,也可以用def来定义一个函数。不过在功能上,mako中的def比python稍微强大一点。
一、函数定义
在mako中,使用<%def>来定义一个函数。注意,这里函数名需要表示成name参数的值。
<%def name="hello()">
hello world
</%def>
定义完函数之后,就可以在模板中直接调用该函数:
call def: ${hello()}
通过这种方式定义的函数是mako的顶层函数(top level def),它可以在该模板任意地方使用,也可以通过namespace在其他模板中使用。
二、函数定义的背后
在mako的运行过程中,所有模板都会生成对应的py文件,浏览这些文件可以很好的理解mako背后的运行机制。在mako中,每一个模板对应的py文件中,都会有一个render_body函数,它是模板的主路口,模板render就是调用这个函数完成整个模板的渲染的。如果在模板中,定义了顶层函数,那么还会生成一个类似render_hello这样的函数,这里hello是具体的函数名。我们上面的例子在编译成py文件之后,就会得到如下代码:
def render_body(context,**pageargs):
__M_caller = context.caller_stack._push_frame()
try:
__M_locals = __M_dict_builtin(pageargs=pageargs)
def hello(): # hello函数,这个函数只完成对render_hello的一个调用,并将上下文作为参数传递下去
return render_hello(context.locals_(__M_locals))
__M_writer = context.writer()
# SOURCE LINE 3
__M_writer(u'\n\ncall def: ')
# SOURCE LINE 5
__M_writer(unicode(hello())) # 调用hello函数
__M_writer(u'\n')
return ''
finally:
context.caller_stack._pop_frame()
def render_hello(context):
__M_caller = context.caller_stack._push_frame()
try:
__M_writer = context.writer()
# SOURCE LINE 1
__M_writer(u'\n hello world\n’) # 显示hello world
return ''
finally:
context.caller_stack._pop_frame()
三、参数的传递
其实,在mako中,说起函数参数可分为def定义的参数和传入到函数的数据参数两种,就像上面提到的函数名name=“hello()”,其实是def的参数,def还有几个类似的参数,我们后面会深入讨论;而如果我们需要往这个函数中传入一些参数量,则也需要写在name这个变量名中,如:
${account(accountname='john')}
<%def name="account(accountname, type='regular')">
account name: ${accountname}, type: ${type}
</%def>
上述代码定义了一个函数account,该函数带两个参数accountname、type,其中type带有默认值。这些参数的使用方式跟python中函数的定义都是一致的。
使用了函数参数之后,编译之后生成的代码片段如下:
def account(accountname,type='regular'):
return render_account(context.locals_(__M_locals),accountname,type)
因此,可以看到,mako仅仅是将这些参数原原本本的转到了python里面而已。因此,所有跟python函数参数相关的规则都适用于mako函数参数。
四、变量的传递
在mako中,还会涉及到render传入的参数变量、模板中定义的变量等。这些变量的处理方式跟函数参数是不一样的。下面我们来看个例子:
Hello there ${username}, how are ya. Lets see what your account says:
<%
module_local_var = 'module_local_var'
%>
${account(accountname='john')}
<%def name="account(accountname)">
<%
account_local_var = 'account_local_var'
%>
Account name for ${username}: ${accountname}
module_local_var = ${module_local_var}
account_local_var = ${account_local_var}
</%def>
这段程序有四种变量:
* username,从render(或framework)传入,放入了context中,因此,可以直接在模板中引用。
* module_local_var ,是在模板中定义的变量
* account_local_var ,是在一个模板函数中定义的变量
*accountname,是函数的参数变量,在函数调用的时候指定。
下面我们来看一下编译生成的py文件:
def render_body(context,**pageargs):
__M_caller = context.caller_stack._push_frame()
try:
__M_locals = __M_dict_builtin(pageargs=pageargs)
username = context.get('username', UNDEFINED) # username是从context中取得的,如果没有定义,则默认使用UNDEFINED
def account(accountname):
return render_account(context.locals_(__M_locals),accountname) # accountname 作为函数参数传入
__M_writer = context.writer()
# SOURCE LINE 1
__M_writer(u'Hello there ')
__M_writer(unicode(username))
__M_writer(u', how are ya. Lets see what your account says:\n')
# SOURCE LINE 2
module_local_var = ‘module_local_var’ # 在模板中定义的变量,现在是render_body函数的变量
__M_locals_builtin_stored = __M_locals_builtin()
__M_locals.update(__M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key]) for __M_key in ['module_local_var'] if __M_key in __M_locals_builtin_stored])) # 将module_local_var 放入__M_locals中,这样render_account函数就能从context中取得该值
# SOURCE LINE 4
__M_writer(u'\n')
# SOURCE LINE 5
__M_writer(unicode(account(accountname='john'))) # 函数调用,传入参数值
__M_writer(u'\n\n')
# SOURCE LINE 14
__M_writer(u'\n')
return ''
finally:
context.caller_stack._pop_frame()
def render_account(context,accountname):
__M_caller = context.caller_stack._push_frame()
try:
username = context.get('username', UNDEFINED) # 在函数中再次取得username,两个函数之间通过context传递信息
module_local_var = context.get('module_local_var', UNDEFINED) # 从context中取得module_local_var,这也是模板级别的变量传递的途径
__M_writer = context.writer()
# SOURCE LINE 7
__M_writer(u'\n')
# SOURCE LINE 8
account_local_var = ‘account_local_var’ # 函数内部定义的变量,不用变化
# SOURCE LINE 10
__M_writer(u'\n Account name for ')
# SOURCE LINE 11
__M_writer(unicode(username))
__M_writer(u': ')
__M_writer(unicode(accountname))
__M_writer(u'
\n module_local_var = ')
# SOURCE LINE 12
__M_writer(unicode(module_local_var))
__M_writer(u'
\n account_local_var = ')
# SOURCE LINE 13
__M_writer(unicode(account_local_var))
__M_writer(u'
\n')
return ''
finally:
context.caller_stack._pop_frame()
由上面的代码,我们可以知道,render传入的参数、模板中定义的变量都是需要经过context传递的。
再来看一个错误使用参数的例子
<%
module_local_var = 'module_local_var'
%>
${account()}
<%def name="account()">
module_local_var = ${module_local_var}
<%
module_local_var = 'new_module_local_var'
%>
</%def>
这里的区别是,我们在函数中重新赋值了module_local_var,并且在赋值之前使用了该变量,我们知道这在python中是要出错的,因此在mako中也必须要出错,要不就混淆了。我们看看生成的代码:
def render_account(context):
__M_caller = context.caller_stack._push_frame()
try:
__M_writer = context.writer()
# SOURCE LINE 6
__M_writer(u'\n module_local_var = ')
# SOURCE LINE 7
__M_writer(unicode(module_local_var))
__M_writer(u'
\n')
# SOURCE LINE 8
module_local_var = 'new_module_local_var'
# SOURCE LINE 10
__M_writer(u'\n')
return ''
finally:
context.caller_stack._pop_frame()
从这段代码可以看出,只要在函数中重新赋值了module_local_var 这个变量,那么这个变量就是一个函数内的局部变量,就没有再从context取变量这个步骤了,也因此,这段代码显然就报错了。
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment