python 2中希望使用print时将\uxxxx的文字打印为中文

 python  python 2中希望使用print时将\uxxxx的文字打印为中文已关闭评论
5月 092020
 

在python 2里使用print打印含有\uxxx的中文字符时不能直接显示中文,可以使用下面的方式:

举例:

obj = [u’\u53c8\u6765\u5077\u5077\u770b\u6211′,u’\u7231\u6211\u4f60\u5c31\u544a\u8bc9\u6211′]

使用: print(repr(obj).decode(‘unicode-escape’))

 

obj=[‘又来偷偷看我’,’爱我你就告诉我’]

使用:print(repr(obj).decode(‘string-escape’))

 

python 3 没有这个问题

supervisord管理python进程时print(stdout)日志输出不及时,甚至有缺失问题解决

 python, supervisord  supervisord管理python进程时print(stdout)日志输出不及时,甚至有缺失问题解决已关闭评论
12月 192019
 

使用supervisord管理python进程,代码中print()打印的日志在使用tail -f查看时输出总是不及时,甚至有缺失问题,网络上有下面的几种方式解决:

If you are also using Supervisor to monitor and heal your long running Python projects and observed that output of your program is not being logged to stdout_logfile, it is because Python print statement does not automatically flush output to STDOUT.

One solution is using sys.stdout.flush() frequently to flush the output or if you are using Python 3.3, print(msg, flush=True) is another solution. However, a better solution is to run python with -u parameter (unbuffered mode).

以上总结方法:

  1. 频繁使用sys.stdout.flush()
  2. python3.3以后版本,print使用print(msg, flush=True)
  3. supervisord配置文件里command命令行添加-u参数(这个是最推荐的方法)

举例:

[program:analysis]
command = python -u AnalysisTest.py -port=70%(process_num)02d

python输出有颜色和背景色的文字(\033[显示方式;前景色;背景色m, \033[0m )

 python  python输出有颜色和背景色的文字(\033[显示方式;前景色;背景色m, \033[0m )已关闭评论
11月 212019
 

python中经常有输出文字的需求,特别是如果输出的文字带颜色或者文字有背景色的化更能突出重点,在运维时特别有用,那如何使输出文字带颜色和背景色呢?

#格式:
设置颜色开始 :\033[显示方式;前景色;背景色m

例如:
\033[31;43;1m #—红底黄字高亮显示
\033[0m     #—采用终端默认设置,即缺省颜色,一般可以在文字显示后,添加在末尾以恢复默认

#说明:
前景色:
30 黑色
31  红色
32 绿色
33 黃色
34 蓝色
35 紫红色
36 青蓝色
37 白色

背景色:
40 黑色
41 红色
42 绿色
43 黃色
44 蓝色
45 紫红色
46 青蓝色
47 白色

显示方式:

0 终端默认设置
1 高亮显示
4 使用下划线
5 闪烁
7 反白显示

 

大家可以发现: ‘;’前后的三个参数:显示方式,前景色,背景色是可选参数,可以只写其中的某一个或者某两个;由于表示三个参数不同含义的数值都是唯一没有重复的,所以三个参数的书写先后顺序没有固定要求,系统都可识别,所以参数的前后顺序实际是没有关系,但还是建议大家按规则来。

 

以下举个例子,看下效果(大家特别体会下结尾有和没有“\033[0m”的效果):

#!/usr/bin/python
#coding=utf-8

print("\033[1;33m 我是1;33m的显示效果   \033[3;31m")
print('我是是上一行结尾的3;31m颜色输出效果 ')
print("\n")

print("==========前景色==========")
print("\033[30;1mHello, Welcome To Python! " + "#黑字")   #黑
print("\033[31;1mHello, Welcome To Python! " + "#红字")   #红
print("\033[32;1mHello, Welcome To Python! " + "#绿字")   #绿
print("\033[33;1mHello, Welcome To Python! " + "#黄字")   #黄
print("\033[34;1mHello, Welcome To Python! \033[0m" + "#蓝字")   #蓝
print("\033[35;1mHello, Welcome To Python! \033[0m" + "#紫字")   #紫
print("\033[36;1mHello, Welcome To Python! \033[0m" + "#青字") #青
print("\033[37;1mHello, Welcome To Python! \033[0m" + "#白字") #白
print("==========背景色==========")
print("\033[40;1mHello, Welcome To Python! \033[0m" + "#黑底")   #黑
print("\033[41;1mHello, Welcome To Python! " + "#红底")   #红
print("\033[42;1mHello, Welcome To Python! " + "#绿底")   #绿
print("\033[43;1mHello, Welcome To Python! \033[0m" + "#黄底")   #黄
print("\033[44;1mHello, Welcome To Python! \033[0m" +"#蓝底")   #蓝
print("\033[45;1mHello, Welcome To Python! \033[0m" + "#紫底")   #紫
print("\033[46;1mHello, Welcome To Python! \033[0m" + "#青底")   #青
print("\033[47;1mHello, Welcome To Python! \033[0m" + "#白底")   #白
print("=======结合前景色背景色==========")
print("\033[31;43;1mHello, Welcome To Python! \033[0m" + "#红字黄底")   #红字黄底
print("\033[43;31;1mHello, Welcome To Python! \033[0m" + "#红字黄底(有意调换)")   #红字黄底
print("\033[35;42;1mHello, Welcome To Python! \033[0m" + "#紫字绿底")   #紫字绿底
print("=======结合前景色背景色显示方式==========")
print("\033[30;47;1mHello, Welcome To Python! \033[0m" + "#高亮显示")   #高亮显示
print("\033[30;47;4mHello, Welcome To Python! \033[0m" + "#使用下划线")   #使用下划线


输出效果如下:

pip安装包到指定目录方法(指定路径)

 mac, pip, python  pip安装包到指定目录方法(指定路径)已关闭评论
10月 242019
 

mac中使用pip安装包后,可能会发现明明提示“Successfully installed xxx“,但就是找不到这个包“ImportError: No module named  xxx”?

其实是这样: 默认pip安装在/usr/local/lib/python2.7/site-packages, 但如果sudo pip安装则会安装在/lib/python2.7/site-packages,那如果我要指定安装包的路径需要怎么做呢?使用下面的黑体字命令即可,在-install-option里添加–install-purelib指定目录即可,以下命令安装google-api-python-client包到/usr/local/lib/python2.7/site-packages

pip install --install-option="--install-purelib=/usr/local/lib/python2.7/site-packages" google-api-python-client

 

DONE!!!

mac下使用pip安装模块时,出现错误提示:     “must supply either home or prefix/exec-prefix — not both”

 mac, python  mac下使用pip安装模块时,出现错误提示:     “must supply either home or prefix/exec-prefix — not both”已关闭评论
10月 232019
 

mac下使用pip安装时,出现错误提示:

    “must supply either home or prefix/exec-prefix — not both”

DistutilsOptionError: must supply either home or prefix/exec-prefix — not both

 

参考Homebrew python页 https://github.com/Homebrew/brew/blob/master/docs/Homebrew-and-Python.md

在文件~/.pydistutils.cfg中加入如下内容:

[install]

prefix=

 

再pip安装即可!

centos 7 使用pyenv 安装 python 3 出现“ModuleNotFoundError: No module named ‘_ctypes’”的问题解决

 python  centos 7 使用pyenv 安装 python 3 出现“ModuleNotFoundError: No module named ‘_ctypes’”的问题解决已关闭评论
10月 232019
 

centos 7 下使用pyenv 安装python 3.7时错误如下;

Downloading Python-3.7.4.tar.xz…
-> https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tar.xz
Installing Python-3.7.4…

BUILD FAILED (CentOS Linux 7 using python-build 20180424)

Inspect or clean up the working tree at /tmp/python-build.20191023165632.13343
Results logged to /tmp/python-build.20191023165632.13343.log

Last 10 log lines:
File “/tmp/tmpgvoc4sao/pip-19.0.3-py2.py3-none-any.whl/pip/_internal/cli/main_parser.py”, line 12, in <module>
File “/tmp/tmpgvoc4sao/pip-19.0.3-py2.py3-none-any.whl/pip/_internal/commands/__init__.py”, line 6, in <module>
File “/tmp/tmpgvoc4sao/pip-19.0.3-py2.py3-none-any.whl/pip/_internal/commands/completion.py”, line 6, in <module>
File “/tmp/tmpgvoc4sao/pip-19.0.3-py2.py3-none-any.whl/pip/_internal/cli/base_command.py”, line 20, in <module>
File “/tmp/tmpgvoc4sao/pip-19.0.3-py2.py3-none-any.whl/pip/_internal/download.py”, line 37, in <module>
File “/tmp/tmpgvoc4sao/pip-19.0.3-py2.py3-none-any.whl/pip/_internal/utils/glibc.py”, line 3, in <module>
File “/tmp/python-build.20191023165632.13343/Python-3.7.4/Lib/ctypes/__init__.py”, line 7, in <module>
from _ctypes import Union, Structure, Array
ModuleNotFoundError: No module named ‘_ctypes’
make: *** [install] Error 1

 

解决方法如下:

  1. 先安装: yum install libffi-devel -y
  2. 再安装python: pyenv install 3.7.4

DONE!

如果安装成功后有WARNING提示:

WARNING: The Python bz2 extension was not compiled. Missing the bzip2 lib?
WARNING: The Python readline extension was not compiled. Missing the GNU readline lib?
WARNING: The Python sqlite3 extension was not compiled. Missing the SQLite3 lib?

为避免后续使用中问题,可进一步安装其它依赖包

yum install readline readline-devel readline-static -y

yum install sqlite-devel -y

yum install bzip2-devel bzip2-libs -y

yum install openssl openssl-devel openssl-static -y

OK!!

ImportError: pycurl: libcurl link-time ssl backend (openssl) is different from compile-time ssl backend (none/other)解决

 python  ImportError: pycurl: libcurl link-time ssl backend (openssl) is different from compile-time ssl backend (none/other)解决已关闭评论
10月 082019
 

mac下使用import pycurl,出现: ImportError: pycurl: libcurl link-time ssl backend (openssl) is different from compile-time ssl backend (none/other)

 

使用下面方式解决:

pip uninstall pycurl
pip install --compile --install-option="--with-openssl" pycurl


Python中filter、map、reduce、lambda 介绍

 python  Python中filter、map、reduce、lambda 介绍已关闭评论
9月 242019
 

Python中filter, map, reduce, lambda函数介绍

filter(function, sequence):对sequence中的item依次执行function(item),将执行结果为True的item组成一个List/String/Tuple(取决于sequence的类型)返回:
>>> def f(x): return x % 2 != 0 and x % 3 != 0
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]
>>> def f(x): return x != ‘a’
>>> filter(f, “abcdef”)
‘bcdef’

map(function, sequence) :对sequence中的item依次执行function(item),见执行结果组成一个List返回:
>>> def cube(x): return x*x*x
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>> def cube(x) : return x + x

>>> map(cube , “abcde”)
[‘aa’, ‘bb’, ‘cc’, ‘dd’, ‘ee’]
另外map也支持多个sequence,这就要求function也支持相应数量的参数输入:
>>> def add(x, y): return x+y
>>> map(add, range(8), range(8))
[0, 2, 4, 6, 8, 10, 12, 14]

reduce(function, sequence, starting_value):对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用,例如可以用来对List求和:
>>> def add(x,y): return x + y
>>> reduce(add, range(1, 11))
55 (注:1+2+3+4+5+6+7+8+9+10)
>>> reduce(add, range(1, 11), 20)
75 (注:1+2+3+4+5+6+7+8+9+10+20)

lambda:这是Python支持一种有趣的语法,它允许你快速定义单行的最小函数,类似与C语言中的宏,这些叫做lambda的函数,是从LISP借用来的,可以用在任何需要函数的地方:
>>> g = lambda x: x * 2
>>> g(3)
6
>>> (lambda x: x * 2)(3)
6

我们也可以把filter map reduce 和lambda结合起来用,函数就可以简单的写成一行。
例如
kmpathes = filter(lambda kmpath: kmpath,
map(lambda kmpath: string.strip(kmpath),

string.split(l, ‘:’)))
看起来麻烦,其实就像用语言来描述问题一样,非常优雅。
对 l 中的所有元素以’:’做分割,得出一个列表。对这个列表的每一个元素做字符串strip,形成一个列表。对这个列表的每一个元素做直接返回操作(这个地方可以加上过滤条件限制),最终获得一个字符串被’:’分割的列表,列表中的每一个字符串都做了strip,并可以对特殊字符串过滤

 Posted by at 上午8:09

pip/pip3切换到国内安装源

 python  pip/pip3切换到国内安装源已关闭评论
9月 162019
 

pip/pip3默认安装源国内下载速度实在太慢了,经常出现timeout,可以使用下面的方式切换到ali源,速度还是很给力的!

linux/mac下运行命令
vi ~/.pip/pip.conf

windows:
%HOMEPATH%\pip\pip.ini

然后写入如下内容并保存:
[global]
trusted-host = mirrors.aliyun.com
index-url = https://mirrors.aliyun.com/pypi/simple

Python模板-Mako语法介绍

 python  Python模板-Mako语法介绍已关闭评论
9月 052019
 

资源

官网 http://www.makotemplates.org/

文档 http://docs.makotemplates.org/en/latest/

文档翻译 Mako模板入门 http://help.42qu.com/code/mako.html

安装

pip install mako

HelloWorld

from mako.template import Template

mytemplate = Template("hello world!")
print mytemplate.render()

-------------------------

from mako.template import Template
print Template("hello ${data}!").render(data="world")

语法

输出变量 ${x}

数学计算 ${1+1}
the contents within the ${} tag are evaluated by Python directly, so full expressions are OK

filter
${"test"|u}
${"test"|u,trim}
内置filter列表
    u : URL escaping, provided by urllib.quote_plus(string.encode('utf-8'))
    h : HTML escaping, provided by markupsafe.escape(string)
    x : XML escaping
    trim : whitespace trimming, provided by string.strip()
    entity : produces HTML entity references for applicable strings, derived from htmlentitydefs
    unicode (str on Python 3): produces a Python unicode string (this function is applied by default)
    decode.<some encoding> : decode input into a Python unicode with the specified encoding
    n : disable all default filtering; only filters specified in the local expression tag will be applied.

分支
% if x == 5:
    abcd
% endif

循环
% for a in ['1', '2', '3']:
    % if a == '1':
      abc
    % elif a == '2':
      def
    % else:
      gh
    % endif
$ endfor

Python语法
this is a template
<%
    x = db.get_resource('foo')
    y = [z.element for z in x if x.frobnizzle==5]
%>
% for elem in y:
    element: ${elem}
% endfor

换行

加 / 强制不换行


设置变量
% for item in ('apple', 'banana'):
    <%
        isBanana = False
    %>
    % if item == 'banana':
    <%
        isBanana = True
    %>
    %endif
    % if isBanana:
        <span> Bought a banana</span>
    %endif
%endfor

注释

## 这是一个注释.
...text ...

多行
<%doc>
这里是注释
更多注释
</%doc>

模块级别语句

<% %> 的一个变体是 <%! %>,代表模块级别的代码块。其中的代码会在模板的模块级别执行,而不是在模板的 rendering 函数中。

<%!
import mylib
import re

def filter(text):
    return re.sub(r'^@', '', text)
%>

标签

定义了当前模板的总体特性,包括缓存参数,以及模板被调用时期待的参数列表(非必须)
<%page args="x, y, z='default'"/>
<%page cached="True" cache_type="memory"/>


<%include file="header.html"/>
hello world
<%include file="footer.html"/>

%def 标签用于定义包含一系列内容的一个 Python 函数,此函数在当前模板的其他某个地方被调用到
<%def name="myfunc(x)">
this is myfunc, x is ${x}
</%def>
${myfunc(7)}

<%block filter="h">
some <html> stuff.
</%block>
<%block name="header">
    <h2><%block name="title"/></h2>
</%block>

Mako 中的 %namespace 等价于 Python 里的 import 语句。它允许访问其他模板文件的所有 rendering 函数和元数据
<%namespace file="functions.html" import="*"/>

<%inherit file="base.html"/>

处理多行注释:
<%doc>
    these are comments
    more comments
</%doc>

该标签使得 Mako 的词法器对模板指令的常规解析动作停止,并以纯文本的形式返回其整个内容部分
<%text filter="h">
heres some fake mako ${syntax}
<%def name="x()">${x}</%def>
</%text>

有时你想中途停止执行一个模板或者 <%def> 方法,只返回已经收集到的文本信息,可以通过在 Python 代码块中使用 return 语句来完成

% if not len(records):
    No records found.
    <% return %>
% endif

文件template

为提高性能,从文件中加载的 Template, 可以将它产生的模块的源代码以普通 python 模块文件的形式(.py),

缓存到文件系统中。只要加一个参数 module_directory 即可做到这一点:

from mako.template import Template

mytemplate = Template(filename='/docs/mytmpl.txt', module_directory='/tmp/mako_modules')
print mytemplate.render()

当上述代码被 render 的时候,会创建文件 /tmp/mako_modules/docs/mytmpl.txt.py.

下一次 Template 对象被用同样参数调用的时候,就会直接重用该模块文件。

文件TemplateLookup

#有一个对 header.txt 文件的包含引用。而从何处去查找 header.txt, 则由 TemplateLookup 指明,是 "/docs" 目录
from mako.template import Template
from mako.lookup import TemplateLookup

mylookup = TemplateLookup(directories=['/docs'])
mytemplate = Template("""<%include file="header.txt"/> hello world!""", lookup=mylookup)


--------------

#可以直接通过 TemplateLookup 来获取模板对象,利用 TemplateLookup 的 get_template 方法,
#并传递模板的 URI 作为参数
mylookup = TemplateLookup(directories=['/docs'], output_encoding='utf-8', encoding_errors='replace')
mytemplate = mylookup.get_template("foo.txt")
print mytemplate.render()

-------------
参数
mylookup = TemplateLookup(directories=['/docs'], output_encoding='utf-8', encoding_errors='replace', , collection_size=500)
TemplateLookup 同时也会在内存中缓存一组模板,所以并不是每一次请求都会导致模板的重新编译和模块重新加载。默认 TemplateLookup 的大小没有限制,但你可以通过 collection_size 参数来限制它
以上的 lookup 会持续加载模板到内存中,直到达到 500 的时候,它就会清除掉一定比例的模板缓存项,根据“最近最少访问”原则

另一个 TemplateLookup 相关的标志是  filesystem_checks. 默认为 True,
每一次 get_template() 方法返回模板后,原始的模板文件的 revision time 会和上次加载模板的时间做对比,
如果文件更新,则会加载其内容,并重新编译该模板。
在生产环境下,设置 filesystem_checks 为 False 可以带来一定的性能提升(和具体的文件系统有关)

自己创建context

from mako.template import Template
from mako.runtime import Context
from StringIO import StringIO

mytemplate = Template("hello, ${name}!")
buf = StringIO()
ctx = Context(buf, name="jack")
mytemplate.render_context(ctx)
print buf.getvalue()

其他

1.解决mako中文乱码问题

TemplateLookup(... , output_encoding='utf-8', ...)
Template(..., input_encoding='utf-8')
又在mako的模板文件的首行添加
## -*- encoding:utf8 -*-

单星号与双星号(*和**)在python函数中的应用

 python  单星号与双星号(*和**)在python函数中的应用已关闭评论
9月 042019
 

想不到单星号还能用于解压参数列表!!

在python的函数中经常能看到输入的参数前面有一个或者两个星号:例如

这两种用法其实都是用来将任意个数的参数导入到python函数中。

单星号(*):*args
将所以参数以元组(tuple)的形式导入:
例如:

双星号(**):**kwargs
将参数以字典的形式导入

此外,单星号的另一个用法是解压参数列表:(这种用法让人惊艳!)

当然这两个用法可以同时出现在一个函数中:例如

centos 7.6 下python2.7 安装 MySQL-python问题解决

 python  centos 7.6 下python2.7 安装 MySQL-python问题解决已关闭评论
5月 162019
 

centos7.6 下python2.7版本使用pip install MySQL-python可能出现下面错误:

可能问题一:  ERROR: Complete output from command python setup.py egg_info:
ERROR: sh: mysql_config: 未找到命令

解决办法: yum install mysql-devel

 

再次安装,没问题最好, 如果出现下面问题:

 可能问题二:_mysql.c:29:20: 致命错误:Python.h:没有那个文件或目录
#include “Python.h”

解决办法: yum install python-devel

 

再次安装 : pip install MySQL-python

问题解决!

 

mac下安装MySQL_python时出现_mysql.c:44:10: fatal error: ‘my_config.h’ file not found

 python  mac下安装MySQL_python时出现_mysql.c:44:10: fatal error: ‘my_config.h’ file not found已关闭评论
12月 242018
 

mac下使用brew install mysql,安装pip install MySQL-python出现下面错误提示: 

    _mysql.c:44:10: fatal error: ‘my_config.h’ file not found
    #include “my_config.h”
             ^~~~~~~~~~~~~
    1 error generated.
    error: command ‘cc’ failed with exit status 1

网上很多方法都无效,使用下面都步骤可解决

brew remove mysql
brew install [email protected]
brew link --force [email protected]
pip install msyql-python (如权限不足,请使用sudo)

python3安装turtle提示错误:Command “python setup.py egg_info” failed with error code 1 错误解决

 python  python3安装turtle提示错误:Command “python setup.py egg_info” failed with error code 1 错误解决已关闭评论
12月 022018
 

 使用pip在python3.6.4安装turtle时出现下面错误:

Collecting turtle

  Using cached https://files.pythonhosted.org/packages/ff/f0/21a42e9e424d24bdd0e509d5ed3c7dfb8f47d962d9c044dba903b0b4a26f/turtle-0.0.2.tar.gz

    Complete output from command python setup.py egg_info:

    Traceback (most recent call last):

      File “<string>”, line 1, in <module>

      File “/tmp/pip-install-hpqxw6_s/turtle/setup.py”, line 40

        except ValueError, ve:

                         ^

    SyntaxError: invalid syntax

    —————————————-

Command “python setup.py egg_info” failed with error code 1 in /xxx/turtle/

其实仔细看是一个语法错误,可以根据上面的url(https://files.pythonhosted.org/packages/ff/f0/21a42e9e424d24bdd0e509d5ed3c7dfb8f47d962d9c044dba903b0b4a26f/turtle-0.0.2.tar.gz)自己先把包下载到本地,然后解压到本地再安装,提示的错误在解压的setup.py文件里面,

  1. 打开setup.py文件,第40行修改为 except (ValueError, ve):
    原来的是Python2的写法,没有括号,加了括号之后Python3就能用了。
  2. 用pip3安装: pip install -e turtle-0.0.2
    -e后面接上我们修改过setup.py文件的目录。
  3. 这样就搞定了。

真正实现tornado异步非阻塞(gen.coroutine,ThreadPoolExecutor,Celery)

 python, tornado  真正实现tornado异步非阻塞(gen.coroutine,ThreadPoolExecutor,Celery)已关闭评论
11月 292018
 

真正实现tornado异步非阻塞,来自:https://blog.csdn.net/u013038616/article/details/72821600

其中 Tornado 的定义是 Web 框架和异步网络库,其中他具备有异步非阻塞能力,能解决他两个框架请求阻塞的问题,在需要并发能力时候就应该使用 Tornado

但是在实际使用过程中很容易把 Tornado 使用成异步阻塞框架,这样对比其他两大框架没有任何优势而言,本文就如何实现真正的异步非阻塞记录。

以下使用的 Python 版本为 2.7.13 
平台为 Macbook
Pro 2016

使用 gen.coroutine 异步编程


Tornado 中两个装饰器:

  • tornado.web.asynchronous

  • tornado.gen.coroutine

asynchronous 装饰器是让请求变成长连接的方式,必须手动调用 self.finish() 才会响应

class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        # bad 
        self.write("Hello, world")

asynchronous 装饰器不会自动调用self.finish() ,如果没有没有指定结束,该长连接会一直保持直到 pending 状态。 

没有调用self.finish()
所以正确是使用方式是使用了 asynchronous 需要手动 finish

class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        self.write("Hello, world")
        self.finish()
coroutine 装饰器是指定改请求为协程模式,说明白点就是能使用 yield 配合 Tornado 编写异步程序。

Tronado 为协程实现了一套自己的协议,不能使用 Python 普通的生成器。

在使用协程模式编程之前要知道如何编写 Tornado 中的异步函数,Tornado 提供了多种的异步编写形式:回调、Future、协程等,其中以协程模式最是简单和用的最多。

编写一个基于协程的异步函数同样需要 coroutine 装饰器

@gen.coroutine
def sleep(self):
    yield gen.sleep(10)
    raise gen.Return([1, 2, 3, 4, 5])

这就是一个异步函数,Tornado 的协程异步函数有两个特点:
  • 需要使用 coroutine 装饰器

  • 返回值需要使用 raise
    gen.Return()
     
    当做异常抛出

返回值作为异常抛出是因为在 Python
3.2
之前生成器是不允许有返回值的。

使用过 Python 生成器应该知道,想要启动生成器的话必须手动执行 next() 方法才行,所以这里的 coroutine 装饰器的其中一个作用就是在调用这个异步函数时候自动执行生成器。

使用 coroutine 方式有个很明显是缺点就是严重依赖第三方库的实现,如果库本身不支持 Tornado 的异步操作再怎么使用协程也是白搭依然会是阻塞的,放个例子感受一下。

import time
import logging
import tornado.ioloop
import tornado.web
import tornado.options
from tornado import gen

tornado.options.parse_command_line()

class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        self.write(“Hello, world”)
        self.finish()

class NoBlockingHnadler(tornado.web.RequestHandler):
    @gen.coroutine
    def get(self):
        yield gen.sleep(10)
        self.write(‘Blocking Request’)

class BlockingHnadler(tornado.web.RequestHandler):
    def get(self):
        time.sleep(10)
        self.write(‘Blocking Request’)

def make_app():
    return tornado.web.Application([
        (r”/”, MainHandler),
        (r”/block”, BlockingHnadler),
        (r”/noblock”, NoBlockingHnadler),
    ], autoreload=True)

if __name__ == “__main__”:
    app = make_app()
    app.listen(8000)
    tornado.ioloop.IOLoop.current().start()


为了显示更明显设置了 10

当我们使用 yield
gen.sleep(10)
 
这个异步的 sleep 时候其他请求是不阻塞的。 
非阻塞效果图
当使用 time.sleep(10) 时候会阻塞其他的请求。 
阻塞效果图
这里的异步非阻塞是针对另一请求来说的,本次的请求该是阻塞的仍然是阻塞的。

gen.coroutine Tornado
3.1
后会自动调用 self.finish() 结束请求,可以不使用 asynchronous 装饰器。

所以这种实现异步非阻塞的方式需要依赖大量的基于 Tornado 协议的异步库,使用上比较局限,好在还是有一些可以用的异步库

基于线程的异步编程


使用 gen.coroutine 装饰器编写异步函数,如果库本身不支持异步,那么响应任然是阻塞的。

Tornado 中有个装饰器能使用 ThreadPoolExecutor 来让阻塞过程编程非阻塞,其原理是在 Tornado 本身这个线程之外另外启动一个线程来执行阻塞的程序,从而让 Tornado 变得阻塞。

futures Python3 是标准库,但是在 Python2 中需要手动安装 
pip
install futures

测试代码

import logging
import tornado.ioloop
import tornado.web
import tornado.options
from tornado import gen
from tornado.concurrent import run_on_executor
from concurrent.futures import ThreadPoolExecutor

tornado.options.parse_command_line()

class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        self.write(“Hello, world”)
        self.finish()

class NoBlockingHnadler(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(4)

    @run_on_executor
    def sleep(self, second):
        time.sleep(second)
        return second

    @gen.coroutine
    def get(self):
        second = yield self.sleep(5)
        self.write(‘noBlocking Request: {}’.format(second))

def make_app():
    return tornado.web.Application([
        (r”/”, MainHandler),
        (r”/noblock”, NoBlockingHnadler),
    ], autoreload=True)

if __name__ == “__main__”:
    app = make_app()
    app.listen(8000)
    tornado.ioloop.IOLoop.current().start()

ThreadPoolExecutor 是对标准库中的 threading 的高度封装,利用线程的方式让阻塞函数异步化,解决了很多库是不支持异步的问题。 

这里写图片描述
但是与之而来的问题是,如果大量使用线程化的异步函数做一些高负载的活动,会导致该 Tornado 进程性能低下响应缓慢,这只是从一个问题到了另一个问题而已。

所以在处理一些小负载的工作,是能起到很好的效果,让 Tornado 异步非阻塞的跑起来。

但是明明知道这个函数中做的是高负载的工作,那么你应该采用另一种方式,使用 Tornado 结合 Celery 来实现异步非阻塞。

基于 Celery 的异步编程

Celery 是一个简单、灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的任务队列,同时也支持任务调度。 
Celery
并不是唯一选择,你可选择其他的任务队列来实现,但是 Celery Python 所编写,能很快的上手,同时 Celery 提供了优雅的接口,易于与 Python
Web
框架集成等特点。

Tornado 的配合可以使用 tornado-celery ,该包已经把 Celery 封装到 Tornado 中,可以直接使用。

实际测试中,由于 tornado-celery 很久没有更新,导致请求会一直阻塞,不会返回

解决办法是:

celery 降级到 3.1
pip install celery==3.1 
pika 降级到 0.9.14
pip install pika==0.9.14

import time
import logging
import tornado.ioloop
import tornado.web
import tornado.options
from tornado import gen

import tcelery, tasks

tornado.options.parse_command_line()
tcelery.setup_nonblocking_producer()


class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        self.write("Hello, world")
        self.finish()


class CeleryHandler(tornado.web.RequestHandler):
    @gen.coroutine
    def get(self):
        response = yield gen.Task(tasks.sleep.apply_async, args=[5])
        self.write('CeleryBlocking Request: {}'.format(response.result))


def make_app(): 
    return tornado.web.Application([
        (r"/", MainHandler),
        (r"/celery-block", CeleryHandler),
    ], autoreload=True)

if __name__ == "__main__":
    app = make_app()
    app.listen(8000)
    tornado.ioloop.IOLoop.current().start()
import os
import time
from celery import Celery
from tornado import gen

celery = Celery("tasks", broker="amqp://")
celery.conf.CELERY_RESULT_BACKEND = os.environ.get('CELERY_RESULT_BACKEND', 'amqp')

@celery.task
def sleep(seconds):
    time.sleep(float(seconds))
    return seconds

if __name__ == "__main__":
    celery.start()

这里写图片描述

Celery Worker 运行在另一个进程中,独立于 Tornado 进程,不会影响 Tornado 运行效率,在处理复杂任务时候比进程模式更有效率。

总结


方法

优点

缺点

可用性

gen.coroutine

简单、优雅

需要异步库支持

★★☆☆☆

线程

简单

可能会影响性能

★★★☆☆

Celery

性能好

操作复杂、版本低

★★★☆☆

目前没有找到最佳的异步非阻塞的编程模式,可用的异步库比较局限,只有经常用的,个人编写异步库比较困难。

推荐使用线程和 Celery 的模式进行异步编程,轻量级的放在线程中执行,复杂的放在 Celery 中执行。当然如果有异步库使用那最好不过了。

Python
3
中可以把 Tornado 设置为 asyncio 的模式,这样就使用
兼容
asyncio 模式的库,这应该是日后的方向。

Reference


celery  [‘selərɪ]  详细X

基本翻译
n. [园艺] 芹菜

网络释义
Celery: 芹菜
celery salt: 香芹盐
celery sticks: 芹菜杆