当前位置:Gxlcms > Python > 深入解析Python的Tornado框架中内置的模板引擎

深入解析Python的Tornado框架中内置的模板引擎

时间:2021-07-01 10:21:17 帮助过:21人阅读

template中的_parse方法是模板文法的解析器,而这个文件中一坨一坨的各种node以及block,就是解析结果的承载者,也就是说在经过parse处理过后,我们输入的tornado的html模板就变成了各种block的集合。
这些block和node的祖宗就是这个“抽象”类, _Node,它定义了三个方法定义,其中generate方法是必须由子类提供实现的(所以我叫它“抽象”类)。
理论上来说,当一个类成为祖宗类时,必定意味着这个类包含了一些在子类中通用的行为,那么,从_Node暴露出来的方法来看,即所有的子类理论上都会有如下特征:
1. 可作为容器 (each_child, find_named_blocks)
2. generate
当然了,理想总是丰满的,现实也总有那么点儿不对劲,对于某些子孙,它们的特征看上去不是那么靠谱,比如_Text。
_Text这个类只用到了generate这个方法,用于将文字(Html, JS)经过trim后添加到输入流中,如果调用它的each_child or find_named_blocks,当然你能这么做,但是没有什么意义。
前面反复说到_Parse方法,它返回的结果是一个_ChunkList的实例,而_ChunkList继承与_Node。这是一个体现了_Node容器特点的类,重写了generate方法和each_child方法,而基本上就是依次调用容器内所有元素的相关方法而已。
_Nodes众多子子孙孙中比较奇葩的是_ExtendsBlock这个类,丫什么事情都没做(That is true),看上去像是另外一个“抽象类”,但是居然会被_Parse初始化,用于处理Extends这个token(tornado术语)。我就纳闷了,一旦这货被generate,难道不会抛一个异常出来木?
真正有意思的是另外几个方法,它们有共通的模式,用_ApplyBlock来举例
在_ApplyBlock中,有趣的是generate方法

  1. def generate(self, writer):
  2. method_name = "apply%d" % writer.apply_counter
  3. writer.apply_counter += 1
  4. writer.write_line("def %s():" % method_name, self.line)
  5. with writer.indent():
  6. writer.write_line("_buffer = []", self.line)
  7. writer.write_line("_append = _buffer.append", self.line)
  8. self.body.generate(writer)
  9. writer.write_line("return _utf8('').join(_buffer)", self.line)
  10. writer.write_line("_append(%s(%s()))" % (
  11. self.method, method_name), self.line)

简单来说,这个函数做了两件事情:
定义了一个python文件全局函数叫做applyXXX():,其中的XXX是一个整形的,自增的值,返回值是一个utf8字符串。
执行这个applyXXX函数,将此函数的输出再作为self.method这个函数的输入。
所以,如果一个类似于这样的模板

  1. {%apply linkify%} {{address}} {%end%}

会得到一个类似于如下的输出:

  1. r = applyXXX()
  2. r = linkify(r)
  3. _append(r)

tornado的template机制,本质上讲,就是允许开发者已HTML + template marker的方式来编写视图模板,但是在背后,tornado会把这些视图模板通过template的处理,变成可编译的python代码。
拿autumn-sea上面的代码作为例子,比较容易理解:
View Template

  1. <title>{{ title }}</title>
  2. hello! {{ name }}

处理后

  1. _buffer = []
  2. _buffer.append('\\n\\n<title>')
  3. _tmp = title
  4. if isinstance(_tmp, str): _buffer.append(_tmp)
  5. elif isinstance(_tmp, unicode): _buffer.append(_tmp.encode('utf-8'))
  6. else: _buffer.append(str(_tmp))
  7. _buffer.append('</title>\\n\\n\\n')
  8. _buffer.append('hello! ')
  9. _tmp = name
  10. if isinstance(_tmp, str): _buffer.append(_tmp)
  11. elif isinstance(_tmp, unicode): _buffer.append(_tmp.encode('utf-8'))
  12. else: _buffer.append(str(_tmp))
  13. _buffer.append('\\n\\n\\n')
  14. return ''.join(_buffer)\n"

实例剖析
tornado的模板基本都在template.py这个文件中,短短800多行代码就实现了基本可用的模板,让我们慢慢揭开她的面纱。
首先我们看看tornado是如何编译模板的,下面是个简单的模板

  1. t = Template("""\
  2. {%if names%}
  3. {% for name in names %}
  4. {{name}}
  5. {%end%}
  6. {%else%}
  7. no one
  8. {%end%}
  9. """)

tornado最后编译代码如下:

  1. <br>
  2. <p></p>
  3. <div class="jb51code">
  4. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>def _tt_execute(): # <string>:0 </li><li> _tt_buffer = [] # <string>:0 </li><li> _tt_append = _tt_buffer.append # <string>:0 </li><li> if names: # <string>:1 </li><li> _tt_append('\n ') # <string>:2 </li><li> for name in names: # <string>:2 </li><li> _tt_append('\n ') # <string>:3 </li><li> _tt_tmp = name # <string>:3 </li><li> if isinstance(_tt_tmp, _tt_string_types): _tt_tmp = _tt_utf8(_tt_tmp) # <string>:3 </li><li> else: _tt_tmp = _tt_utf8(str(_tt_tmp)) # <string>:3 </li><li> _tt_tmp = _tt_utf8(xhtml_escape(_tt_tmp)) # <string>:3 </li><li> _tt_append(_tt_tmp) # <string>:3 </li><li> _tt_append('\n ') # <string>:4 </li><li> pass # <string>:2 </li><li> _tt_append('\n') # <string>:5 </li><li> pass # <string>:5 </li><li> else: # <string>:5 </li><li> _tt_append('\nno one\n') # <string>:7 </li><li> pass # <string>:1 </li><li> _tt_append('\n') # <string>:8 </li><li> return _tt_utf8('').join(_tt_buffer) # <string>:0 </li><li></string></string></string></string></string></string></string></string></string></string></string></string></string></string></string></string></string></string></string></string></string></li></ol></pre>
  5. <p></p>
  6. <p>是的,你没看错,tornado编译就是将之翻译成一个个代码块,最后通exec传递我们给的参数命名空间执行_tt_execute函数。<br>
  7. 在我们上面的模板中包含了4种预定义的NODE节点,_ControlBlock,_Expression,_TEXT,每种Node节点都有自己的生成方式。<br>
  8. 比如说_Expression表达式节点,也就是我们模板中的{{name}},当_parse解析时发现'{'后面还是'{'就认为是表达式节点,<br>
  9. </p>
  10. <div class="jb51code">
  11. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>class _Expression(_Node): </li><li> def __init__(self, expression, line, raw=False): </li><li> self.expression = expression </li><li> self.line = line </li><li> self.raw = raw </li><li> </li><li> def generate(self, writer): </li><li> writer.write_line("_tt_tmp = %s" % self.expression, self.line) </li><li> writer.write_line("if isinstance(_tt_tmp, _tt_string_types):" </li><li> " _tt_tmp = _tt_utf8(_tt_tmp)", self.line) </li><li> writer.write_line("else: _tt_tmp = _tt_utf8(str(_tt_tmp))", self.line) </li><li> if not self.raw and writer.current_template.autoescape is not None: </li><li> # In python3 functions like xhtml_escape return unicode, </li><li> # so we have to convert to utf8 again. </li><li> writer.write_line("_tt_tmp = _tt_utf8(%s(_tt_tmp))" % </li><li> writer.current_template.autoescape, self.line) </li><li> writer.write_line("_tt_append(_tt_tmp)", self.line) </li><li></li></ol></pre>
  12. <p></p>
  13. <p>最后生成时会调用节点的generate方法,self.expression就是上面的name,所以当exec的时候就会把name的值append到内部的列表中。<br>
  14. 像if,for等都是控制节点,他们的定义如下: <br>
  15. </p>
  16. <div class="jb51code">
  17. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>class _ControlBlock(_Node): </li><li> def __init__(self, statement, line, body=None): </li><li> self.statement = statement </li><li> self.line = line </li><li> self.body = body </li><li> </li><li> def each_child(self): </li><li> return (self.body,) </li><li> </li><li> def generate(self, writer): </li><li> writer.write_line("%s:" % self.statement, self.line) </li><li> with writer.indent(): </li><li> self.body.generate(writer) </li><li> # Just in case the body was empty </li><li> writer.write_line("pass", self.line) </li></ol></pre>
  18. <p></p>
  19. <p><br>
  20. 控制节点的generate方法有点意义,因为if,for等是下一行是需要缩进的,所以调用了with writer.indent继续缩进控制,可以看下<br>
  21. _CodeWriter的indent方法。<br>
  22. 节点中比较有意思的是_ExtendsBlock,这是实现目标基础的节点,<br>
  23. </p>
  24. <div class="jb51code">
  25. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>class _ExtendsBlock(_Node): </li><li> def __init__(self, name): </li><li> self.name = name </li><li></li></ol></pre>
  26. <p></p>
  27. <p>我们发现并没有定义generate方法,那当生成继承节点时不是会报错吗?让我们看一段事例<br>
  28. </p>
  29. <div class="jb51code">
  30. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>loader = Loader('.') </li><li>t=Template("""\ </li><li>{% extends base.html %} </li><li>{% block login_name %}hello world! {{ name }}{% end %} </li><li>""",loader=loader) </li><li></li></ol></pre>
  31. <p></p>
  32. <p>当前目录下base.html如下:<br>
  33. </p>
  34. <div class="jb51code">
  35. <pre class="brush:php;toolbar:false layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li> </li><li> </li><li><title>{{ title }}</title> </li><li> </li><li> </li><li>{% block login_name %}hello! {{ name }}{% end %} </li><li> </li><li> </li></ol></pre>
  36. <p></p>
  37. <p> 我们可以看看解析后的节点,<br>
  38. </p>
  39. <p><img alt="2016711182740215.jpg (706×500)" src="http://files.jb51.net/file_images/article/201607/2016711182740215.jpg?2016611182750"></p>
  40. <p>由于我们继承了base.html,所以我们的应该以base.html的模板生成,并使用新定义的block代替base.html中的block,<br>
  41. 这是很正常的思路,tornado也的确是这么干的,只不过处理的并不是在_ExtendsBlock。<br>
  42. 而实在Template的_generate_python中<br>
  43. </p>
  44. <div class="jb51code">
  45. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>def _generate_python(self, loader, compress_whitespace): </li><li> buffer = StringIO() </li><li> try: </li><li> # named_blocks maps from names to _NamedBlock objects </li><li> named_blocks = {} </li><li> ancestors = self._get_ancestors(loader) </li><li> ancestors.reverse() </li><li> for ancestor in ancestors: </li><li> ancestor.find_named_blocks(loader, named_blocks) </li><li> writer = _CodeWriter(buffer, named_blocks, loader, ancestors[0].template, </li><li> compress_whitespace) </li><li> ancestors[0].generate(writer) </li><li> return buffer.getvalue() </li><li> finally: </li><li> buffer.close() </li><li> </li><li> def _get_ancestors(self, loader): </li><li> ancestors = [self.file] </li><li> for chunk in self.file.body.chunks: </li><li> if isinstance(chunk, _ExtendsBlock): </li><li> if not loader: </li><li> raise ParseError("{% extends %} block found, but no " </li><li> "template loader") </li><li> template = loader.load(chunk.name, self.name) </li><li> ancestors.extend(template._get_ancestors(loader)) </li><li> return ancestors </li><li></li></ol></pre>
  46. <p></p>
  47. <p>_generate_python中调用_get_ancestors获取当前模板的父模板,我们看到如果当前模板的_FILE节点中有_ExtendsBlock就代表有父模板并通过loader.load加载父模板,此时父模板已经是解析过的_FILE节点了。所以,在上面的模板中,ancestors是[当前模板_FILE节点,父模板_FILE节点],ancestors.reverse()后其实ancestors[0]就是父模板,我们看到最后是通过ancestors[0].generate(writer)来生成代码的。那当前模板是如何替换父模板的block内容呢?<br>
  48. 看上图,block login_name通过解析为_NamedBlock,在_generate_python中通过调用ancestor.find_named_blocks来替换<br>
  49. 父模板的_NamedBlock的。<br>
  50. </p>
  51. <div class="jb51code">
  52. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>for ancestor in ancestors:</li><li> ancestor.find_named_blocks(loader, named_blocks)</li><li>ancestor其实就是_FILE节点,find_named_blocks将遍历_FILE节点中所有节点并调用find_named_blocks</li><li> </li><li>class _NamedBlock(_Node): </li><li> def find_named_blocks(self, loader, named_blocks): </li><li> named_blocks[self.name] = self </li><li> _Node.find_named_blocks(self, loader, named_blocks) </li><li></li></ol></pre>
  53. <p></p>
  54. <p>其它节点find_named_blocks都没有做什么事,_NamedBlock通过named_blocks[self.name] = self替换为当前模板的_NamedBlock,因为ancestors父模板在前,当前模板在后,所以最后使用的是当前模板的_NamedBlock。<br>
  55. 生成代码后generate将在给定的命名空间中exec代码<br>
  56. </p>
  57. <div class="jb51code">
  58. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>def generate(self, **kwargs): </li><li> """Generate this template with the given arguments.""" </li><li> namespace = { </li><li> "escape": escape.xhtml_escape, </li><li> "xhtml_escape": escape.xhtml_escape, </li><li> "url_escape": escape.url_escape, </li><li> "json_encode": escape.json_encode, </li><li> "squeeze": escape.squeeze, </li><li> "linkify": escape.linkify, </li><li> "datetime": datetime, </li><li> "_tt_utf8": escape.utf8, # for internal use </li><li> "_tt_string_types": (unicode_type, bytes_type), </li><li> # __name__ and __loader__ allow the traceback mechanism to find </li><li> # the generated source code. </li><li> "__name__": self.name.replace('.', '_'), </li><li> "__loader__": ObjectDict(get_source=lambda name: self.code), </li><li> } </li><li> namespace.update(self.namespace) </li><li> namespace.update(kwargs) </li><li> exec_in(self.compiled, namespace) </li><li> execute = namespace["_tt_execute"] </li><li> # Clear the traceback module's cache of source data now that </li><li> # we've generated a new template (mainly for this module's </li><li> # unittests, where different tests reuse the same name). </li><li> linecache.clearcache() </li><li> return execute() </li><li></li></ol></pre>
  59. <p></p>
  60. <p>所以在模板中可以使用datetime等,都是通过在这里注入到模板中的,当然还有其它的是通过<br>
  61. web.py 中get_template_namespace注入的 <br>
  62. </p>
  63. <div class="jb51code">
  64. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li> def get_template_namespace(self): </li><li> """Returns a dictionary to be used as the default template namespace. </li><li> </li><li> May be overridden by subclasses to add or modify values. </li><li> </li><li> The results of this method will be combined with additional </li><li> defaults in the `tornado.template` module and keyword arguments </li><li> to `render` or `render_string`. </li><li> """ </li><li> namespace = dict( </li><li> handler=self, </li><li> request=self.request, </li><li> current_user=self.current_user, </li><li> locale=self.locale, </li><li> _=self.locale.translate, </li><li> static_url=self.static_url, </li><li> xsrf_form_html=self.xsrf_form_html, </li><li> reverse_url=self.reverse_url </li><li> ) </li><li> namespace.update(self.ui) </li><li> return namespace </li><li></li></ol></pre>
  65. <p></p>
  66. <p>我们再来看看tornado的模板是如何对UI模块的支持的。<br>
  67. </p>
  68. <div class="jb51code">
  69. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>{% for entry in entries %} </li><li> {% module Entry(entry) %} </li><li>{% end %} </li><li></li></ol></pre>
  70. <p></p>
  71. <p>在使用module时将会生成_Module节点 <br>
  72. </p>
  73. <div class="jb51code">
  74. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>class _Module(_Expression): </li><li> def __init__(self, expression, line): </li><li> super(_Module, self).__init__("_tt_modules." + expression, line, </li><li> raw=True) </li><li></li></ol></pre>
  75. <p></p>
  76. <p>我们看到其实_Module节点是继承自_Expression节点,所以最后执行的是_tt_modules.Entry(entry)<br>
  77. _tt_modules定义在web.py的RequestHandler中<br>
  78. </p>
  79. <div class="jb51code">
  80. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>self.ui["_tt_modules"] = _UIModuleNamespace(self,application.ui_modules)</li><li></li></ol></pre>
  81. <p></p>
  82. <p>并通过上文的get_template_namespace中注入到模板中。<br>
  83. </p>
  84. <div class="jb51code">
  85. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>class _UIModuleNamespace(object): </li><li> """Lazy namespace which creates UIModule proxies bound to a handler.""" </li><li> def __init__(self, handler, ui_modules): </li><li> self.handler = handler </li><li> self.ui_modules = ui_modules </li><li> </li><li> def __getitem__(self, key): </li><li> return self.handler._ui_module(key, self.ui_modules[key]) </li><li> </li><li> def __getattr__(self, key): </li><li> try: </li><li> return self[key] </li><li> except KeyError as e: </li><li> raise AttributeError(str(e)) </li><li></li></ol></pre>
  86. <p></p>
  87. <p>所以当执行_tt_modules.Entry(entry)时先访问_UIModuleNamespace的__getattr__,后访问__getitem__,最后调用<br>
  88. handler._ui_module(key, self.ui_modules[key]),<br>
  89. </p>
  90. <div class="jb51code">
  91. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>def _ui_module(self, name, module): </li><li> def render(*args, **kwargs): </li><li> if not hasattr(self, "_active_modules"): </li><li> self._active_modules = {} </li><li> if name not in self._active_modules: </li><li> self._active_modules[name] = module(self) </li><li> rendered = self._active_modules[name].render(*args, **kwargs) </li><li> return rendered </li><li> return render </li><li></li></ol></pre>
  92. <p></p>
  93. <p>_tt_modules.Entry(entry)中entry将会传给_ui_module内部的render,也就是args=entry<br>
  94. self._active_modules[name] = module(self)此时就是实例化后的UIModule,调用render获取渲染后的内容<br>
  95. </p>
  96. <div class="jb51code">
  97. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>class Entry(tornado.web.UIModule): </li><li> def render(self, entry, show_comments=False): </li><li> return self.render_string( </li><li> "module-entry.html", entry=entry, show_comments=show_comments) </li></ol></pre>
  98. <p></p>
  99. <p>当然如果你觉得这么做费事,也可以使用tornado自带的TemplateModule,它继承自UIModule,<br>
  100. 你可以这么用<br>
  101. </p>
  102. <div class="jb51code">
  103. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>{% module Template("module-entry.html", show_comments=True) %} </li><li></li></ol></pre>
  104. <p></p>
  105. <p>在module_entry.html中可以通过set_resources引用需要的静态文件<br>
  106. </p>
  107. <div class="jb51code">
  108. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>{{ set_resources(embedded_css=".entry { margin-bottom: 1em; }") }} </li><li></li></ol></pre>
  109. <p></p>
  110. <p>这里需要注意的是:只能在Template引用的html文件中使用set_resources函数,因为set_resources是TemplateModule.render的内部函数 <br>
  111. </p>
  112. <div class="jb51code">
  113. <pre class="brush:py; layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li>class TemplateModule(UIModule): </li><li> """UIModule that simply renders the given template. </li><li> </li><li> {% module Template("foo.html") %} is similar to {% include "foo.html" %}, </li><li> but the module version gets its own namespace (with kwargs passed to </li><li> Template()) instead of inheriting the outer template's namespace. </li><li> </li><li> Templates rendered through this module also get access to UIModule's </li><li> automatic javascript/css features. Simply call set_resources </li><li> inside the template and give it keyword arguments corresponding to </li><li> the methods on UIModule: {{ set_resources(js_files=static_url("my.js")) }} </li><li> Note that these resources are output once per template file, not once </li><li> per instantiation of the template, so they must not depend on </li><li> any arguments to the template. </li><li> """ </li><li> def __init__(self, handler): </li><li> super(TemplateModule, self).__init__(handler) </li><li> # keep resources in both a list and a dict to preserve order </li><li> self._resource_list = [] </li><li> self._resource_dict = {} </li><li> </li><li> def render(self, path, **kwargs): </li><li> def set_resources(**kwargs): </li><li> if path not in self._resource_dict: </li><li> self._resource_list.append(kwargs) </li><li> self._resource_dict[path] = kwargs </li><li> else: </li><li> if self._resource_dict[path] != kwargs: </li><li> raise ValueError("set_resources called with different " </li><li> "resources for the same template") </li><li> return "" </li><li> return self.render_string(path, set_resources=set_resources, </li><li> **kwargs) </li></ol></pre> </div>
  114. <div class="">
  115. <ul class="m-news-opt fix">
  116. <li class="opt-item">
  117. <a href="/python-357185.html" target="_blank"><p>< 上一篇</p><p class="ellipsis">Python使用SocketServer模块编写基本服务器程序的教程</p></a>
  118. </li>
  119. <li class="opt-item ta-r">
  120. <a href="/python-357187.html" target="_blank"><p>下一篇 ></p><p class="ellipsis">详解Python中open()函数指定文件打开方式的用法</p></a>
  121. </li>
  122. </ul>
  123. </div>
  124. </div>
  125. </div>
  126. <div class="g-title fix">
  127. <h2 class="title-txt">人气教程排行</h2>
  128. </div>
  129. <div class="m-rank u-dashed mb40">
  130. <ul>
  131. <li class="rank-item">
  132. <a href="/python-361871.html" title="对Python2.7pandas中的read_excel详解" class="item-name ellipsis" target="_blank">
  133. <span class="g-art-count fr">384次</span>
  134. <span class="g-sort-num top">1</span>
  135. 对Python2.7pandas中的read_excel详解 </a>
  136. </li>
  137. <li class="rank-item">
  138. <a href="/python-357851.html" title="Python实现定时弹窗提醒" class="item-name ellipsis" target="_blank">
  139. <span class="g-art-count fr">383次</span>
  140. <span class="g-sort-num second">2</span>
  141. Python实现定时弹窗提醒 </a>
  142. </li>
  143. <li class="rank-item">
  144. <a href="/python-359898.html" title="python爬虫入门(3)--利用requests构建知乎API" class="item-name ellipsis" target="_blank">
  145. <span class="g-art-count fr">383次</span>
  146. <span class="g-sort-num third">3</span>
  147. python爬虫入门(3)--利用requests构建知乎API </a>
  148. </li>
  149. <li class="rank-item">
  150. <a href="/python-361328.html" title="python如何爬取搜狗微信公众号文章永久链接的思路解析" class="item-name ellipsis" target="_blank">
  151. <span class="g-art-count fr">382次</span>
  152. <span class="g-sort-num ">4</span>
  153. python如何爬取搜狗微信公众号文章永久链接的思路解析 </a>
  154. </li>
  155. <li class="rank-item">
  156. <a href="/python-363639.html" title="python字典的键可以相同吗" class="item-name ellipsis" target="_blank">
  157. <span class="g-art-count fr">381次</span>
  158. <span class="g-sort-num ">5</span>
  159. python字典的键可以相同吗 </a>
  160. </li>
  161. <li class="rank-item">
  162. <a href="/python-462846.html" title="python是一种面向什么的语言?" class="item-name ellipsis" target="_blank">
  163. <span class="g-art-count fr">381次</span>
  164. <span class="g-sort-num ">6</span>
  165. python是一种面向什么的语言? </a>
  166. </li>
  167. <li class="rank-item">
  168. <a href="/python-355903.html" title="python通过pil为png图片填充上背景颜色的方法" class="item-name ellipsis" target="_blank">
  169. <span class="g-art-count fr">381次</span>
  170. <span class="g-sort-num ">7</span>
  171. python通过pil为png图片填充上背景颜色的方法 </a>
  172. </li>
  173. <li class="rank-item">
  174. <a href="/python-364233.html" title="python语言的编程模式有什么" class="item-name ellipsis" target="_blank">
  175. <span class="g-art-count fr">380次</span>
  176. <span class="g-sort-num ">8</span>
  177. python语言的编程模式有什么 </a>
  178. </li>
  179. <li class="rank-item">
  180. <a href="/python-353438.html" title="使用python获取进程pid号的方法" class="item-name ellipsis" target="_blank">
  181. <span class="g-art-count fr">380次</span>
  182. <span class="g-sort-num ">9</span>
  183. 使用python获取进程pid号的方法 </a>
  184. </li>
  185. <li class="rank-item">
  186. <a href="/python-362615.html" title="Python中如何解决无限循环的问题" class="item-name ellipsis" target="_blank">
  187. <span class="g-art-count fr">380次</span>
  188. <span class="g-sort-num ">10</span>
  189. Python中如何解决无限循环的问题 </a>
  190. </li>
  191. <li class="rank-item">
  192. <a href="/python-466149.html" title="怎么解决pip不是内部或外部命令" class="item-name ellipsis" target="_blank">
  193. <span class="g-art-count fr">378次</span>
  194. <span class="g-sort-num ">11</span>
  195. 怎么解决pip不是内部或外部命令 </a>
  196. </li>
  197. <li class="rank-item">
  198. <a href="/python-374795.html" title="python中def是什么意思" class="item-name ellipsis" target="_blank">
  199. <span class="g-art-count fr">378次</span>
  200. <span class="g-sort-num ">12</span>
  201. python中def是什么意思 </a>
  202. </li>
  203. <li class="rank-item">
  204. <a href="/python-361381.html" title="对numpy中数组元素的统一赋值实例" class="item-name ellipsis" target="_blank">
  205. <span class="g-art-count fr">376次</span>
  206. <span class="g-sort-num ">13</span>
  207. 对numpy中数组元素的统一赋值实例 </a>
  208. </li>
  209. <li class="rank-item">
  210. <a href="/python-378450.html" title="python的选择语句是什么语句" class="item-name ellipsis" target="_blank">
  211. <span class="g-art-count fr">374次</span>
  212. <span class="g-sort-num ">14</span>
  213. python的选择语句是什么语句 </a>
  214. </li>
  215. <li class="rank-item">
  216. <a href="/python-362375.html" title="Python中构造方法的解析(附示例)" class="item-name ellipsis" target="_blank">
  217. <span class="g-art-count fr">374次</span>
  218. <span class="g-sort-num ">15</span>
  219. Python中构造方法的解析(附示例) </a>
  220. </li>
  221. <li class="rank-item">
  222. <a href="/python-360729.html" title="关于python中引入导入与自定义模块以及外部文件的实例分享" class="item-name ellipsis" target="_blank">
  223. <span class="g-art-count fr">373次</span>
  224. <span class="g-sort-num ">16</span>
  225. 关于python中引入导入与自定义模块以及外部文件的实例分享 </a>
  226. </li>
  227. <li class="rank-item">
  228. <a href="/python-364421.html" title="python如何在不同类之间调用方法" class="item-name ellipsis" target="_blank">
  229. <span class="g-art-count fr">372次</span>
  230. <span class="g-sort-num ">17</span>
  231. python如何在不同类之间调用方法 </a>
  232. </li>
  233. <li class="rank-item">
  234. <a href="/python-462395.html" title="python中的【//】是什么运算符号" class="item-name ellipsis" target="_blank">
  235. <span class="g-art-count fr">372次</span>
  236. <span class="g-sort-num ">18</span>
  237. python中的【//】是什么运算符号 </a>
  238. </li>
  239. <li class="rank-item">
  240. <a href="/python-363743.html" title="python中╲t是什么" class="item-name ellipsis" target="_blank">
  241. <span class="g-art-count fr">371次</span>
  242. <span class="g-sort-num ">19</span>
  243. python中╲t是什么 </a>
  244. </li>
  245. <li class="rank-item">
  246. <a href="/python-357501.html" title="python同时给多个变量赋值" class="item-name ellipsis" target="_blank">
  247. <span class="g-art-count fr">371次</span>
  248. <span class="g-sort-num ">20</span>
  249. python同时给多个变量赋值 </a>
  250. </li>
  251. </ul>
  252. </div>
  253. </div>
  254. </div>
  255. <!-- / 教程内容页 -->
  256. </div>
  257. </div>
  258. <!-- 页尾 -->
  259. <div class="footer">
  260. 本站所有资源全部来源于网络,若本站发布的内容侵害到您的隐私或者利益,请联系我们删除!</div>
  261. <!-- / 页尾 -->
  262. <div style="display:none">
  263. <div class="login-box" id="login-dialog">
  264. <div class="login-top"><a class="current" rel="nofollow" id="login1" onclick="setTab('login',1,2);">登录</a></div>
  265. <div class="login-form" id="nav-signin">
  266. <!-- <div class="login-ico"><a rel="nofollow" class="qq" id="qqlogin" target="_blank" href="/user-center-qqlogin.html"> QQ </a></div> -->
  267. <div class="login-box-form" id="con_login_1">
  268. <form id="loginform" action="/user-center-login.html" method="post" onsubmit="return false;">
  269. <p class="int-text">
  270. <input class="email" id="username" name="username" type="text" value="用户名或Email" onfocus="if(this.value=='用户名或Email'){this.value='';}" onblur="if(this.value==''){this.value='用户名或Email';};"></p>
  271. <p class="int-text">
  272. <input class="password1" type="password" id="password" name="password" value="******" onblur="if(this.value=='') this.value='******';" onfocus="if(this.value=='******') this.value='';">
  273. </p>
  274. <p class="int-info">
  275. <label class="ui-label"> </label>
  276. <label for="agreement" class="ui-label-checkbox">
  277. <input type="checkbox" value="" name="cookietime" id="cookietime" checked="checked">
  278. <input type="hidden" name="notforward" id="notforward" value="1">
  279. <input type="hidden" name="dosubmit" id="dosubmit" value="1">记住我的登录 </label>
  280. <a rel="nofollow" class="aright" href="/user-center-forgetpwd.html" target="_blank"> 忘记密码? </a></p>
  281. <p class="int-btn"><a rel="nofollow" id="loginbt" class="loginbtn"><span>登录</span></a></p>
  282. </form>
  283. </div>
  284. <form id="regform" action="/user-center-reg.html" method="post">
  285. <div class="login-reg" style="display: none;" id="con_login_2">
  286. <input type="hidden" name="t" id="t">
  287. <p class="int-text">
  288. <input id="email" name="email" type="text" value="Email" onfocus="if(this.value=='Email'){this.value='';}" onblur="if(this.value==''){this.value='Email';};"></p>
  289. <p class="int-text">
  290. <input id="uname" name="username" type="text" value="用户名或昵称" onfocus="if(this.value=='用户名或昵称'){this.value='';}" onblur="if(this.value==''){this.value='用户名或昵称';};"></p>
  291. <p class="int-text">
  292. <input type="password" id="pwd" name="password" value="******" onblur="if(this.value=='') this.value='******';" onfocus="if(this.value=='******') this.value='';"> </p>
  293. <p class="int-text1"><span class="inputbox">
  294. <input id="validate" name="validate" type="text" value="验证码" onfocus="if(this.value=='验证码'){this.value='';}" onblur="if(this.value==''){this.value='验证码';};">
  295. </span><span class="yzm-img"><img src="/user-checkcode-index" alt="看不清楚换一张" id="indexlogin"></span></p>
  296. <p class="int-info">
  297. <label>
  298. <input value="" name="agreement" id="agreement" checked="checked" type="checkbox">
  299. 我已阅读<a rel="nofollow" href="/user-center-agreement.html">用户协议</a>及<a rel="nofollow" href="/user-center-agreement.html">版权声明</a></label>
  300. </p>
  301. <p class="int-btn"><input type="hidden" name="dosubmit">
  302. <a rel="nofollow" class="loginbtn" id="register"><span>注册</span></a></p>
  303. </div>
  304. </form>
  305. </div>
  306. </div>
  307. </div>
  308. </div>
  309. <div data-type="4" data-plugin="aroundbox" data-plugin-aroundbox-x="left" data-plugin-aroundbox-y="bottom" data-plugin-aroundbox-iconsize="60x60" data-plugin-aroundbox-fixed="1" data-plugin-aroundbox-offsetx="10"></div>
  310. <script src="https://hm.baidu.com/hm.js?6dc1c3c5281cf70f49bc0bc860ec24f2"></script><script>
  311. var _hmt = _hmt || [];
  312. (function() {
  313. var hm = document.createElement("script");
  314. hm.src = "https://hm.baidu.com/hm.js?6dc1c3c5281cf70f49bc0bc860ec24f2";
  315. var s = document.getElementsByTagName("script")[0];
  316. s.parentNode.insertBefore(hm, s);
  317. })();
  318. </script>
  319. <script type="text/javascript" src="/layui/layui.js"></script>
  320. <script>
  321. layui.use('code', function() {
  322. layui.code({
  323. elem: 'pre', //默认值为.layui-code
  324. about: false,
  325. skin: 'notepad',
  326. title: 'php怎么实现数据库验证跳转代码块',
  327. encode: true //是否转义html标签。默认不开启
  328. });
  329. });
  330. </script>
  331. </div></div></div></div></div></div></div></div></div></div></div>