<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>My blog</title>
		<description>A jekyll theme</description>
		<link>https://breezecloud.github.io</link>
		<atom:link href="https://breezecloud.github.io/feed.xml" rel="self" type="application/rss+xml" />
		
			<item>
				<title>jupyter-notebook&nbdev&github实现python开发全过程</title>
				<description>&lt;h1 id=&quot;前言&quot;&gt;前言&lt;/h1&gt;
&lt;p&gt;  以前一直用sublime文本编辑器写代码。不得不说作为一个业余编程爱好者，这款文本编辑器太好用了，几乎满足了所有编写代码的需求。但同时缺点也很明显，你必须写完代码到命令行窗口下测试，或者版本管理等任何其它事情，虽然sublime有很多插件也许能满足你的需求，但毕竟插件功能有限，也不一定很好找到合适的。直到最近看到有一篇介绍nbdev的文章，介绍基于jupyter-notebook的nbdev说成可以完成整个编程生命周期的神器，可以结合github自动完成很多工作。正好在冠状肺炎病毒肆虐之际，有时间在家里学习一下，是否可以能让我能鸟枪换炮呢？&lt;/p&gt;

&lt;h1 id=&quot;jupyter-notebook&quot;&gt;jupyter-notebook&lt;/h1&gt;
&lt;p&gt;  先来说说jupyter-notebook。这是一个交互式笔记本，支持运行40 多种编程语言。Jupyter-Notebook在浏览器中创建和共享程序文档，支持实时代码，数学方程，可视化和 markdown。 -用途包括：数据清理和转换，数值模拟，统计建模，机器学习等等。如果用它来写python，有点类似python自带的IDLE，但比IDLE功能多，编写更多类型的文件，可以保存代码运行的结果。它具有以下优势：&lt;br /&gt;-
-可选择语言：支持超过40种编程语言，包括Python、R、Julia、Scala等。
-分享笔记本：可以使用电子邮件、Dropbox、GitHub和Jupyter Notebook Viewer与他人共享。
-交互式输出：代码可以生成丰富的交互式输出，包括HTML、图像、视频、LaTeX等等。
-大数据整合：通过Python、R、Scala编程语言使用Apache Spark等大数据框架工具。支持使用pandas、scikit-learn、ggplot2、TensorFlow来探索同一份数据&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;安装
  安装很简单，因为是基于python开发，直接用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install jupyter&lt;/code&gt;经过漫长的等待顺利安装完成（pip服务器实在太慢，考虑换一个国内的）。安装时注意python和其下面的pip命令执行路径是否在PATH环境变量中，如果不在建议加上。&lt;/li&gt;
  &lt;li&gt;运行
  在命令行下执行：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jupyter notebook
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;命令会启动一个web服务器，并自动打开默认浏览器，自动指向http://localhost:8888/tree的链接，显示当前目录的文件列表。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;简单的使用
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jupyter notebook --generate-config
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;在用户目录下生成配置文件，打开“.jupyter”文件夹，可以看到里面有个配置文件，下次启动会使用这个默认配置文件。
新建一个文档：
&lt;img src=&quot;/images/2020-2-5-1.png&quot; alt=&quot;&quot; /&gt;
输入：
&lt;img src=&quot;/images/2020-2-5-2.png&quot; alt=&quot;&quot; /&gt;
在这里输入一条然后运行：
&lt;img src=&quot;/images/2020-2-5-3.png&quot; alt=&quot;&quot; /&gt;
查看快捷键：
&lt;img src=&quot;/images/2020-2-5-4.png&quot; alt=&quot;&quot; /&gt;
这样jupyter基本使用就可以了，我们也可以使用ctl+s随时保存文档；这样我们所有操作记录就保存下来，下次打开直接使用。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;nbdev介绍&quot;&gt;nbdev介绍&lt;/h1&gt;
&lt;p&gt;   notebook 或 REPL 不具备的功能，比如：优秀的文档查找功能、优秀的语法高亮功能、集成单元测试，以及（关键的）生成最终可分发源代码文件的能力。nbdev增强了所谓“探索式”编程的功能，同时将 IDE/编辑器开发的优势带入 notebook 系统中，以便用户在 notebook 中完成开发，且不会影响整个项目生命周期。本文的大部分内容基于！&lt;a href=&quot;https://nbdev.fast.ai/tutorial/&quot;&gt;nbdev教程&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;安装&quot;&gt;安装&lt;/h1&gt;
&lt;p&gt;  和安装其他python模块一样：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install nbdev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;设置repo&quot;&gt;设置Repo&lt;/h1&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;新建模板
  在登录自己github账号之后，点击nbdev template(https://github.com/fastai/nbdev_template/generate) 。会自动在自己git账号下产生一个Repo，包括一个符合nbdev的最小模板，你可以以此为起点，将该库clone到本地后进行代码编写。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;github pages
  该模板doc目录下是一个基于github的个人主页（项目文档）。要启用该主页，需到自己的github上打开刚才生成的Repo，在进入setting菜单，下列到Github Pages,设置”Source” to Master branch /docs选项，保存之后重新回到该选项，会有一个url就代表这个Repo的文档主页。这时可以回到Repo的页面，点击Edit，在Website输入框将刚才的url写入。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;修改-settingsinit&quot;&gt;修改 settings.init&lt;/h1&gt;
&lt;p&gt;  这个文件自动生成在跟目录下，包含了需要打包库文件的所有信息，去掉以下一些注释去掉(否则生成代码会报错):&lt;br /&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# lib_name = your_project_name
# user = your_github_username
# description = A description of your project
# keywords = some keywords
# author = Your Name
# author_email = email@example.com
# copyright = Your Name or Company Name
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;目录结构一个为根目录下有jupyter notebokk的文件；doc目录（刚才已经提到）；一个打包生产你python代码的目录。&lt;/p&gt;

&lt;h1 id=&quot;安装git-钩子&quot;&gt;安装git 钩子&lt;/h1&gt;
&lt;p&gt;  Jupyter Notebooks可能会引起版本冲突，可以在终端中cd到项目文件目录下执行:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;nbdev_install_git_hooks
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;  这样在commit时会去掉元数据(metadata)，以减少冲突的可能&lt;/p&gt;

&lt;h1 id=&quot;编辑-00_coreipynb&quot;&gt;编辑 00_core.ipynb&lt;/h1&gt;
&lt;p&gt;  编辑00_core.ipynb，你的代码可以从这个文件开始写&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# default_exp core
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;表示会产生 lib_name/core.py库&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#export
def say_hello(to):
    &quot;Say hello to somebody&quot;
    return f'Hello {to}!'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;定义一个函数，#export表示会包含在生成的库文件中。
之后你可以写一些说明文档，包括增加一个测试脚本：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;assert say_hello(&quot;Jeremy&quot;)==&quot;Hello Jeremy!&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;生成代码&quot;&gt;生成代码&lt;/h1&gt;
&lt;p&gt;  你可以在命令行中执行nbdev_build_lib，或者在notebook中执行：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from nbdev.export import *
notebook2script()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;结束之后，可以看到在lib_name目录下生成了相应的core.py文件&lt;/p&gt;

&lt;h1 id=&quot;编辑indexipynb&quot;&gt;编辑index.ipynb&lt;/h1&gt;
&lt;p&gt;  该文档会自动生成doc目录下的index.html。是整个项目的说明文档的起始页面。 帮助文件中的例子应该是notebook包括输出的cell。&lt;/p&gt;

&lt;h1 id=&quot;生成文档&quot;&gt;生成文档&lt;/h1&gt;
&lt;p&gt;  该只需简单的在命令行执行nbdev_build_docs，就能生成文档。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ nbdev_build_docs
converting: /home/jhoward/git/nbdev/nbs/00_core.ipynb
converting: /home/jhoward/git/nbdev/nbs/index.ipynb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;在doc目录下可以看到，00_core.ipynb对应生成core.html,index.ipynb对应生成index.html&lt;/p&gt;

&lt;h1 id=&quot;commit到github&quot;&gt;commit到github&lt;/h1&gt;
&lt;p&gt;  这时，你可以使用git commit/git push将修改提交到github。实际在测试时，因为修改过的文件没有加入跟踪，所以在用了git add -A命令之后，才将所有修改提交到github。
依托了github的最新Actions功能，push之后，github会自动执行测试等任务。&lt;br /&gt;
  GitHub Actions 的主要作用就是让用户能够在 GitHub 服务器上直接执行和测试代码，只需几个简单步骤就可以实现构建、共享和执行代码。其实现了所谓的CI持续集成（CONTINUOUS INTEGRATION）的功能。在持续集成环境中，开发人员将会频繁的提交代码到主干。这些新提交在最终合并到主线之前，都需要通过编译和自动化测试流进行验证，而这一切在github都是免费的。
  你可以进到github的项目首页，点击commit，将会看到每次提交的过程，包括测试是否成功等等。&lt;/p&gt;

&lt;h1 id=&quot;参考&quot;&gt;参考&lt;/h1&gt;
&lt;p&gt;nbdev官方网站：[https://nbdev.fast.ai/ ]https://nbdev.fast.ai/&lt;br /&gt;
nbdev: use Jupyter Notebooks for everything:&lt;a href=&quot;https://www.fast.ai/2019/12/02/nbdev/&quot;&gt;https://www.fast.ai/2019/12/02/nbdev/&lt;/a&gt;&lt;br /&gt;
中文版：&lt;a href=&quot;https://tech.sina.com.cn/roll/2020-02-03/doc-iimxxste8470190.shtml&quot;&gt;https://tech.sina.com.cn/roll/2020-02-03/doc-iimxxste8470190.shtml&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;
</description>
				<pubDate>Wed, 05 Feb 2020 00:15:00 +0000</pubDate>
				<link>https://breezecloud.github.io/2020/02/nbdev-tutorial</link>
				<guid isPermaLink="true">https://breezecloud.github.io/2020/02/nbdev-tutorial</guid>
			</item>
		
			<item>
				<title>flask-restful用户指南-快速入门（摘自官方文档）</title>
				<description>&lt;h1 id=&quot;什么是flask-restful&quot;&gt;什么是Flask-RESTful&lt;/h1&gt;
&lt;p&gt;  是对Flask的扩展，它增加了对快速构建RESTAPI的支持。它是一个轻量级的抽象，可以与现有的ORM/库一起工作。Flask-RESTful鼓励最佳实践与最低限度的设置。如果你熟悉Flask，Flask-RESTful应该很容易使用。
官方文档位于（ https://flask-restful.readthedocs.io/en/latest/ ）&lt;/p&gt;

&lt;h1 id=&quot;安装&quot;&gt;安装&lt;/h1&gt;
&lt;p&gt;  pip安装&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install flask-restful需要Flask 0.10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;  flask-restful需要Flask 0.10以上，Python version 2.7, 3.4, 3.5, 3.6 or 3.7&lt;/p&gt;

&lt;h1 id=&quot;快速入门&quot;&gt;快速入门&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;最小的API
```
from flask import Flask
from flask_restful import Resource, Api&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;app = Flask(&lt;strong&gt;name&lt;/strong&gt;)
api = Api(app)&lt;/p&gt;

&lt;p&gt;class HelloWorld(Resource):
    def get(self):
        return {‘hello’: ‘world’}&lt;/p&gt;

&lt;p&gt;api.add_resource(HelloWorld, ‘/’)&lt;/p&gt;

&lt;p&gt;if &lt;strong&gt;name&lt;/strong&gt; == ‘&lt;strong&gt;main&lt;/strong&gt;’:
    app.run(debug=True)&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;保存为api.py之后运行：
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;$ python api.py&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Running on http://127.0.0.1:5000/&lt;/li&gt;
  &lt;li&gt;Restarting with reloader
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;
然后在命令行下执行：&lt;/code&gt;$ curl http://127.0.0.1:5000/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;来验证是否输出&lt;/code&gt;{“hello”: “world”}```&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;资源路由&quot;&gt;资源路由&lt;/h1&gt;
&lt;p&gt;  flask-RESTful提供的主要构件是资源。资源构建在Flask可插拔视图(http://flask.pocoo.org/docs/views/) 之上，只需在资源上定义方法，就可以方便地访问多个HTTP方法。todo应用程序的基本CRUD资源(当然)如下所示：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from flask import Flask, request
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)

todos = {}

class TodoSimple(Resource):
    def get(self, todo_id):
        return {todo_id: todos[todo_id]}

    def put(self, todo_id):
        todos[todo_id] = request.form['data']
        return {todo_id: todos[todo_id]}

api.add_resource(TodoSimple, '/&amp;lt;string:todo_id&amp;gt;')

if __name__ == '__main__':
    app.run(debug=True)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;你可以这样试一试：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ curl http://localhost:5000/todo1 -d &quot;data=Remember the milk&quot; -X PUT
{&quot;todo1&quot;: &quot;Remember the milk&quot;}
$ curl http://localhost:5000/todo1
{&quot;todo1&quot;: &quot;Remember the milk&quot;}
$ curl http://localhost:5000/todo2 -d &quot;data=Change my brakepads&quot; -X PUT
{&quot;todo2&quot;: &quot;Change my brakepads&quot;}
$ curl http://localhost:5000/todo2
{&quot;todo2&quot;: &quot;Change my brakepads&quot;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;如果安装了requests库，也可以从python执行：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from requests import put, get
&amp;gt;&amp;gt;&amp;gt; put('http://localhost:5000/todo1', data={'data': 'Remember the milk'}).json()
{u'todo1': u'Remember the milk'}
&amp;gt;&amp;gt;&amp;gt; get('http://localhost:5000/todo1').json()
{u'todo1': u'Remember the milk'}
&amp;gt;&amp;gt;&amp;gt; put('http://localhost:5000/todo2', data={'data': 'Change my brakepads'}).json()
{u'todo2': u'Change my brakepads'}
&amp;gt;&amp;gt;&amp;gt; get('http://localhost:5000/todo2').json()
{u'todo2': u'Change my brakepads'}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;flask-RESTful从视图方法中理解多种类型的返回值。类似于Flask，您可以返回任何可迭代的包括原始的flask响应对象,并且它将被转换为response。flask-RESTful还支持使用多个返回值设置响应代码和响应头，如下所示：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Todo1(Resource):
    def get(self):
        # Default to 200 OK
        return {'task': 'Hello world'}

class Todo2(Resource):
    def get(self):
        # Set the response code to 201
        return {'task': 'Hello world'}, 201

class Todo3(Resource):
    def get(self):
        # Set the response code to 201 and return custom headers
        return {'task': 'Hello world'}, 201, {'Etag': 'some-opaque-string'}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;endpoints&quot;&gt;Endpoints&lt;/h1&gt;
&lt;p&gt;  很多时候，在API中，您的资源将有多个URL。可以将多个URL传递给Api对象上的add_resources()方法。每一个都会被路由到你的资源。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;api.add_resource(HelloWorld,
    '/',
    '/hello')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;可以将路径的部分作为变量匹配到资源方法中。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;api.add_resource(Todo,
    '/todo/&amp;lt;int:todo_id&amp;gt;', endpoint='todo_ep')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;参数语法&quot;&gt;参数语法&lt;/h1&gt;
&lt;p&gt;  虽然Flask提供了对请求数据（即querystring或表单后编码数据）的简单访问，但验证表单数据仍然是一个难题。Flask RESTful内置了对使用类似于argparse的库进行请求数据验证的支持。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from flask_restful import reqparse

parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate to charge for this resource')
args = parser.parse_args()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;注意：
与argparse模块不同，reqparse.RequestParser.parse_args）返回一个Python字典，而不是自定义数据结构。&lt;/p&gt;

&lt;p&gt;使用reqparse模块还可以免费提供正常的错误消息。如果参数未能通过验证，Flask RESTful将响应400个错误请求和一个突出显示错误的响应。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ curl -d 'rate=foo' http://127.0.0.1:5000/todos
{'status': 400, 'message': 'foo cannot be converted to int'}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;inputs模块提供了许多包含的公共转换函数，如inputs.date()和inputs.url()。&lt;/p&gt;

&lt;p&gt;使用strict=True调用parse_args可确保在请求包含解析器未定义的参数时抛出错误。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;args = parser.parse_args(strict=True)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;数据格式&quot;&gt;数据格式&lt;/h1&gt;
&lt;p&gt;  默认情况下，返回的可迭代中的所有字段都将按原样呈现。虽然这在处理Python数据结构时非常有效，但在处理对象时可能会变得非常令人沮丧。为了解决这个问题，Flask RESTful提供了fields模块和marshal_with（）装饰。与Django ORM和WTForm类似，您使用fields模块来描述你响应的结构。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from flask_restful import fields, marshal_with

resource_fields = {
    'task':   fields.String,
    'uri':    fields.Url('todo_ep')
}

class TodoDao(object):
    def __init__(self, todo_id, task):
        self.todo_id = todo_id
        self.task = task

        # This field will not be sent in the response
        self.status = 'active'

class Todo(Resource):
    @marshal_with(resource_fields)
    def get(self, **kwargs):
        return TodoDao(todo_id='my_todo', task='Remember the milk')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;上面的示例接受一个python对象并准备将其序列化。marshal_with（）装饰将应用于resource_fields字段描述的转换。从对象中提取的唯一字段是task。fields.Url字段是一个特殊的字段，它接受一个endpoint名称，并在响应中为该端点生成一个Url。您需要的许多字段类型已经包括在内。有关完整列表，请参阅“fields”指南。&lt;/p&gt;

&lt;h1 id=&quot;完整示例&quot;&gt;完整示例&lt;/h1&gt;
&lt;p&gt;将此示例保存在api.py中&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from flask import Flask
from flask_restful import reqparse, abort, Api, Resource

app = Flask(__name__)
api = Api(app)

TODOS = {
    'todo1': {'task': 'build an API'},
    'todo2': {'task': '?????'},
    'todo3': {'task': 'profit!'},
}


def abort_if_todo_doesnt_exist(todo_id):
    if todo_id not in TODOS:
        abort(404, message=&quot;Todo {} doesn't exist&quot;.format(todo_id))

parser = reqparse.RequestParser()
parser.add_argument('task')


# Todo
# shows a single todo item and lets you delete a todo item
class Todo(Resource):
    def get(self, todo_id):
        abort_if_todo_doesnt_exist(todo_id)
        return TODOS[todo_id]

    def delete(self, todo_id):
        abort_if_todo_doesnt_exist(todo_id)
        del TODOS[todo_id]
        return '', 204

    def put(self, todo_id):
        args = parser.parse_args()
        task = {'task': args['task']}
        TODOS[todo_id] = task
        return task, 201


# TodoList
# shows a list of all todos, and lets you POST to add new tasks
class TodoList(Resource):
    def get(self):
        return TODOS

    def post(self):
        args = parser.parse_args()
        todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
        todo_id = 'todo%i' % todo_id
        TODOS[todo_id] = {'task': args['task']}
        return TODOS[todo_id], 201

##
## Actually setup the Api resource routing here
##
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/&amp;lt;todo_id&amp;gt;')


if __name__ == '__main__':
    app.run(debug=True)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;示例用法&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ python api.py
 * Running on http://127.0.0.1:5000/
 * Restarting with reloader
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;获取列表&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ curl http://localhost:5000/todos
{&quot;todo1&quot;: {&quot;task&quot;: &quot;build an API&quot;}, &quot;todo3&quot;: {&quot;task&quot;: &quot;profit!&quot;}, &quot;todo2&quot;: {&quot;task&quot;: &quot;?????&quot;}}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;获取单个任务&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ curl http://localhost:5000/todos/todo3
{&quot;task&quot;: &quot;profit!&quot;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;删除任务&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ curl http://localhost:5000/todos/todo2 -X DELETE -v

&amp;gt; DELETE /todos/todo2 HTTP/1.1
&amp;gt; User-Agent: curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3
&amp;gt; Host: localhost:5000
&amp;gt; Accept: */*
&amp;gt;
* HTTP 1.0, assume close after body
&amp;lt; HTTP/1.0 204 NO CONTENT
&amp;lt; Content-Type: application/json
&amp;lt; Content-Length: 0
&amp;lt; Server: Werkzeug/0.8.3 Python/2.7.2
&amp;lt; Date: Mon, 01 Oct 2012 22:10:32 GMT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;增加一个新任务&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ curl http://localhost:5000/todos -d &quot;task=something new&quot; -X POST -v

&amp;gt; POST /todos HTTP/1.1
&amp;gt; User-Agent: curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3
&amp;gt; Host: localhost:5000
&amp;gt; Accept: */*
&amp;gt; Content-Length: 18
&amp;gt; Content-Type: application/x-www-form-urlencoded
&amp;gt;
* HTTP 1.0, assume close after body
&amp;lt; HTTP/1.0 201 CREATED
&amp;lt; Content-Type: application/json
&amp;lt; Content-Length: 25
&amp;lt; Server: Werkzeug/0.8.3 Python/2.7.2
&amp;lt; Date: Mon, 01 Oct 2012 22:12:58 GMT
&amp;lt;
* Closing connection #0
{&quot;task&quot;: &quot;something new&quot;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;更新一个任务&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ curl http://localhost:5000/todos/todo3 -d &quot;task=something different&quot; -X PUT -v

&amp;gt; PUT /todos/todo3 HTTP/1.1
&amp;gt; Host: localhost:5000
&amp;gt; Accept: */*
&amp;gt; Content-Length: 20
&amp;gt; Content-Type: application/x-www-form-urlencoded
&amp;gt;
* HTTP 1.0, assume close after body
&amp;lt; HTTP/1.0 201 CREATED
&amp;lt; Content-Type: application/json
&amp;lt; Content-Length: 27
&amp;lt; Server: Werkzeug/0.8.3 Python/2.7.3
&amp;lt; Date: Mon, 01 Oct 2012 22:13:00 GMT
&amp;lt;
* Closing connection #0
{&quot;task&quot;: &quot;something different&quot;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
				<pubDate>Sun, 01 Sep 2019 00:15:00 +0000</pubDate>
				<link>https://breezecloud.github.io/2019/09/flask-restful-doc</link>
				<guid isPermaLink="true">https://breezecloud.github.io/2019/09/flask-restful-doc</guid>
			</item>
		
			<item>
				<title>python属性查找（attribute lookup）是怎么回事？</title>
				<description>&lt;h1 id=&quot;python属性查找attribute-lookup是怎么回事&quot;&gt;python属性查找（attribute lookup）是怎么回事？&lt;/h1&gt;
&lt;p&gt;学习python，绕不过学习python的属性查找问题。原来引用python的属性不是简单的该对象的属性值，它有一定的规则来判断到底取那个对象的属性或方法的值，涉及到描述符（descriptor）和装饰器（Decorators）方面的知识。但具体什么规则，查了好多网上文章，虽然说法不错，但因为写的角度不同，总觉得没说清楚。经过实验，终于搞清楚了背后的机制。&lt;br /&gt;
以下内容转至：https://www.cnblogs.com/Jimmy1988/p/6808237.html&lt;br /&gt;
在Python中，属性查找（attribute lookup）是比较复杂的，特别是涉及到描述符descriptor的时候。首先，我们知道：python中一切都是对象，“everything is object”，包括类，类的实例，数字，模块任何object都是类（class or type）的实例（instance）如果一个descriptor只实现了__get__方法，我们称之为non-data descriptor， 如果同时实现了__get__ &lt;strong&gt;set__我们称之为data descriptor。按照python doc，如果obj是某个类的实例，那么obj.name首先调用__getattribute&lt;/strong&gt;。如果类定义了__getattr__方法，那么在__getattribute__抛出 AttributeError 的时候就会调用到__getattr__，而对于描述符(&lt;strong&gt;get&lt;/strong&gt;）的调用，则是发生在__getattribute__内部的。&lt;br /&gt;
obj = Clz(), 那么obj.attr 顺序如下：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        （1）如果“attr”是出现在Clz或其基类的__dict__中， 且attr是data descriptor， 那么调用其__get__方法, 否则
        （2）如果“attr”出现在obj的__dict__中， 那么直接返回 obj.__dict__['attr']， 否则
        （3）如果“attr”出现在Clz或其基类的__dict__中
            （3.1）如果attr是non-data descriptor，那么调用其__get__方法， 否则
            （3.2）返回 __dict__['attr']
        （4）如果Clz有__getattr__方法，调用__getattr__方法，否则
        （5）抛出AttributeError 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;　　下面是测试代码： ```    
#coding=utf-8
class DataDescriptor(object):
    def __init__(self, init_value):
        self.value = init_value
  
    def __get__(self, instance, typ):
        return 'DataDescriptor __get__'
  
    def __set__(self, instance, value):
        print ('DataDescriptor __set__')
        self.value = value
 
class NonDataDescriptor(object):
    def __init__(self, init_value):
        self.value = init_value

    def __get__(self, instance, typ):
        return('NonDataDescriptor __get__')
 
class Base(object):
    dd_base = DataDescriptor(0)
    ndd_base = NonDataDescriptor(0)

 
class Derive(Base):
    dd_derive = DataDescriptor(0)
    ndd_derive = NonDataDescriptor(0)
    same_name_attr = 'attr in class'
 
    def __init__(self):
        self.not_des_attr = 'I am not descriptor attr'
        self.same_name_attr = 'attr in object'
 
    def __getattr__(self, key):
        return '__getattr__ with key %s' % key
 
    def change_attr(self):
        self.__dict__['dd_base'] = 'dd_base now in object dict '
        self.__dict__['ndd_derive'] = 'ndd_derive now in object dict '
 
def main():
    b = Base()
    d = Derive()
    print('Derive object dict', d.__dict__)
    assert d.dd_base == &quot;DataDescriptor __get__&quot;
    assert d.ndd_derive == 'NonDataDescriptor __get__'
    assert d.not_des_attr == 'I am not descriptor attr'
    assert d.no_exists_key == '__getattr__ with key no_exists_key'
    assert d.same_name_attr == 'attr in object'
    d.change_attr()
    print('Derive object dict', d.__dict__)
    assert d.dd_base != 'dd_base now in object dict '
    assert d.ndd_derive == 'ndd_derive now in object dict '

    try:
        b.no_exists_key
    except Exception, e:
        assert isinstance(e, AttributeError)
 
if __name__ == '__main__':
    main() ```
    调用change_attr方法之后，dd_base既出现在类的__dict__（作为data descriptor）, 也出现在实例的__dict__， 因为attribute lookup的循序，所以优先返回的还是Clz.__dict__['dd_base']。而ndd_base虽然出现在类的__dict__， 但是因为是nondata descriptor，所以优先返回obj.__dict__['dd_base']。其他：line48,line56表明了__getattr__的作用。line49表明obj.__dict__优先于Clz.__dict__
    前面提到过，类也是对象，类是元类（metaclass）的实例，所以类属性的查找顺序基本同上，区别在于第二步，由于Clz可能有基类，所以是在Clz及其基类的__dict__查找“attr&quot;&amp;lt;br&amp;gt;
    补充说明：很多网上说属性最高优先级是__getattribute__。但一般我们不会去覆盖__getattribute__，因为一旦覆盖了__getattribute__，所有后面的规则都失效了，所有要理解上面提到的&quot;生在__getattribute__内部&quot;。&amp;lt;br&amp;gt;
   再看以下代码： ```       
import functools, time
class cached_property(object):
    &quot;&quot;&quot; A property that is only computed once per instance and then replaces
        itself with an ordinary attribute. Deleting the attribute resets the
        property. &quot;&quot;&quot;

    def __init__(self, func):
        functools.update_wrapper(self, func)
        self.func = func

    def __get__(self, obj, cls):
        if obj is None: return self
        value = obj.__dict__[self.func.__name__] = self.func(obj)
        return value

class TestClz(object):
    @cached_property
    def complex_calc(self):
        print('very complex_calc')
        return sum(range(100))

if __name__=='__main__':
    t = TestClz()
    print('&amp;gt;&amp;gt;&amp;gt; first call')
    print(t.complex_calc)
    print('&amp;gt;&amp;gt;&amp;gt; second call')
    print(t.complex_calc) ```
cached_property是一个non-data descriptor。在TestClz中，用cached_property装饰方法complex_calc，返回值是一个descriptor实例，所以在调用的时候没有使用小括号。第一次调用t.complex_calc之前，obj(t)的__dict__中没有”complex_calc“， 根据查找顺序第三条，执行cached_property.__get__, 这个函数代用缓存的complex_calc函数计算出结果，并且把结果放入obj.__dict__。那么第二次访问t.complex_calc的时候，根据查找顺序，第二条有限于第三条，所以就直接返回obj.__dict__['complex_calc']。&amp;lt;br&amp;gt; 再举一个网上常见的例子： ```
class NotNegative():
    def __init__(self,name):
        self.name = name

    def __set__(self, instance, value):
        if value &amp;lt; 0:
            raise ValueError(self.name+' must be &amp;gt;= 0')
        else:
            instance.__dict__[self.name] = value

class Product():
    quantity = NotNegative('quantity')
    price = NotNegative('price')

    def __init__(self,name,quantity,price):
        self.name = name
        self.quantity = quantity
        self.price = price

book = Product('mybook',2,5) ```
    通过描述符来限制设置quantity和price必须是非负。在该例子中，如果执行book.quantity=3,解释器会先查找实例属性，发现有quantity属性，但是解释器又发现同样有一个类属性是描述符，于是解释器最终会选择走描述符这条路。然后因为是描述符，于是会执行描述符中的set特殊方法。&amp;lt;br&amp;gt;

    描述符中的set特殊方法的参数有为&amp;lt;br&amp;gt; ```        
    self ：是描述符实例
    instance ：是相当于例子中的实例book
    value ：就是要赋予的值 ``` get方法同样有3个参数self, instance, owner。self，instance与set中的相同，owner为例子中的Product类&amp;lt;br&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;python的描述符和装饰器功能强大，用好了极大地增强了代码的复用性和可读性，但同时也是比较难学的知识点，多看看别人的代码可以帮助我们更快的理解。&lt;/p&gt;
</description>
				<pubDate>Sun, 01 Sep 2019 00:15:00 +0000</pubDate>
				<link>https://breezecloud.github.io/2019/09/python-descriptor</link>
				<guid isPermaLink="true">https://breezecloud.github.io/2019/09/python-descriptor</guid>
			</item>
		
			<item>
				<title>Odoo加载机制流程源码解析</title>
				<description>&lt;h1 id=&quot;odoo加载机制流程源码解析&quot;&gt;“Odoo加载机制流程源码解析&lt;/h1&gt;
&lt;p&gt;本文参考主要内容参考！[]https://www.cnblogs.com/dancesir/p/7008852.html
分析的是odoo12的源码，加了一些自己的理解，特别是关于model注册到registry部分内容，查遍整个互联网也没看到详细解释，为自己研究和原创。模块加载外层就是封装一个Registry(Mapping)对象:实际是一个字典，它包含对应的db，model等映射关系，一个DB对应一个Registry。后续的操作都会围绕这个Registry进行，将相关的数据赋值给相应的属性项。modules/registry.py定义了Registry类，Registry类的__new__函数调用new()函数产生一个实例，new()调用odoo.modules.load_modules()函数装载模块（位于modules/loading.py:321)。其中的核心程序为load_modules函数。函数经过如下步骤完成了模块（module）和模型（model）的加载。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;初始化数据库（初次运行）
调用odoo.modules.db.initialize(cr)(modules/db.py:17)完成加载base模块下的base.sql文件并执行。
此时数据库表为(具体见base.sql)：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CREATE TABLE ir_actions (
...
CREATE TABLE ir_model (
...
CREATE TABLE ir_model_fields (
...
CREATE TABLE res_lang (
...
CREATE TABLE res_users (
...
create table wkf (
...
CREATE TABLE ir_module_category (
...
CREATE TABLE ir_module_module (
...
CREATE TABLE ir_module_module_dependency (
...
CREATE TABLE ir_model_data (
...
CREATE TABLE ir_model_constraint (
...
CREATE TABLE ir_model_relation (
...  
CREATE TABLE res_currency (
...
CREATE TABLE res_company (
...
CREATE TABLE res_partner (
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;这20张表是odoo系统级的，它是模块加载及系统运行的基础。后续模块生成的表及相关数据都可以在这20张中找到蛛丝马迹。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;数据库表初始化后，就可以加载模块数据（addons）到数据库了，这个也是odoo作为平台灵活的原因，所有的数据都在数据库。
找到addons-path下所有的模块,然后一个一个的加载到数据库中。
先完成如下操作odoo.modules.db.initialize()(modules/db.py:336)：
    &lt;ul&gt;
      &lt;li&gt;将模块信息写入ir_module_module表：&lt;/li&gt;
      &lt;li&gt;将module信息写入ir_model_data表：
一个module要写两次ir_model_data表，&lt;/li&gt;
      &lt;li&gt;写module的dependency表：根据依赖关系进行判断，递归更新那些需要auto_install的模块状态为“to install”。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;到目前为止，模块的加载都是在数据库级别，只是将“模块文件”信息存入数据库表，但是还没有真正加载到程序中。
Odoo运行时查找object是通过Registry.get()获取的，而不是通过python自己的机制来找到相应的object，所以odoo在最初import模块时(cli/command.py:53)会把模块下包含的model全部注册到models.py的MetaModel.module_to_models类变量(注意此时仅仅登记了model的类，并未完成实例化)。
下面的步骤就是加载模块到内存（实例化）。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;加载base模块
graph.add_module()(:352)定义在modules/graph.py中两个类Graph,Node。Graph是一个存放Node的类，Node代表一个模块，Graph表示了模块之间的关系。其depth代表深度0，代表没有依赖模块，children代表依赖他的模块，所以Node的graph指向Graph类。
Graph的add_modules方法使得一组module和其关系信息全部导入Graph，当一个模块的所有依赖模块已经在Graph里，则简单的加入该模块（add_node），如果不满足，则加入later并且将模块放到列表最后，如此循环，直到packages列表空或者later列表里的模块已经超过等待导入的模块。注意info保存了模块所有信息的字典，通过load_information_from_description_file()导入文件__manifest__.py的内容。&lt;br /&gt;。
之后调用module/loading.py(:360)的load_module_graph()方法加载模块，最终执行加载的方法。这个方法是odoo加载model的核心，通过 __import__方法加载模块，这个是python的机制，当import到某个继承了BaseModel类的class时，它的实例化将有别于python自身的实例化操作，后者说它根本不会通过python自身的__new__方法创建实例，所有的实例创建都是通过实例化Registry对象在执行new函数是调用load_modules(registry.py:86)实现模型的实例化。load_modules(loading.py:321) _build_model 方法及元类创建。通过这种方式实例化model就可以解决我们在xml中配置model时指定的继承，字段，约束等各种属性。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;标记需要加载或者更新的模块（db）&lt;/li&gt;
  &lt;li&gt;加载被标记的模块（加载过程与加载base模块一致）&lt;/li&gt;
  &lt;li&gt;完成及清理安装&lt;/li&gt;
  &lt;li&gt;清理菜单&lt;/li&gt;
  &lt;li&gt;删除卸载的模块&lt;/li&gt;
  &lt;li&gt;核实model的view&lt;/li&gt;
  &lt;li&gt;运行post-install测试&lt;/li&gt;
&lt;/ol&gt;
</description>
				<pubDate>Fri, 12 Jul 2019 00:15:00 +0000</pubDate>
				<link>https://breezecloud.github.io/2019/07/odoo-model-load</link>
				<guid isPermaLink="true">https://breezecloud.github.io/2019/07/odoo-model-load</guid>
			</item>
		
			<item>
				<title>odoo设置nginx的反向代理及ssl</title>
				<description>&lt;h1 id=&quot;odoo设置nginx反向代理及ssl&quot;&gt;odoo设置nginx反向代理及ssl&lt;/h1&gt;
&lt;p&gt;本文参考：Odoo.11.Development.Cookbook.2nd.Edition一书，也可以参考https://alanhou.org/odoo12-deployment/ 及官方文档
https://www.odoo.com/documentation/12.0/setup/deploy.html&lt;br /&gt;
假设已经安装好odoo和nginx，并且申请了CA证书（别忘了设置证书自动更新）。配置以odoo.example.com为例。关于证书的安装，可以参考我前面的文章。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;作为root，建立文件 /etc/nginx/sites-available/odoo-80:
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;server { 
 listen [::]:80 ipv6only=off; 
 server_name odoo.example.com; 
 access_log /var/log/nginx/odoo80.access.log combined; 
 error_log /var/log/nginx/odoo80.error.log; 
 location / { 
     rewrite ^/(.*) https://odoo.example.com:443/$1 permanent; 
 } 
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;建立配置文件 /etc/nginx/sites-available/odoo-443:
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;server { 
 listen [::]:443 ipv6only=off; 
 server_name odoo.example.com; 
 ssl on; 
 ssl_certificate /etc/letsencrypt/live/odoo.example.com/fullchain.pem; 
 ssl_certificate_key /etc/letsencrypt/live/odoo.example.com/privkey.pem; 
 access_log /var/log/nginx/odoo443.access.log combined; 
 error_log /var/log/nginx/odoo443.error.log; 
 client_max_body_size 128M; 
 gzip on; 
 proxy_read_timeout 600s; 
 index index.html index.htm index.php; 
 add_header Strict-Transport-Security &quot;max-age=31536000&quot;;
	
 proxy_set_header Host $http_host; 
 proxy_set_header X-Real-IP $remote_addr; 
 proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; 
 proxy_set_header X-Forwarded-Proto https; 
 proxy_set_header X-Forwarded-Host $http_host; 
 location / { 
     proxy_pass http://localhost:8069; 
     proxy_read_timeout 6h; 
     proxy_connect_timeout 5s; 
     proxy_redirect http://$http_host/ https://$host:$server_port/; 
     add_header X-Static no; 
     proxy_buffer_size 64k; 
     proxy_buffering off; 
     proxy_buffers 4 64k; 
     proxy_busy_buffers_size 64k; 
     proxy_intercept_errors on; 
 } 
 location /longpolling/ { 
     proxy_pass http://localhost:8072; 
 } 
 location ~ /[a-zA-Z0-9_-]*/static/ { 
     proxy_pass http://localhost:8069; 
     proxy_cache_valid 200 60m; 
     proxy_buffering on; 
     expires 864000; 
 } 
} 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;建立配置文件链接 /etc/nginx/sites-enabled/:
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# ln -s /etc/nginx/sites-available/odoo-80 /etc/nginx/sites-enabled/odoo-80 
# ln -s /etc/nginx/sites-available/odoo-443 /etc/nginx/sites-enabled/odoo-443 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;删除默认文件/etc/nginx/sites-enabled/default:
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# rm /etc/nginx/sites-enabled/default 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;编辑odoo的启动配置文件
修改proxy_mode = True&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;重启odoo和nginx，浏览http://odoo.example.com&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;以上为Cookbook上的内容，按照此方法配置好以后还存在问题，在discuss模块无法正常进行及时聊天。后发现odoo使用长轮询机制实现聊天，应该是会打开一个8072端口。nginx已经配置了8072端口的跳转，但发现8072端口根本没有启动（无法进行反向代理，但不用反向代理情况下可以正常使用discuss）。后百度找到解决办法，把odoo的配置文件里的workers = 0调整为workers = 1，再次启动odoo服务后用lsof -i命令，可以看到8072端口已经启动。&lt;/li&gt;
&lt;/ol&gt;

</description>
				<pubDate>Tue, 11 Jun 2019 00:15:00 +0000</pubDate>
				<link>https://breezecloud.github.io/2019/06/odoo-config-ssl</link>
				<guid isPermaLink="true">https://breezecloud.github.io/2019/06/odoo-config-ssl</guid>
			</item>
		
			<item>
				<title>申请Let’s Encrypt免费证书实践</title>
				<description>&lt;h1 id=&quot;1什么是ssl和ca证书&quot;&gt;1.什么是SSL和CA证书&lt;/h1&gt;
&lt;p&gt;  互联网的http协议是通过明文传输的，很容易造成信息的泄漏。为了在网上实现信息安全，Netscape开发了ssl协议(https)用以保障在Internet上数据传输之安全。&lt;br /&gt;
CA证书顾名思义就是由CA（Certification Authority）机构发布的数字证书。要对CA证书完全理解及其作用，首先要理解SSL。SSL（security sockets layer，安全套接层）是为网络通信提供安全及数据完整性的一种安全协议。SSL3.0版本以后又被称为TLS。&lt;br /&gt;
SSL如何保证网络通信的安全和数据的完整性呢？就是采用了两种手段：身份认证和数据加密。首先身份认证就需要用到CA证书了，解决了”我是谁?”的问题。  &lt;br /&gt;
证书分三种:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;域名型https证书（DVSSL）:信任等级一般，只需验证网站的真实性便可颁发证书保护网站；&lt;/li&gt;
  &lt;li&gt;企业型https证书（OVSSL）:信任等级高，须要验证企业的身份，审核严格，安全性更强；&lt;/li&gt;
  &lt;li&gt;增强型https证书（EVSSL）:信任等级最高，一般用于银行证券等金融机构，审核严格，安全性最好，同时可以激活绿色网址栏&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;2ssl协议的建立和传输&quot;&gt;2.SSL协议的建立和传输&lt;/h1&gt;
&lt;p&gt;  需要理解SSL的加密机制，在使用SSL的网络通讯过程中，消息在请求和响应中都是加密传送的。首先要知道加密算法分为两种：对称加密和非对称加密。对称加密就是发送双发使用相同的密钥对消息进行加解密，常见的对称加密为DES、3DES,AES等。非对称加密是发送双方各自拥有一对公钥私钥，其中公钥是公开的，私钥是保密的。当发送方向接收方发送消息时，发送方利用接收方的公钥对消息进行加密，接收方收到消息后，利用自己的私钥解密就能得到消息的明文。其中非对称加密方法有RSA、Elgamal、ECC等。由于非对称加密的速度比较慢，所以它一般用于密钥交换，双方通过公钥算法协商出一份密钥，然后通过对称加密来通信。  &lt;br /&gt;
  密钥协商过程作为一种互联网安全加密技术，原理较为复杂，枯燥而无味。这里我们用个形象的比喻，我们假设A与B通信，A是SSL客户端，B是SSL服务器端，加密后的消息放在方括号[]里，以突出明文消息的区别。双方的处理动作的说明用圆括号（）括起。&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A：我想和你安全的通话，我这里的对称加密算法有DES,RC5,密钥交换算法有RSA和DH，摘要算法有MD5和SHA。&lt;/li&gt;
  &lt;li&gt;B：我们用DES－RSA－SHA这对组合好了。
这是我的证书，里面有我的名字和公钥，你拿去验证一下我的身份（把证书发给A）。
目前没有别的可说的了。&lt;/li&gt;
  &lt;li&gt;A：（查看证书上B的名字是否无误，并通过手头早已有的CA的证书验证了B的证书的真实性，如果其中一项有误，发出警告并断开连接，这一步保证了B的公钥的真实性）
（产生一份秘密消息，这份秘密消息处理后将用作加密密钥，加密初始化向量（IV）和hmac的密钥。将这份秘密消息-协议中称为per_master_secret-用B的公钥加密，封装成称作ClientKeyExchange的消息。由于用了B的公钥，保证了第三方无法窃听）
我生成了一份秘密消息，并用你的公钥加密了，给你（把ClientKeyExchange发给B）
注意，下面我就要用加密的办法给你发消息了！
（将秘密消息进行处理，生成加密密钥，加密初始化向量和hmac的密钥）
[我说完了]&lt;/li&gt;
  &lt;li&gt;B：（用自己的私钥将ClientKeyExchange中的秘密消息解密出来，然后将秘密消息进行处理，生成加密密钥，加密初始化向量和hmac的密钥，这时双方已经安全的协商出一套加密办法了）
注意，我也要开始用加密的办法给你发消息了！
[我说完了]&lt;/li&gt;
  &lt;li&gt;A: [我的秘密是…]&lt;/li&gt;
  &lt;li&gt;B: [其它人不会听到的…]&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;3如何部署https网站&quot;&gt;3.如何部署HTTPS网站&lt;/h1&gt;
&lt;p&gt;  部署HTTPS网站的时候需要证书，证书由CA机构签发，大部分传统CA机构签发证书是需要收费的，这不利于推动HTTPS协议的使用。
&lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let’s Encrypt&lt;/a&gt; 也是一个CA机构，但这个CA机构是免费的！！！也就是说签发证书不需要任何费用。当然他的证书属于DVSSL。  &lt;br /&gt;
  Let’s Encrypt设计了一个ACME协议。那为什么要创建ACME协议呢，传统的CA机构是人工受理证书申请、证书更新、证书撤销，完全是手动处理的。而ACME协议规范化了证书申请、更新、撤销等流程，只要一个客户端实现了该协议的功能，通过客户端就可以向 Let’s Encrypt 申请证书，也就是说 Let’s Encrypt CA完全是自动化操作的。任何人都可以基于ACME协议实现一个客户端，官方推荐的客户端是&lt;a href=&quot;https://certbot.eff.org/&quot;&gt;Certbot&lt;/a&gt;。&lt;/p&gt;

&lt;h1 id=&quot;4certbot客户端安装和命令&quot;&gt;4.Certbot客户端安装和命令&lt;/h1&gt;
&lt;p&gt;  除非您有非常特别的要求，建议您使用您操作系统的包管理工具提供的Certbot包(参见certbot.eff.org)。如果这个包不可用，我们建议使用certbot-AUTO，它自动在您的系统上安装Certbot。Certbot客户端支持两种用于获取和安装证书的两种类型的插件：身份验证和安装程序。  &lt;br /&gt;
  份验证与certonly命令一起使用的插件，用于获取证书。身份验证验证您是否控制了请求证书的域，获得了指定域的证书，并将证书放置在计算机上的/etc/letscrypt目录中。身份验证器不安装证书(它不编辑服务器的任何配置文件以提供所获得的证书)。如果指定要进行身份验证的多个域，则所有域都将在单个证书中列出。要获得多个单独的证书，您需要多次运行Certbot。  &lt;br /&gt;
安装程序是与install命令一起使用的插件，用于安装证书。这些插件可以修改Web服务器的配置，使certbot获得的证书通过HTTPS为您的网站提供服务。官方有支持不同web服务器的列表  &lt;br /&gt;
比如debain以nginx为例的安装命令(其他环境参考官网)：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo apt-get install certbot python-certbot-nginx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;命令说明：  &lt;br /&gt;
不写任何子命令时，默认使用 run 命令。certbot-auto –nginx -d xx.com 等价于 certbot-auto run –nginx -d xx.com。
列表只涉及获取、安装、更新证书部分，完整的命令文档请参考官方用户手册。&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;(default) run   为当前服务器获取并安装一个证书&lt;/li&gt;
  &lt;li&gt;certonly        只获取或更新证书，不安装&lt;/li&gt;
  &lt;li&gt;renew           更新所有快要过期的证书&lt;/li&gt;
  &lt;li&gt;-d DOMAINS       指定要获取证书的域名，一个证书支持多个域名，用逗号分隔&lt;/li&gt;
  &lt;li&gt;–apache          使用Apache服务器插件来认证和安装证书&lt;/li&gt;
  &lt;li&gt;–standalone      certbot 会自己运行一个 web server 来进行验证&lt;/li&gt;
  &lt;li&gt;–nginx           使用 Nginx 服务器插件来认证和安装证书，可以读取 Nginx 配置文件并自动更改。&lt;/li&gt;
  &lt;li&gt;–webroot         会利用既有的 web server，在其 web root目录下创建隐藏文件， Let’s Encrypt 服务端会通过域名来访问这些隐藏文件，以确认你的确拥有对应域名的控制权&lt;/li&gt;
  &lt;li&gt;–manual          交互式获取证书，或者使用钩子脚本&lt;/li&gt;
  &lt;li&gt;-n               非交互式运行&lt;/li&gt;
  &lt;li&gt;–test-cert       从 staging server 获取一个测试证书&lt;/li&gt;
  &lt;li&gt;–dry-run         测试 “renew” 和 “certonly” 命令，不保存证书到磁盘上&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;5获取安装单域名证书&quot;&gt;5.获取安装单域名证书&lt;/h1&gt;
&lt;p&gt;  客户在申请Let’s Encrypt证书的时候，需要校验域名的所有权，证明操作者有权利为该域名申请证书，目前支持三种验证方式：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;dns-01：给域名添加一个 DNS TXT 记录。有时候我们想为内网的某台主机设置HTTPS，内网的主机无法被letsencrypt的服务器访问到，可以使用DNS记录来验证域名&lt;/li&gt;
  &lt;li&gt;http-01：在域名对应的 Web 服务器下放置一个 HTTP well-known URL 资源文件。&lt;/li&gt;
  &lt;li&gt;tls-sni-01：在域名对应的 Web 服务器下放置一个 HTTPS well-known URL 资源文件。
比如下命令获取证书(不安装)：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$./certbot certonly --standalone --email 58472399@qq.com -d quqianzhao.top -d www.quqianzhao.top
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;此处需要注意:&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;（1）执行此命令必须使用 root用户获得文件夹的权限&lt;/li&gt;
  &lt;li&gt;（2）域名能访问并且有绑定的公网IP&lt;/li&gt;
  &lt;li&gt;（3）必须在此域名绑定的服务器上运行&lt;/li&gt;
  &lt;li&gt;（4）会使用80断端口，如果nginx监听80端口，把nginx先关掉（nginx -s stop）
命令采用内置web server来验证给出的域名，并在/etc/letsencrypt/live/quqianzhao.top下生成此域名证书（不安装），目录下有4个文件就是生成的密钥证书文件。&lt;/li&gt;
  &lt;li&gt;cert.pem  - Apache服务器端证书&lt;/li&gt;
  &lt;li&gt;chain.pem  - Apache根证书和中继证书&lt;/li&gt;
  &lt;li&gt;fullchain.pem  - Nginx所需要ssl_certificate文件&lt;/li&gt;
  &lt;li&gt;privkey.pem - 安全证书KEY文件
如下命令获取证书并安装（通过nginx验证，并修改nginx配置）：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$./certbot --nginx --email 58472399@qq.com -d quqianzhao.top -d www.quqianzhao.top
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;此命令采用ngnix服务器验证并在/etc/nginx/site-enable/目录下生成default的配置文件（会自动导入nginx配置），注意此时要保持nginx运行。有时候服务器响应慢执行失败可以多试几次。
在执行之前可以用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;certbot delete -d quqianzhao.top&lt;/code&gt;删除已经安装的证书  &lt;br /&gt;
如果配置文件有问题，可以重新安装nginx：&lt;/p&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo apt-get --purge remove nginx
$ sudo apt-get --purge remove nginx-common
$ sudo apt-get install nginx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;6获取和安装通配符证书&quot;&gt;6.获取和安装通配符证书&lt;/h1&gt;
&lt;p&gt;  对于个人用户来说，主机并不是太多，使用单域名证书(证书仅仅包含一个主机)也没什么问题，但是对于大公司来子域名非常多，而且过一段时间可能就要使用一个新的主机。注册域也非常多。Let’s Encrypt 通配符证书通配符证书就是证书中可以包含一个通配符，比如.example.com、.example.cn，这样大型企业也可以使用通配符证书了，一张证书可以防护更多的主机了。这个功能可以说非常重要，从功能上看 Let’s Encrypt和传统CA机构没有什么区别了。  &lt;br /&gt;
而申请通配符证书，只能使用 dns-01 的方式进行验证  &lt;br /&gt;
申请证书命令：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./certbot certonly  -d *.newyingyong.cn --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;介绍下相关参数：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;certonly，表示安装模式，Certbot 有安装模式和验证模式两种类型的插件。&lt;/li&gt;
  &lt;li&gt;–manual  表示手动安装插件，Certbot 有很多插件，不同的插件都可以申请证书，用户可以根据需要自行选择&lt;/li&gt;
  &lt;li&gt;-d 为那些主机申请证书，如果是通配符，输入 *.newyingyong.cn（可以替换为你自己的域名）&lt;/li&gt;
  &lt;li&gt;–preferred-challenges dns，使用 DNS 方式校验域名所有权&lt;/li&gt;
  &lt;li&gt;–server，Let’s Encrypt ACME v2 版本使用的服务器,不同于 v1 版本，需要显示指定
安装过程中，有两个交互式的提示：是否同意 Let’s Encrypt协议要求询问是否对域名和机器（IP）进行绑定确认同意才能继续。继续查看命令行的输出，非常关键：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-------------------------------------------------------------------------------Please deploy a DNS TXT record under the 
name_acme-challenge.newyingyong.cnwiththe following value:2_8KBE_jXH8nYZ2unEViIbW52LhIqxkg6i9mcwsRvhQBefore continuing, verify the
 recordisdeployed.-------------------------------------------------------------------------------Press Enter to ContinueWaitingforverification...Cleaning up challenges
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;要求配置 DNS TXT 记录，从而校验域名所有权，也就是判断证书申请者是否有域名的所有权。上面输出要求给 _acme-challenge.newyingyong.cn  配置一条 TXT 记录，在没有确认 TXT 记录生效之前不要回车执行。然后进入你的DNS的提供方网站，增加txt记录，值为：2_8KBE_jXH8nYZ2unEViIbW52LhIqxkg6i9mcwsRvhQ  &lt;br /&gt;
然后输入下列命令确认 TXT 记录是否生效：&lt;/p&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo apt-get install dnsutils $debian 安装dig工具
$ dig -t txt  _acme-challenge.newyingyong.cn @8.8.8.8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;确认生效后，回车执行。恭喜您，证书申请成功，证书和密钥保存在下列目录：/etc/letsencrypt/live/newyingyong.cn  &lt;br /&gt;
注意:之前设置了一个CNAME域名，执行后会发生错误：Detail: CAA record for *.xxx.xxx prevents issuance的错误提示，删除CNAME记录就可以了。  &lt;br /&gt;
最后校验证书信息，输入如下命令：&lt;/p&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;openssl x509 -in /etc/letsencrypt/liev/newyingyong.cn/cert1.pem -noout -text
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;关键输出如下：X509v3SubjectAlternativeName:DNS:*.newyingyong.cn完美，证书包含了扩展的值就是 *.newyingyong.cn  &lt;br /&gt;
openssl的使用可以参考&lt;a href=&quot;https://www.cnblogs.com/yangxiaolan/p/6256838.html&quot;&gt;openssl用法详解&lt;/a&gt;  &lt;br /&gt;
修改nginx配置文件（/etc/nginx/nginx.conf）。  &lt;br /&gt;
最后将域名配置成https访问&lt;/p&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;server {
      listen 443 ssl http2;
      listen [::]:443 ssl http2 ;
      server_name maomao.run;

      ssl on;
      ssl_certificate /etc/letsencrypt/live/newyingyong.cn/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/newyingyong.cn/privkey.pem;
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
      ssl_session_cache shared:SSL:1m;
      ssl_session_timeout 10m;
      ssl_ciphers HIGH:!aNULL:!MD5;
      ssl_prefer_server_ciphers on;

      location / {
          proxy_pass http://127.0.0.1:8001;
      }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;将80端口重定向到443端口。用户无论如何输入域名，都将使用https访问。&lt;/p&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;server {
      listen 80;
      listen [::]:80;
      server_name maomao.run;
      rewrite ^(.*)$ https://${server_name}$1 permanent;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;设置后重启nginx，可以在&lt;a href=&quot;https://www.ssllabs.com/ssltest/&quot;&gt;SSL Labs&lt;/a&gt;输入域名进行验证测试。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;7证书的自动更新&quot;&gt;7.证书的自动更新&lt;/h1&gt;
&lt;p&gt;  证书有效期只有三个月，所以更新域名非常关键。更新操作其实和初次申请差不多，只是命令不同，参数都一样。所以智能的 Certbot 在初次申请完成以后就把所有参数都保存下来，方便更新的时候使用。更新的参数保存在 /etc/letsencrypt/renewal/newyingyong.cn。
既然参数都还在，那更新证书只需要一条简单的命令就可以了：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo certbot renew
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Linux 的定时任务有两种方法：cron 和 systemd/timers，任何一种都能实现定时的更新任务，可爱的 Certbot 两种都设置了，我们分别看看。  &lt;br /&gt;
*cron&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ vim /etc/cron.d/certbot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;打开文件可以看到 Certbot 设定的任务记录,如果使用Timers方式，则将这条规则注释到。
*Timers
Certbot 在 /lib/systemd/system/ 下生成了两个文件：certbot.service 和 certbot.timer，一个是服务，一个是定时器。
certbot.service:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[Unit]
Description=Certbot
Documentation=file:///usr/share/doc/python-certbot-doc/html/index.html
Documentation=https://letsencrypt.readthedocs.io/en/latest/
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot -q renew
PrivateTmp=true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;可以看到也是运行 certbot -q renew 命令。  &lt;br /&gt;
certbot.timer:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[Unit]
Description=Run certbot twice daily

[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=43200
Persistent=true

[Install]
WantedBy=timers.target
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;每天 0 点和 12 点激活 certbot.service。其实不需要这么频繁的更新证书，而且在更新证书的时候我们加了钩子来关闭和开启 Nginx 服务，所以最好是在凌晨没人访问网站的时候更新证书，我们稍微修改一下：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[Unit]
Description=Run certbot every 05:00

[Timer]
OnCalendar=*-*-* 05:00:00
Persistent=true

[Install]
WantedBy=timers.target
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;保存修改以后需要重启定时器：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ systemctl daemon-reload
$ systemctl restart certbot.timer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;至此就算大功告成了！&lt;/p&gt;

&lt;p&gt;参考1：&lt;a href=&quot;https://certbot.eff.org/docs/using.html&quot;&gt;certbot User Guide&lt;/a&gt;&lt;br /&gt;
参考2：&lt;a href=&quot;https://www.jianshu.com/p/da6803c5ab57&quot;&gt;https://www.jianshu.com/p/da6803c5ab57&lt;/a&gt;&lt;br /&gt;
参考3：&lt;a href=&quot;https://blog.csdn.net/weixin_41830501/article/details/81128968&quot;&gt;理解服务器证书 CA&amp;amp;SSL&lt;/a&gt;&lt;br /&gt;
参考4：&lt;a href=&quot;https://www.jianshu.com/p/87eb532a036f&quot;&gt;在CentOS7上使用Certbot申请Wildcard证书&lt;/a&gt;&lt;br /&gt;
参考5：&lt;a href=&quot;https://blog.csdn.net/kikajack/article/details/79122701&quot;&gt;Nginx 实现 HTTPS（基于 Let’s Encrypt 的免费证书）&lt;/a&gt;&lt;/p&gt;
</description>
				<pubDate>Fri, 24 May 2019 00:15:00 +0000</pubDate>
				<link>https://breezecloud.github.io/2019/05/ssl-and-ca</link>
				<guid isPermaLink="true">https://breezecloud.github.io/2019/05/ssl-and-ca</guid>
			</item>
		
			<item>
				<title>odoo-code-example</title>
				<description>&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#1&quot;&gt;(一)数据模型&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#2&quot;&gt;创建数据模型&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#3&quot;&gt;测试&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#4&quot;&gt;向模型添加字段&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#100&quot;&gt;(二)视图&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#101&quot;&gt;添加菜单&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#102&quot;&gt;创建窗体视图&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#200&quot;&gt;(三)业务逻辑&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#300&quot;&gt;(四)权限&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#301&quot;&gt;访问控制安全&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#302&quot;&gt;Row-level访问规则&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;一数据模型&quot;&gt;&lt;span id=&quot;1&quot;&gt;(一)数据模型&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;部分表名称&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;res_users 用户
res_groups 用户组(角色)
res_lang 语言
res_partner 供应商/客户/联系人
res_font 字体
res_company 公司
res_bank 银行
res_country 国家
res_country_state 州/省
res_currency 货币
res_currency_rate 汇率
ir_ui_menu 菜单 
ir_act_window 菜单动作
ir_act_window_view 菜单动作与视图的对应关系
ir_ui_view 视图
ir_cron 计划的动作
wkf 工作流
wkf_activity 活动
wkf_transition 迁移
sale_order 报价单/销售订单
sale_order_line 销售订单明细行
purchase_order 询价单/采购订单
purchase_order_line 采购订单明细行
product_category 产品分类
product_product 产品
product_template 产品信息
product_uom 计量单位
product_pricelist 价格表
product_pricelist_version 价格表版本
product_price_type 计价类型
stock_warehouse 仓库
stock_location 库位
stock_picking 分拣单(出库/入库/内部移动)
stock_move 库存移动(移库单)
stock_quant 库存分析
stock_inventory 库存盘点
stock_inventory_line 库存盘点明细
stock_production_lot 序列号(产品批次)
stock_warehouse_orderpoint 再订货规则
procurement_rule 补货规则(拉式流)
procurement_order 补货单
stock_location_route 路线
account_asset_category 固定资产类别
account_asset_asset 固定资产
account_account 会计科目
account_account_type 科目类型
account_tax 税
account_fiscalyear 会计年度
account_period 会计期间
account_invoice 发票
account_invoice_line 发票明细
account_move 会计凭证(会计分录)
account_move_line 会计凭证明细
account_voucher 记账凭证(收付款凭证)
account_voucher_line 记账明细
account_journal 凭证类型
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;span id=&quot;2&quot;&gt;创建数据模型&lt;/span&gt;
=========&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  # -*- coding: utf-8 -*- 
  from odoo import models, fields 
  class TodoTask(models.Model): 
   _name = 'todo.task' 
   _description = 'To-do Task'
   name = fields.Char('Description', required=True) 
   is_done = fields.Boolean('Done?') 
   active = fields.Boolean('Active?', default=True) 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;span id=&quot;3&quot;&gt;测试&lt;/span&gt;
=========&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# -*- coding: utf-8 -*-
from odoo.tests.common import TransactionCase
class TestTodo(TransactionCase):
def test_create(self):
    &quot;Create a simple Todo&quot;
    Todo = self.env['todo.task']
    task = Todo.create({'name': 'Test Task'})
    self.assertEqual(task.is_done, False)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;执行 $ ./odoo-bin -d todo -i todo_app –test-enable进行测试&lt;/p&gt;

&lt;p&gt;下面动作的测试函数&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   # def test_create(self):
        # ...
        # Test Toggle Done
        task.do_toggle_done()
        self.assertTrue(task.is_done)
        # Test Clear Done
        Todo.do_clear_done()
        self.assertFalse(task.active)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;测试安全访问权限&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  ＃class TestTodo（TransactionCase):
        def setUp（self，* args，** kwargs）：
            result = super（TestTodo，self）.setUp（* args，\
            ** kwargs）
            user_demo = self.env.ref（'base.user_demo'）
            self.env = self.env（user = user_demo）
            return result
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;向模型添加字段&quot;&gt;&lt;span id=&quot;4&quot;&gt;向模型添加字段&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;类继承&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# -*- coding: utf-8 -*-
  from odoo import models, fields, api
  class TodoTask(models.Model):
  _inherit = 'todo.task'
  user_id = fields.Many2one('res.users', 'Responsible')
  date_deadline = fields.Date('Deadline')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;接下来在model文件夹里面还需要创建一个__init__.py文件，代码如下：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#-*- coding:utf-8 -*-
#!/usr/bin/env python
from . import todo_task
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;通过继承方式也可以对父类的字段进行属性修改，它是通过向子类添加和父类具有相同名称的字段来完成的，但是只能对字段进行属性的设置。 举例,如果我们要给当前例子的父类的name field添加一个帮助信息,我们可以在子类的todo_task.py文件里面类的成员添加如下代码: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name = fields.Char(help=&quot;What needs to be done?&quot;)&lt;/code&gt; 注意：这个name是父类的属性&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;继承也在业务逻辑级别起作用。添加新方法很简单：只需在继承类中声明新的函数。&lt;br /&gt;
要扩展或更改现有逻辑，可以通过声明具有完全相同名称的方法来覆盖相应的方法。新方法将替换前一个方法，它也可以只是扩展继承类的代码，使用Python的super（）方法来调用父方法。然后，可以在调用super（）方法之前和之后，在原有逻辑周围添加新逻辑。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; from odoo.exceptions import ValidationError  
 # ...
 # class TodoTask(models.Model):
 # ...
 @api.one
 def do_toggle_done(self):
   if self.user_id != self.env.user:
     raise Exception('Only the responsible can do this!')
   else:
     return super(TodoTask, self).do_toggle_done()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;原型继承复制特征&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from odoo import models 
class TodoTask(models.Model): 
_name = 'todo.task' 
_inherit = 'mail.thread'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;使用委托继承嵌入模型&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from odoo import models, fields 
class User(models.Model): 
_name = 'res.users' 
_inherits = {'res.partner': 'partner_id'} 
partner_id = fields.Many2one('res.partner')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;请注意，使用委托继承，字段是继承的，但方法不是。&lt;br /&gt;&lt;/p&gt;

&lt;h1 id=&quot;一视图&quot;&gt;&lt;span id=&quot;100&quot;&gt;(一)视图&lt;/span&gt;&lt;/h1&gt;

&lt;h1 id=&quot;添加菜单&quot;&gt;&lt;span id=&quot;101&quot;&gt;添加菜单&lt;/span&gt;&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt; 
&amp;lt;odoo&amp;gt; 
 &amp;lt;act_window id=&quot;action_todo_task&quot; 
   name=&quot;To-do Task&quot; 
   res_model=&quot;todo.task&quot; 
   view_mode=&quot;tree,form&quot; /&amp;gt; 
 &amp;lt;menuitem id=&quot;menu_todo_task&quot; 
   name=&quot;Todos&quot; 
   action=&quot;action_todo_task&quot; /&amp;gt; 
&amp;lt;/odoo&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;修改菜单和操作记录&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   &amp;lt; ！ — — 修改菜单项--&amp;gt;
   &amp;lt;record id=&quot;todo_app.menu_todo_task&quot; model=&quot;ir.ui.menu&quot;&amp;gt;
       &amp;lt;field name=&quot;name&quot;&amp;gt;My To-Do&amp;lt;/field&amp;gt;
   &amp;lt;/record&amp;gt;
   &amp;lt;record model=&quot;ir.actions.act_window&quot;
    id=&quot;todo_app.action_todo_task&quot;&amp;gt;
       &amp;lt;field name=&quot;context&quot;&amp;gt;
          {'search_default_filter_my_tasks': True}
       &amp;lt;/field&amp;gt;
   &amp;lt;/record&amp;gt;   
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;span id=&quot;102&quot;&gt;(一)视图&lt;/span&gt;
=========&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt; 
&amp;lt;odoo&amp;gt; 
 &amp;lt;record id=&quot;view_form_todo_task&quot; model=&quot;ir.ui.view&quot;&amp;gt; 
   &amp;lt;field name=&quot;name&quot;&amp;gt;To-do Task Form&amp;lt;/field&amp;gt; 
   &amp;lt;field name=&quot;model&quot;&amp;gt;todo.task&amp;lt;/field&amp;gt; 
   &amp;lt;field name=&quot;arch&quot; type=&quot;xml&quot;&amp;gt; 
   &amp;lt;form&amp;gt;
       &amp;lt;header&amp;gt;
           &amp;lt;button name=&quot;do_toggle_done&quot; type=&quot;object&quot; string=&quot;Toggle Done&quot; class=&quot;oe_highlight&quot; /&amp;gt;
           &amp;lt;button name=&quot;do_clear_done&quot; type=&quot;object&quot; string=&quot;Clear All Done&quot; /&amp;gt;
       &amp;lt;/header&amp;gt;
       &amp;lt;sheet&amp;gt;
           &amp;lt;group name=&quot;group_top&quot;&amp;gt;
               &amp;lt;group name=&quot;group_left&quot;&amp;gt;
                   &amp;lt;field name=&quot;name&quot;/&amp;gt;
               &amp;lt;/group&amp;gt;
               &amp;lt;group name=&quot;group_right&quot;&amp;gt;
                   &amp;lt;field name=&quot;is_done&quot;/&amp;gt;
                   &amp;lt;field name=&quot;active&quot; readonly=&quot;1&quot; /&amp;gt;
               &amp;lt;/group&amp;gt;
           &amp;lt;/group&amp;gt;
       &amp;lt;/sheet&amp;gt;
   &amp;lt;/form&amp;gt;
   &amp;lt;/field&amp;gt; 
 &amp;lt;/record&amp;gt; 
&amp;lt;/odoo&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;列表视图（tree）&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;record id=&quot;view_tree_todo_task&quot; model=&quot;ir.ui.view&quot;&amp;gt; 
 &amp;lt;field name=&quot;name&quot;&amp;gt;To-do Task Tree&amp;lt;/field&amp;gt; 
 &amp;lt;field name=&quot;model&quot;&amp;gt;todo.task&amp;lt;/field&amp;gt; 
 &amp;lt;field name=&quot;arch&quot; type=&quot;xml&quot;&amp;gt; 
   &amp;lt;tree decoration-muted=&quot;is_done==True&quot;&amp;gt; 
     &amp;lt;field name=&quot;name&quot;/&amp;gt; 
     &amp;lt;field name=&quot;is_done&quot;/&amp;gt; 
   &amp;lt;/tree&amp;gt; 
 &amp;lt;/field&amp;gt; 
&amp;lt;/record&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;搜索视图&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;record id=&quot;view_filter_todo_task&quot; model=&quot;ir.ui.view&quot;&amp;gt; 
 &amp;lt;field name=&quot;name&quot;&amp;gt;To-do Task Filter&amp;lt;/field&amp;gt; 
 &amp;lt;field name=&quot;model&quot;&amp;gt;todo.task&amp;lt;/field&amp;gt; 
 &amp;lt;field name=&quot;arch&quot; type=&quot;xml&quot;&amp;gt; 
   &amp;lt;search&amp;gt; 
     &amp;lt;field name=&quot;name&quot;/&amp;gt; 
     &amp;lt;filter string=&quot;Not Done&quot; 
       domain=&quot;[('is_done','=',False)]&quot;/&amp;gt; 
     &amp;lt;filter string=&quot;Done&quot; 
       domain=&quot;[('is_done','!=',False)]&quot;/&amp;gt; 
   &amp;lt;/search&amp;gt; 
 &amp;lt;/field&amp;gt; 
&amp;lt;/record&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;扩展视图&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   &amp;lt;record id=&quot;view_form_todo_task_inherited&quot; model=&quot;ir.ui.view&quot;&amp;gt;
       &amp;lt;field name=&quot;name&quot;&amp;gt;Todo Task form - User extension&amp;lt;/field&amp;gt;
       &amp;lt;field name=&quot;model&quot;&amp;gt;todo.task&amp;lt;/field&amp;gt;
       &amp;lt;field name=&quot;inherit_id&quot; ref=&quot;todo_app.view_form_todo_task&quot;/&amp;gt;
       &amp;lt;field name=&quot;arch&quot; type=&quot;xml&quot;&amp;gt;
           &amp;lt;field name=&quot;name&quot; position=&quot;after&quot;&amp;gt;
               &amp;lt;field name=&quot;user_id&quot; /&amp;gt;
           &amp;lt;/field&amp;gt;
           &amp;lt;field name=&quot;is_done&quot; position=&quot;before&quot;&amp;gt;
               &amp;lt;field name=&quot;date_deadline&quot; /&amp;gt;
           &amp;lt;/field&amp;gt;
           &amp;lt;field name=&quot;active&quot; position=&quot;attributes&quot;&amp;gt;
               &amp;lt;attribute name=&quot;invisible&quot;&amp;gt;1&amp;lt;/attribute&amp;gt;
           &amp;lt;/field&amp;gt;
       &amp;lt;/field&amp;gt;
    &amp;lt;/record&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;下面是一个写在arch中的实现在is_done字段之前添加date_deadline字段的具体例子：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;xpath expr=&quot;//field[@name='is_done']&quot; position=&quot;before&quot;&amp;gt;
       &amp;lt;field name=&quot;date_deadline&quot; /&amp;gt;
   &amp;lt;/xpath&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;快捷方式&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;field name=&quot;is_done&quot; position=&quot;before&quot;&amp;gt;
      &amp;lt;field name=&quot;date_deadline&quot; /&amp;gt;
   &amp;lt;/field&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;after:将内容添加到父元素之中，匹配的节点之后。&lt;br /&gt;
before:添加内容在匹配节点之前。&lt;br /&gt;
inside（默认值）:匹配节点内的追加内容。&lt;br /&gt;
replace:替换匹配的节点。如果使用空内容，它将删除该匹配的元素。从Odoo 10开始，它还允许用其他标记包装一个元素，通过在内容中使用$0来表示被替换的元素。&lt;br /&gt;
attributes：修改匹配元素的XML属性。在元素内容使用&amp;lt;attribute name =“attr-name”&amp;gt;实现给属性name设置新属性值attr-name。&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;扩展树视图&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   &amp;lt;record id=&quot;view_tree_todo_task_inherited&quot; model=&quot;ir.ui.view&quot;&amp;gt;
       &amp;lt;field name=&quot;name&quot;&amp;gt;Todo Task tree - User extension&amp;lt;/field&amp;gt;
       &amp;lt;field name=&quot;model&quot;&amp;gt;todo.task&amp;lt;/field&amp;gt;
       &amp;lt;field name=&quot;inherit_id&quot; ref=&quot;todo_app.view_tree_todo_task&quot;/&amp;gt;
       &amp;lt;field name=&quot;arch&quot; type=&quot;xml&quot;&amp;gt;
            &amp;lt;field name=&quot;name&quot; position=&quot;after&quot;&amp;gt; 
                &amp;lt;field name=&quot;user_id&quot; /&amp;gt;
            &amp;lt;/field&amp;gt;
       &amp;lt;/field&amp;gt;
    &amp;lt;/record&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;扩展搜索视图&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   &amp;lt;record id=&quot;view_filter_todo_task_inherited&quot; model=&quot;ir.ui.view&quot;&amp;gt;
       &amp;lt;field name=&quot;name&quot;&amp;gt;Todo Task tree - User extension&amp;lt;/field&amp;gt;
       &amp;lt;field name=&quot;model&quot;&amp;gt;todo.task&amp;lt;/field&amp;gt;
       &amp;lt;field name=&quot;inherit_id&quot; ref=&quot;todo_app.view_filter_todo_task&quot;/&amp;gt;
       &amp;lt;field name=&quot;arch&quot; type=&quot;xml&quot;&amp;gt;
           &amp;lt;field name=&quot;name&quot; position=&quot;after&quot;&amp;gt;
               &amp;lt;field name=&quot;user_id&quot; /&amp;gt;
               &amp;lt;filter name=&quot;filter_my_tasks&quot; string=&quot;My Tasks&quot; domain=&quot;[('user_id','in',[uid,False])]&quot; /&amp;gt;
               &amp;lt;filter name=&quot;filter_not_assigned&quot; string=&quot;Not Assigned&quot; domain=&quot;[('user_id','=',False)]&quot; /&amp;gt;
           &amp;lt;/field&amp;gt; 
       &amp;lt;/field&amp;gt;
    &amp;lt;/record&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;三业务逻辑&quot;&gt;&lt;span id=&quot;200&quot;&gt;(三)业务逻辑&lt;/span&gt;&lt;/h1&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from odoo import models, fields, api
@api.multi 
def do_toggle_done(self): 
   for task in self: 
       task.is_done = not task.is_done 
   return True
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;通常表单按钮只能对选定的记录起作用，但在这种情况下，我们希望它也对除当前记录之外的记录起作用，所以使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@api.model&lt;/code&gt;装饰符&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from odoo import models, fields, api
@api.model 
def do_clear_done(self): 
   dones = self.search([('is_done', '=', True)]) 
   dones.write({'active': False}) 
   return True
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;写入方法同时对记录集的所有元素设置值。 要写入的值使用字典进行描述。 在这里使用write比遍历记录集更有效率，以便逐一为每个记录集赋值。&lt;/p&gt;

&lt;h1 id=&quot;访问控制安全&quot;&gt;&lt;span id=&quot;301&quot;&gt;访问控制安全&lt;/span&gt;&lt;/h1&gt;

&lt;p&gt;security/ir.model.access.csv。添加以下内容︰&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
acess_todo_task_group_user,todo.task.user,model_todo_task,base.group_user,1,1,1,1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;row-level访问规则&quot;&gt;&lt;span id=&quot;302&quot;&gt;Row-level访问规则&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;security/todo_access_rules.xml&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;odoo&amp;gt;
    
        &amp;lt;record id=&quot;todo_task_user_rule&quot; model=&quot;ir.rule&quot;&amp;gt;
            &amp;lt;field name=&quot;name&quot;&amp;gt;ToDo Tasks only for owner&amp;lt;/field&amp;gt;
            &amp;lt;field name=&quot;model_id&quot; ref=&quot;model_todo_task&quot;/&amp;gt;
            &amp;lt;field name=&quot;domain_force&quot;&amp;gt;[('create_uid','=',user.id)]&amp;lt;/field&amp;gt;
            &amp;lt;field name=&quot;groups&quot; eval=&quot;[(4,ref('base.group_user'))]&quot;/&amp;gt;
        &amp;lt;/record&amp;gt;
    
&amp;lt;/odoo&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;小心noupdate =”1”属性。这意味着此数据在模块的升级时将不会更新。这就使它能够进行定制，因为后面模块的升级不会破坏用户进行的更改。但请注意，在开发的时候也会这样，所以在开发的时候你可以设置noupdate =”0” ，直到你对你的数据文件满意为止。&lt;br /&gt;
在groups字段，你还会发现一个特殊的表达式。它是一个一对多的关系字段，他们有特殊的操作语法。在这种情况下， (4，x) 元组指要追加 x 记录，这里 的x 是关联的员工组，用base.group_user标识的。&lt;br /&gt;
修改安全记录规则&lt;br /&gt;
覆盖todo_app.todo_task_user_rule以将domain_force字段修改为新值&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &amp;lt;？xml version =“1.0”encoding =“utf-8”？&amp;gt;
   &amp;lt;odoo&amp;gt;
    &amp;lt;data noupdate =“1”&amp;gt;
        &amp;lt;record id =“todo_app.todo_task_per_user_rule”model =“ir.rule”&amp;gt;
           &amp;lt;field name =“name”&amp;gt;所有者和关注者的ToDo任务&amp;lt;/ field&amp;gt;
           &amp;lt;field name =“model_id”ref =“model_todo_task”/&amp;gt;
           &amp;lt;field name =“groups”eval =“[（4，ref（'base.group_user'））]”/&amp;gt;
            &amp;lt;field name =“domain_force”&amp;gt;['|'，（'user_id'，'in'，[user.id，False]），（'message_follower_ids'，'in'，[user.partner_id.id]）]  &amp;lt;/field&amp;gt;
        &amp;lt;/ record&amp;gt;
    &amp;lt;/ data&amp;gt;
  &amp;lt;/ odoo&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;  本人的更多原创文章请加入个人微信公众号。&lt;br /&gt;
&lt;img src=&quot;/images/weixin.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</description>
				<pubDate>Mon, 13 May 2019 00:15:00 +0000</pubDate>
				<link>https://breezecloud.github.io/2019/05/odoo-code-example</link>
				<guid isPermaLink="true">https://breezecloud.github.io/2019/05/odoo-code-example</guid>
			</item>
		
			<item>
				<title>How To Use Smiley</title>
				<description>&lt;p&gt;layout: post
title:  “python调试工具Smiley使用手册”
date: 2019-3-12 08:15:00 +0800
categories: jekyll update
—
python调试工具Smiley使用手册
===
原文：https://smiley.readthedocs.io/en/latest/
Smiley is a tool for spying on your Python programs and recording their activities. It can be used for post-mortem debugging, performance analysis, or simply understanding what parts of a complex program are actually used in different code paths.
Smiley是一个工具，用于监视您的Python程序并记录它们的活动。它可以用于事后调试、性能分析，或者简单地理解复杂程序的实际执行的不同代码路径。&lt;/p&gt;

&lt;p&gt;一、快速指南
1、安装
$ pip install smiley&lt;/p&gt;

&lt;p&gt;2、使用
在一个终端下执行：(test.py是smiley软件包test_app目录下的示例)
$ smiley monitor
在另一个终端下执行：
smiley run ./test.py
smiley run –remote ./test.py
监视器会话将显示应用程序的执行路径和局部变量。&lt;/p&gt;

&lt;p&gt;3、将参数传递给跟踪程序
$ smiley run – ./test.py -e
–将命令序列与运行选项分开&lt;/p&gt;

&lt;p&gt;二、命令参考
run
Run an application and trace its execution.&lt;/p&gt;

&lt;p&gt;monitor
Listen for trace data from an application running under the run command.&lt;/p&gt;

&lt;p&gt;record
Listen for trace data from an application running under the run command and write it to a database for later analysis.&lt;/p&gt;

&lt;p&gt;list
Show the runs previously recorded in the database.&lt;/p&gt;

&lt;p&gt;delete
Delete runs from the database.&lt;/p&gt;

&lt;p&gt;replay
Given a single run id, dump the data from that run in the same format as the monitor command.&lt;/p&gt;

&lt;p&gt;server
Run a web server for browsing the pre-recorded run data collected by the record command.&lt;/p&gt;

&lt;p&gt;report
Export a set of HTML files describing the pre-recorded run data collected by the record command.&lt;/p&gt;

&lt;p&gt;stats show
Show the profiling data from a run.&lt;/p&gt;

&lt;p&gt;stats export
Dump the profiling data from a run to a local file.&lt;/p&gt;

&lt;p&gt;help
Get help for the smiley command or a subcommand.&lt;/p&gt;

&lt;p&gt;三、服务器模式
服务器命令在本地端口上启动Web服务器，为浏览记录和运行捕获的运行数据提供用户界面。它连接到同一个数据库，因此当捕获新的运行时，它们将出现在用户界面中。&lt;/p&gt;

&lt;p&gt;1、Run List
服务器监听http://127.0.0.1:8080 默认情况下。在浏览器中访问该页面会使服务器以反向时间顺序返回数据库中的运行列表。对于每次运行，该列表显示其id、“Description”、开始和结束时间以及任何最终错误消息。单击最左边列中的“X”链接将永久删除从数据库中运行的链接。&lt;/p&gt;

&lt;p&gt;2、运行详细信息
单击其中一个Run id值将打开为该运行记录的详细信息。详细信息页显示程序运行时的逐行状态，包括程序控件所在的位置(文件名、行号和源行)以及局部变量和函数的返回值。这是由监控器和重放，以一种更容易阅读的格式。&lt;/p&gt;

&lt;p&gt;3、源文件
RunDetails视图中的每个文件名都链接到一个页面，该页面显示Python文件的完整来源，就像在程序执行时一样。&lt;/p&gt;

&lt;p&gt;4、文件列表
对于具有多个源文件的应用程序，可以通过导航到文件列表视图并从列表中选择文件来检查源代码。&lt;/p&gt;

&lt;p&gt;5、统计分析
stats视图显示运行的分析器输出，并按累积时间排序。与运行详细信息一样，每个文件名都链接到模块的完整源代码&lt;/p&gt;

&lt;p&gt;6、调用图
调用图视图使用gprof2dot和图文生成一个树图，显示在程序的不同部分中使用了多少时间，以便更容易地将注意力集中在使用时间最多的区域上。&lt;/p&gt;

&lt;p&gt;注
若要使此页面正常工作，dot命令必须已安装。安装smiley应该自动安装gprof2dot的。&lt;/p&gt;
</description>
				<pubDate>Tue, 12 Mar 2019 00:00:00 +0000</pubDate>
				<link>https://breezecloud.github.io/2019/03/how-to-use-smiley</link>
				<guid isPermaLink="true">https://breezecloud.github.io/2019/03/how-to-use-smiley</guid>
			</item>
		
			<item>
				<title>python内置对象说明</title>
				<description>&lt;h1 id=&quot;python内置对象说明&quot;&gt;python内置对象说明&lt;/h1&gt;
&lt;p&gt;dir(&lt;strong&gt;builtins&lt;/strong&gt;):打印所有内置对象，内置函数可以参考:[]!http://www.runoob.com/python/python-built-in-functions.html&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;‘&lt;strong&gt;build_class&lt;/strong&gt;’, build_class 函数是创建类对象过程中的一个核心函数&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘&lt;strong&gt;debug&lt;/strong&gt;’, 
只读变量,参考：[]!https://blog.csdn.net/you_are_my_dream/article/details/53328293&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘&lt;strong&gt;doc&lt;/strong&gt;’, 
每个对象都会有一个__doc__属性，用于描述该对象的作用。在一个模块被import时，其文件中的某些特殊的字符串会被python解释器保存在相应对象的__doc__属性中。比如，一个模块有模块的__doc__，一个class或function也有其对应的__doc__属性。在python中，一个模块其实就是一个.py文件。在文件中特殊的地方书写的字符串就是所谓的docstrings，就是将被放到__doc__的内容。这个“特殊的地方”包括：&lt;/p&gt;
    &lt;ol&gt;
      &lt;li&gt;一个文件任何一条可执行的代码之前  #模块的__doc__&lt;/li&gt;
      &lt;li&gt;一个类，在类定义语句后，任何可执行代码前#类的__doc__&lt;/li&gt;
      &lt;li&gt;一个函数，在函数定义语句后，任何可执行代码前#函数的__doc__
```
#use  &lt;strong&gt;doc&lt;/strong&gt; 属性
class MyClass:
  ‘string.’
  def printSay():
   ‘print say welcome to you.’
   print ‘say welcome to you.’
print MyClass.&lt;strong&gt;doc&lt;/strong&gt;
print MyClass.printSay.&lt;strong&gt;doc&lt;/strong&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;#输出结果
 string.
print say welcome to you.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
* '__import__', 
import导入的是一个标准模块，而标准模块的概念是一个文件夹里面必须包含__init__.py文件。它的作用更像是一种声明，且import模块进来之后，万一原本的模块有什么变化，可以通过reload()进行重新加载。
__import__()作为一个函数，只能接受字符串参数，返回值可以直接用来操作，通常在动态加载的时候用到这个函数，最常见的情景就是插件功能的支持。

* '__loader__', 
[]!https://www.cnblogs.com/baishoujing/p/6358685.html 
'__loader__'是由加载器在导入的模块上设置的属性，访问它时将会返回加载器对象本身。

* '__name__', 
显示了当前模块执行过程中的名称，如果当前程序运行在这个模块中，__name__ 的名称就是__main__如果不是，则为这个模块的名称

* '__package__', 
获取导入文件的路径，多层目录以点分割，注意：对当前文件返回None

* '__spec__', TODO(找不到)

* 'abs', 
绝对值

* 'all', 
* 'any', 
all() # 全为真，输出Ture ， 则输出Flase
any() # 只要有真，输出Ture，则输出Flase
0,None,&quot;&quot;,[],(),{}  表示False
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;n1 = all([1,2,3,[],None])
print (n1)&lt;/p&gt;
&lt;h1 id=&quot;false&quot;&gt;False&lt;/h1&gt;
&lt;p&gt;n2 = any([1,0,””,[]])
print (n2)&lt;/p&gt;
&lt;h1 id=&quot;true&quot;&gt;True&lt;/h1&gt;
&lt;p&gt;n1 = all([1,2,3,[],None])
print (n1)&lt;/p&gt;
&lt;h1 id=&quot;false-1&quot;&gt;False&lt;/h1&gt;
&lt;p&gt;n2 = any([1,0,””,[]])
print (n2)&lt;/p&gt;
&lt;h1 id=&quot;true-1&quot;&gt;True&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
* 'ascii', 
自动执行对象的__repr__方法

* 'bin', 
将十进制转为二进制  0b 表示二进制

* 'bool',
布尔值
0,None,&quot;&quot;,[],(),{}  表示False

* 'breakpoint', 
Python 3.7添加了breakpoint()，这个内置函数使得函数被调用时，让执行切换到调试器。相应的调试器不一定是Python自己的pdb，可以是之前被设为首选调试器的任何调试器。以前，调试器不得不手动设置，然后调用，因而使代码更冗长。而有了breakpoint()，只需一个命令即可调用调试器，并且让设置调试器和调用调试器泾渭分明。

* 'bytearray', 
根据传入的参数创建一个新的字节数组
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;blockquote&gt;
  &lt;blockquote&gt;
    &lt;blockquote&gt;
      &lt;p&gt;bytearray(‘中文’,’utf-8’)
bytearray(b’\xe4\xb8\xad\xe6\x96\x87’)&lt;/p&gt;
      &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;* 'bytes',
根据传入的参数创建一个新的不可变字节数组
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;      &lt;/div&gt;
      &lt;p&gt;bytes(‘中文’,’utf-8’)
b’\xe4\xb8\xad\xe6\x96\x87’
```&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/blockquote&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;‘callable’, 
检测传递的值是否可以调用
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def f1():
  pass
f1()
f2 = 123
print (callable(f1))
# True
print (callable(f2))
# False
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;‘chr’, 
输出 ASCII的对应关系，chr()输出十进制位置的字符，ord()输出字符在ASCii表的位置
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;print (chr(65))
# A
print (ord(&quot;A&quot;))
# 65
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;‘classmethod’, 
classmethod 是一个装饰器函数，用来标示一个方法为类方法。类方法的第一个参数是类对象参数，在方法被调用的时候自动将类对象传入，参数名称约定为cls。如果一个方法被标示为类方法，则该方法可被类对象调用(如 C.f())，也可以被类的实例对象调用(如 C().f())
```
    &lt;blockquote&gt;
      &lt;blockquote&gt;
        &lt;blockquote&gt;
          &lt;p&gt;class C:
  @classmethod
  def f(cls,arg1):
      print(cls)
      print(arg1)&lt;/p&gt;
        &lt;/blockquote&gt;
      &lt;/blockquote&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;blockquote&gt;
    &lt;blockquote&gt;
      &lt;p&gt;C.f(‘类对象调用类方法’)&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;class ‘&lt;strong&gt;main&lt;/strong&gt;.C’&amp;gt;
类对象调用类方法&lt;/p&gt;

&lt;blockquote&gt;
  &lt;blockquote&gt;
    &lt;blockquote&gt;
      &lt;p&gt;c = C()
c.f(‘类实例对象调用类方法’)&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;class ‘&lt;strong&gt;main&lt;/strong&gt;.C’&amp;gt;
类实例对象调用类方法&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
* 'compile', 
编译，将字符串编译成python代码，见exec

* 'complex', 
返回一个复数。有两个可选参数。当两个参数都不提供时，返回复数 0j。

* 'copyright', 
版权信息

* 'credits', 
感谢信息

* 'dict', 
用于创建一个字典

* 'dir', 
快速获取模块,对象提供的功能

* 'divmod',
得到商和余数

* 'enumerate',
多用于在for循环中得到计数，利用它可以同时获得索引和值，即需要index和value值的时候可以使用enumerate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;#指定索引从1开始&lt;/p&gt;
&lt;blockquote&gt;
  &lt;blockquote&gt;
    &lt;blockquote&gt;
      &lt;p&gt;lst = [1,2,3,4,5,6]
for index,value in enumerate(lst,1):
print (‘%s,%s’ % (index,value))
1,1
2,2
3,3
4,4
5,5
6,6
```&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/blockquote&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;‘eval’,
执行表达式，并且获取结果
eval有返回值，exec没返回值&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;‘exec’,
执行python代码
eval有返回值，exec没返回值
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;s = &quot;print('hello,python~')&quot;
r = compile(s,&quot;&amp;lt;string&amp;gt;&quot;,&quot;exec&quot;)
exec (r)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘exit’,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sys.exit(n)&lt;/code&gt; 退出程序引发SystemExit异常，可以捕获异常执行些清理工作。n默认值为0，表示正常退出，其他都是非正常退出。还可以sys.exit(“sorry, goodbye!”); 一般主程序中使用此退出。
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;os._exit(n)&lt;/code&gt;， 直接退出, 不抛异常, 不执行相关清理工作。常用在子进程的退出。
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit()/quit()&lt;/code&gt;，跑出SystemExit异常。一般在交互式shell中退出时使用。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;‘filter’,
函数返回值为Ture,将元素添加结果中,filter 循环第二个参数，让每个循环元素执行函数，如果函数返回值为Ture,表示函数合法
```
def f1(args):
  result = []
  for item in args:
      if item &amp;gt; 22:
          result.append(item)
  return result
li = [11,22,33,44,55,66,77,88]
ret = f1(li)
print (ret)
    &lt;h1 id=&quot;33-44-55-66-77-88&quot;&gt;[33, 44, 55, 66, 77, 88]&lt;/h1&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;优化示例1&quot;&gt;优化示例1&lt;/h1&gt;
&lt;p&gt;def f2(a):
    if a &amp;gt; 22:
        return True
ls1 = [11,22,33,44,55,66,77]
res1 = filter(f2,ls1)
print(list(res1))&lt;/p&gt;
&lt;h1 id=&quot;33-44-55-66-77&quot;&gt;[33, 44, 55, 66, 77]&lt;/h1&gt;

&lt;h1 id=&quot;知识扩展lambda-函数&quot;&gt;知识扩展，lambda 函数&lt;/h1&gt;
&lt;p&gt;res2 = filter(lambda x:x &amp;gt; 22,ls1)
print (res2) # 返回一个filter object&lt;/p&gt;
&lt;h1 id=&quot;filter-object-at-0x000000e275771748&quot;&gt;&amp;lt;filter object at 0x000000E275771748&amp;gt;&lt;/h1&gt;
&lt;p&gt;print (list(res2))&lt;/p&gt;
&lt;h1 id=&quot;33-44-55-66-77-1&quot;&gt;[33, 44, 55, 66, 77]&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
* 'float', 
函数用于将整数和字符串转换成浮点数

* 'format', 
函数功能将一个数值进行格式化显示。如果参数format_spec未提供，则和调用str(value)效果相同，转换成字符串格式化。
参考：[]!https://www.cnblogs.com/sesshoumaru/p/6005368.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;#整形数值可以提供的参数有 ‘b’ ‘c’ ‘d’ ‘o’ ‘x’ ‘X’ ‘n’ None&lt;/p&gt;
&lt;blockquote&gt;
  &lt;blockquote&gt;
    &lt;blockquote&gt;
      &lt;p&gt;format(3,’b’) #转换成二进制
‘11’
format(97,’c’) #转换unicode成字符
‘a’
format(11,’d’) #转换成10进制
‘11’
format(11,’o’) #转换成8进制
‘13’
format(11,’x’) #转换成16进制 小写字母表示
‘b’
format(11,’X’) #转换成16进制 大写字母表示
‘B’
format(11,’n’) #和d一样
‘11’
format(11) #默认和d一样
‘11’
```&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/blockquote&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;‘frozenset’,
传入一个可迭代对象，生成一个新的不可变集合。
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; a = frozenset(range(10))
&amp;gt;&amp;gt;&amp;gt; a
frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘globals’, 
所有的全局变量&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘hasattr’, 
函数功能用来检测对象object中是否含有名为name的属性，如果有则返回True，如果没有返回False&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘hash’, 
生成一个hash值（字符串）&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘help’,
快速获取模块,对象提供的功能&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘hex’,
将十进制转为十六进制  0x 表示十六进制&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘id’, 
 函数用于获取对象的内存地址&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;‘input’, 
如果提供了promat参数，首先将参数值输出到标准的输出，并且不换行。函数读取用户输入的值，将其转换成字符串。
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; s = input('please input your name:')
please input your name:Ain
&amp;gt;&amp;gt;&amp;gt; s
'Ain'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘int’, 
把其他类型转换为整数&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘isinstance’, 
isinstance(object, classinfo)
函数功能用于判断对象是否是类型对象的实例，object参数表示需要检查的对象，calssinfo参数表示类型对象。如果object参数是classinfo类型对象(或者classinfo类对象的直接、间接、虚拟子类)的实例，返回True。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘issubclass’, 
issubclass(class, classinfo)
函数功能用于判断一个类型对象是否是另一个类型对象的子类，class参数表示需要检查的类型对象，calssinfo参数表示需要对比类型对象。如果class参数是classinfo类型对象(或者classinfo类对象的直接、间接、虚拟子类)的实例，返回True&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;‘iter’, 
 函数功能返回一个迭代器对象。当第二个参数不提供时，第一个参数必须是一个支持可迭代协议(即实现了__iter__()方法)的集合(字典、集合、不可变集合)，或者支持序列协议(即实现了__getitem__()方法，方法接收一个从0开始的整数参数)的序列(元组、列表、字符串)，否则将报错。
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; l = [1, 2, 3]
for i in iter(l):
    print(i)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;如果传递了第二个参数，则object必须是一个可调用的对象（如，函数）。此时，iter创建了一个迭代器对象，每次调用这个迭代器对象的__next__()方法时，都会调用object。
如果__next__的返回值等于sentinel，则抛出StopIteration异常，否则返回下一个值&lt;/p&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  class TestIter(object):
 
      def __init__(self):
          self.l=[1,2,3,4,5]
          self.i=iter(self.l)
      def __call__(self):  #定义了__call__方法的类的实例是可调用的
          item = next(self.i)
          print (&quot;__call__ is called,which would return&quot;,item)
          return item
      def __iter__(self): #支持迭代协议(即定义有__iter__()函数)
          print (&quot;__iter__ is called!!&quot;)
          return iter(self.l)
 
  t = TestIter()  # t是可调用的
  t1 = iter(t, 3)  # t必须是callable的，否则无法返回callable_iterator
  print(callable(t))
  for i in t1:
      print(i)
# 它每次在调用的时候，都会调用__call__函数，并且最后输出3就停止了。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘len’, 
输出对象的长度&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘license’, 
打印license&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘list’, 
列表，可将元组，字符串等转换为列表。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘locals’, 
所有的局部变量&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;‘map’, 
将函数返回值添加结果中,(函数，可迭代的对象（可以for循环）)
```
def f1(args):
  result = []
  for i in args:
      result.append(i + 100)
  return result
lst1 = [11,22,33,44,55,66]
rest = f1(lst1)
print (list(rest))
    &lt;h1 id=&quot;111-122-133-144-155-166&quot;&gt;[111, 122, 133, 144, 155, 166]&lt;/h1&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;优化示例2map函数&quot;&gt;优化示例2，，,map函数&lt;/h1&gt;
&lt;p&gt;def f2(a):
    return a + 100
lst2 = [11,22,33,44,55,66]
result1 = map(f2,lst2)
print (list(result1))&lt;/p&gt;
&lt;h1 id=&quot;111-122-133-144-155-166-1&quot;&gt;[111, 122, 133, 144, 155, 166]&lt;/h1&gt;

&lt;h1 id=&quot;优化示例2map函数lambda函数&quot;&gt;优化示例2，map函数+lambda函数&lt;/h1&gt;
&lt;p&gt;lst3 = [11,22,33,44,55,66]
result2 = map(lambda a:a+100,lst3)
print (list(result2))&lt;/p&gt;
&lt;h1 id=&quot;111-122-133-144-155-166-2&quot;&gt;[111, 122, 133, 144, 155, 166]&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
* 'max', 
最大值
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;lit = [11,22,33,44,55]
print (max(lit))
print (min(lit))
print (sum(lit))&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
* 'memoryview', 
该函数会创建一个引用自 obj 的内存视图(memoryview)对象。内存视图对象允许 Python 代码访问支持缓冲区协议(buffer protocol)的对象的内部数据，且无需拷贝。
obj 必须支持缓冲区协议(buffer protocol)。支持缓冲区协议的内置对象有 bytes 和 bytearray。bytes 和 bytearray 由 memoryview 提供支持，内存视图使用缓冲区协议(buffer protocol)访问来访问其它二进制对象的内存，且无需拷贝。
示例 - 使用内存视图对象修改一个短整型有符号整数数组的数据
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;from array import array&lt;/p&gt;

&lt;p&gt;numbers = array(‘h’, [-2, -1, 0, 1, 2]) # ‘h’表示signed short
memv = memoryview(numbers) 
len(memv) #&amp;gt; 5&lt;/p&gt;
&lt;h1 id=&quot;转换成列表形式&quot;&gt;转换成列表形式&lt;/h1&gt;
&lt;p&gt;memv.tolist() #&amp;gt; [-2, -1, 0, 1, 2]&lt;/p&gt;
&lt;h1 id=&quot;转换成无符号字符类型&quot;&gt;转换成无符号字符类型&lt;/h1&gt;
&lt;p&gt;memv_oct = memv.cast(‘B’)     &lt;br /&gt;
memv_oct.tolist() #&amp;gt; [254, 255, 255, 255, 0, 0, 1, 0, 2, 0]&lt;/p&gt;
&lt;h1 id=&quot;修改第3个元素的高位字段&quot;&gt;修改第3个元素的高位字段&lt;/h1&gt;
&lt;p&gt;memv_oct[5] = 4               &lt;br /&gt;
numbers #&amp;gt; array(‘h’, [-2, -1, 1024, 1, 2])&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
* 'min', 
最小值

* 'next', 
 函数必须接收一个可迭代对象参数，每次调用的时候，返回可迭代对象的下一个元素。如果所有元素均已经返回过，则抛出StopIteration 异常

* 'object', 
Object类是Python中所有类的基类，如果定义一个类时没有指定继承那个类，则默认继承object类

* 'oct', 
将十进制转为八进制  0o 表示八进制

* 'open',
open函数，该函数用于处理文件,参考[]!https://www.cnblogs.com/sesshoumaru/p/6047046.html

* 'ord',
见'chr'

* 'pow', 
求指数
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;print(pow(2,10))&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
* 'print', 
用于打印输出，最常见的一个函数
参考：[]!http://www.runoob.com/python/python-func-print.html

* 'property',
* 'setattr', 
* 'getattr', 
* 'delattr', 
property()函数中的三个函数分别对应的是获取属性的方法、设置属性的方法以及删除属性的方法，这样一来，外部的对象就可以通过访问x的方式，来达到获取、设置或删除属性的目的。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;class Shuxing():
    def &lt;strong&gt;init&lt;/strong&gt;(self, size = 10):
        self.size = size
    def getSize(self):
        return self.size
    def setSize(self, value):
        self.size = value
    def delSize(self):
        del self.size
    x = property(getSize, setSize, delSize)&lt;/p&gt;

&lt;blockquote&gt;
  &lt;blockquote&gt;
    &lt;blockquote&gt;
      &lt;p&gt;sx = Shuxing(100)
sx.x
100
sx.x= 106
sx.x
106
del sx.x
sx.x
Traceback (most recent call last):
  File “&amp;lt;pyshell#60&amp;gt;”, line 1, in &lt;module&gt;
    sx.x
  File &quot;&amp;lt;pyshell#54&amp;gt;&quot;, line 5, in getSize
    return self.size
AttributeError: 'Shuxing' object has no attribute 'size'
```&lt;/module&gt;&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/blockquote&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;‘quit’, 
同exit()&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;‘range’, 
可创建一个整数列表，一般用在 for 循环中
语法：range(start, stop[, step])
start: 计数从 start 开始。默认是从 0 开始。例如range（5）等价于range（0， 5）; 
stop: 计数到 stop 结束，但不包括 stop。例如：range（0， 5） 是[0, 1, 2, 3, 4]没有5 
step：步长，默认为1。例如：range（0， 5） 等价于 range(0, 5, 1)
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt;range(10)        # 从 0 开始到 10
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;‘repr’, 
函数功能返回一个对象的字符串表现形式。其功能和str函数比较类似，但是两者也有差异：函数str() 用于将值转化为适于人阅读的形式，而repr() 转化为供解释器读取的形式。
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; a = 'some text'
&amp;gt;&amp;gt;&amp;gt; str(a)
'some text'
&amp;gt;&amp;gt;&amp;gt; repr(a)
&quot;'some text'&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;‘reversed’, 
反转
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;lit1 = [11,22,33,44,55,66]
print (list(reversed(lit1)))
# [66, 55, 44, 33, 22, 11]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;‘round’, 
四舍五入求值&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘set’,
 函数创建一个无序不重复元素集，可进行关系测试，删除重复数据，还可以计算交集、差集、并集等&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘slice’, 
slice() 函数实现切片对象，主要用在切片操作函数里的参数传递。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;‘sorted’, 
排序 等同于列表的sort&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;‘staticmethod’,&lt;/li&gt;
  &lt;li&gt;‘str’,
字节转化为字符串
```
    &lt;h1 id=&quot;将字符串转换为字节类型系统中的表现形式为16进制&quot;&gt;将字符串转换为字节类型,系统中的表现形式为16进制&lt;/h1&gt;
    &lt;h1 id=&quot;bytes字符串编码格式&quot;&gt;bytes(字符串，编码格式)&lt;/h1&gt;
    &lt;p&gt;s = “中国”
n3 = bytes(s,encoding=”utf-8”)
print (n3)&lt;/p&gt;
    &lt;h1 id=&quot;bxe4xb8xadxe5x9bxbd&quot;&gt;b’\xe4\xb8\xad\xe5\x9b\xbd’&lt;/h1&gt;
    &lt;p&gt;n4 = bytes(s,encoding=”gbk”)
print (n4)&lt;/p&gt;
    &lt;h1 id=&quot;bxd6xd0xb9xfa&quot;&gt;b’\xd6\xd0\xb9\xfa’&lt;/h1&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;n5 = str(b’\xd6\xd0\xb9\xfa’,encoding=”gbk”)
print (n5)&lt;/p&gt;
&lt;h1 id=&quot;中国&quot;&gt;中国&lt;/h1&gt;
&lt;p&gt;n6 = str(b’\xe4\xb8\xad\xe5\x9b\xbd’,encoding=”utf-8”)
print (n6)&lt;/p&gt;
&lt;h1 id=&quot;中国-1&quot;&gt;中国&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;'sum', 
求和

* 'super', 
函数是用于调用父类(超类)的一个方法
Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx

* 'tuple', 
将列表转换为元组

* 'type', 
type() 函数如果你只有第一个参数则返回对象的类型，三个参数返回新的类型对象。 
isinstance() 与 type() 区别：
type() 不会认为子类是一种父类类型，不考虑继承关系。
isinstance() 会认为子类是一种父类类型，考虑继承关系。 
如果要判断两个类型是否相同推荐使用 isinstance()。

* 'vars', 
返回对象object的属性和属性值的字典对象

* 'zip',
函数用于将可迭代的对象作为参数，将对象中对应的元素打包成一个个元组，然后返回由这些元组组成的列表
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;l1 = [“hello”,11,22,33]
l2 = [“world”,44,55,66]
l3 = [“python”,77,88,99]
l4 = zip(l1,l2,l3)&lt;/p&gt;
&lt;h1 id=&quot;print-listl4&quot;&gt;print (list(l4))&lt;/h1&gt;
&lt;h1 id=&quot;-hello-world-python-11-44-77-22-55-88-33-66-99&quot;&gt;# [(‘hello’, ‘world’, ‘python’), (11, 44, 77), (22, 55, 88), (33, 66, 99)]&lt;/h1&gt;
&lt;p&gt;temp1 = list(l4)[0]
print (temp1[0])
ret1 = “ “.join(temp1)
print (ret1)&lt;/p&gt;
&lt;h1 id=&quot;hello-world-python&quot;&gt;hello world python&lt;/h1&gt;
&lt;p&gt;```&lt;/p&gt;
</description>
				<pubDate>Fri, 01 Mar 2019 00:15:00 +0000</pubDate>
				<link>https://breezecloud.github.io/2019/03/python-inter-object</link>
				<guid isPermaLink="true">https://breezecloud.github.io/2019/03/python-inter-object</guid>
			</item>
		
			<item>
				<title>jekyll简明使用说明</title>
				<description>&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;什么是jekyll？
jekyll是一个简单的免费的Blog生成工具（将纯文本转化为静态网站和博客），类似WordPress。但是和WordPress又有很大的不同，原因是jekyll只是一个生成静态网页的工具，不需要数据库支持。但是可以配合第三方服务,例如Disqus。最关键的是jekyll可以免费部署在Github上，而且可以绑定自己的域名。我们可以在客户端生成静态网站，然后上传到服务器。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;安装
    &lt;ul&gt;
      &lt;li&gt;下载windows安装包，参考http://www.madhur.co.in/blog/2013/07/20/buildportablejekyll.html
软件包位于:https://github.com/madhur/PortableJekyll ,已经包括git包&lt;/li&gt;
      &lt;li&gt;git clone https://github.com/scotte/jekyll-clean.git #下载主题&lt;/li&gt;
      &lt;li&gt;gem isntall bundler #更新bunller&lt;/li&gt;
      &lt;li&gt;bundle install #安装依赖&lt;/li&gt;
      &lt;li&gt;启动
cd jekyll-clean
jekyll server # 启动本地 http://127.0.0.1/jekyll-clean ( jekyll serve –watch)
jekyll build #重新生成静态网站&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;上传到git
    &lt;ul&gt;
      &lt;li&gt;新建github pages&lt;/li&gt;
      &lt;li&gt;git clone git@github.com:yourname/yourname.github.io.git #复制github&lt;/li&gt;
      &lt;li&gt;下载jekyll模板，并安装上面的步骤本地测试&lt;/li&gt;
      &lt;li&gt;将下载的模板copy到yourname.github.io目录，注意不要覆盖.git目录&lt;/li&gt;
      &lt;li&gt;修改_config.yml
url: https://scotte.github.io/jekyll-clean 改为https://breezecloud.github.io/
baseurl: /jekyll-clean 改为 ‘‘(根目录)&lt;/li&gt;
      &lt;li&gt;上传到服务器
git add . 
git commit -a -m “版本说明”
git push&lt;/li&gt;
      &lt;li&gt;jekyll serve 浏览https://breezecloud.github.io/&lt;/li&gt;
      &lt;li&gt;设置域名，如果你想设置自己的域名，在breezecloud.github.io的目录下有一个CNAME目录，将自己的域名写如（比如：youery.cn），同时设置域名解析服务器增加记录（@表示直接解析）
&lt;br /&gt;&lt;img src=&quot;/images/2019-2-28-1.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;添加文章
_post目录下增加md文件，增加头信息如：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;---
layout: post
title:  &quot;给树莓派安装ArchLinux&quot;
date: 2017-12-15 16:25:06 -0700
---
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;生成的静态网站会自动根据时间排序，之后执行jekyll build重新生成site&lt;br /&gt;
参考：&lt;br /&gt;
https://www.jianshu.com/p/9f71e260925d&lt;br /&gt;
https://www.jekyll.com.cn&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
</description>
				<pubDate>Thu, 28 Feb 2019 00:15:00 +0000</pubDate>
				<link>https://breezecloud.github.io/2019/02/jekyll-install-and-use</link>
				<guid isPermaLink="true">https://breezecloud.github.io/2019/02/jekyll-install-and-use</guid>
			</item>
		
	</channel>
</rss>
