Flask 框架
装饰器知识回顾
http://www.cnblogs.com/0bug/p/7978595.html
普通装饰器格式:
def wrapper(func):
def inner(*args, **kwargs):
return func(*args, **kwargs) return inner @wrapper # index = wrapper(index)
def index(request):
pass
带参数的装饰器
def d(a=None): # 定义一个外层函数,给装饰器传参数a默认是None
def foo(func): # foo是我们原来的装饰器函数,func是被装饰的函数
def bar(*args, **kwargs): # args和kwargs是被装饰器函数的参数
# 根据装饰器的参数做一些逻辑判断
if a:
print("{}您好".format(a))
else:
print("您好")
# 调用被装饰的函数,接收参数args和kwargs
func(*args, **kwargs) return bar return foo @d() # 不给装饰器传参数,使用默认的'None'参数
def wish(name):
print("{}祝您前程似锦".format(name)) @d("亲爱的园友") # 给装饰器传一个参数
def greet_wish(name):
print("{}祝您前程似锦".format(name)) if __name__ == '__main__':
wish("lcg")
print('-' * 50)
greet_wish("lcg")
输出
您好
lcg祝您前程似锦
--------------------------------------------------
亲爱的园友您好
lcg祝您前程似锦
flask路由本质就是带参数的装饰器:
@web.route('/index')
def index():
return 'Index' def route(self, rule, **options):
def decorator(f):
endpoint = options.pop("endpoint", f.__name__)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
Flask,Django,Tornado三大框架对比
Django:内部包含了非常多的组件比如:orm,form,modelForm,缓存,session等等。
Flask:短小精悍,内部没有包含多少组件,但是第三方的组件是非常丰富的。路由比较特殊:基于装饰器来实现,但是究其本质还是通过add_url_rule来实现
Tornado:是异步非阻塞框架,node.js也是异步非阻塞。
初识Flask
安装:pip install flask
模板引擎则使用Jinjia2
Flask使用BSD授权
WSGI工具箱采用Werkzeug
Werkzeug示例(Flask的本质):
from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple @Request.application
def hello(request):
return Response('Hello World!') if __name__ == '__main__':
run_simple('localhost', 4000, hello)
wsgiref示例:
from wsgiref.simple_server import make_server def runserver(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ] if __name__ == '__main__':
httpd = make_server('', 8000, runserver)
httpd.serve_forever()
上面的Werkzeug和wsgiref的本质也都是基于Socket实现的。
一个flask示例:
from flask import Flask # 实例化Flask对象
app = Flask(__name__) # 将 '/' 和 函数index的对应关系添加到路由中。
"""
{
‘/’:index
}
"""
@app.route('/')
def index():
return 'Hello World!' if __name__ == '__main__':
# 监听用户请求
# 如果有用户请求到来,则执行app的__call__()方法
# app.__call__()
app.run()
上面的代码进行源码分析:
app=Flask(__name__),实例化Flask对象。
然后通过装饰器将 '/' 和 函数index的对应关系添加到路由中。
app.run()源码中 run_simple(host, port, self, **options),其中self就是Flask的实例化对象,而app就是Flask()的实例化对象,从上面的Werkzeug示例中我们看到run_simple的第三个参数就是一个方法(函数),那么这里就相当于app(),而对象加括号就相当于执行类对象的__call__()方法。即是执行了Flask类的实例化对象也就是app对象的__call__()方法。__call__()方法中的具体细节,后面在做探讨。
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`."""
return self.wsgi_app(environ, start_response)
Flask快速入门,实现登录认证
from flask import Flask, render_template, request, redirect, session, url_for app = Flask(__name__)
app.debug = True
app.secret_key = 'lcgasdasdadhahah' USERS = {
: {'name': '漩涡鸣人', 'age': , 'gender': '男', 'text': '我一直都是说道做到的,因为这就是我的忍道'},
: {'name': '藤原佐为', 'age': , 'gender': '男', 'text': '我只知道自己执黑子时从来没输过!'},
: {'name': '李舜生', 'age': , 'gender': '女', 'text': '总有一天我会撕去这满是虚假的星空'}} def wrapper(func):
def inner(*args, **kwargs):
if not session.get("user_info"):
return redirect(url_for('login'))
ret = func(*args, **kwargs)
return ret return inner @app.route('/login', methods=['GET', 'POST'], endpoint='login')
def login():
if request.method == "GET":
return render_template('login.html')
else:
user = request.form.get('user')
pwd = request.form.get('pwd')
if user == 'lcg' and pwd == '':
session['user_info'] = user
return redirect(url_for('index'))
return render_template('login.html', error='用户名或密码错误') @app.route('/detail/<int:nid>', methods=['GET', 'POST'], endpoint='detail')
@wrapper
def detail(nid):
info = USERS.get(nid)
return render_template('detail.html', info=info) @app.route('/index', methods=['GET'], endpoint='index')
@wrapper
def index():
return render_template('index.html', user_dict=USERS) if __name__ == '__main__':
app.run()
demo.py
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户登录</h1>
<form method="post">
<input type="text" name="user">
<input type="text" name="pwd">
<input type="submit" value="登录">{{error}}
</form>
</body>
</html>
templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户列表</h1>
<table>
{% for k,v in user_dict.items() %}
<tr>
<td>{{k}}</td>
<td>{{v.name}}</td>
<td>{{v['name']}}</td>
<td>{{v.get('name')}}</td>
<td><a href="/detail/{{k}}">查看详细</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>
templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>详细信息 {{info.name}}</h1>
<div>
{{info.text}}
</div>
</body>
</html>
templates/index.html
配置文件
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
{
'DEBUG': get_debug_flag(default=False), 是否开启Debug模式
'TESTING': False, 是否开启测试模式
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
'SECRET_KEY': None,
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
'USE_X_SENDFILE': False,
'LOGGER_NAME': None,
'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None,
'APPLICATION_ROOT': None,
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS': False,
'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http',
'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
}
方式一:
app.config['DEBUG'] = True PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...)
方式二:
app.config.from_pyfile("python文件名称") 如:
settings.py
DEBUG = True app.config.from_pyfile("settings.py") app.config.from_envvar("环境变量名称")
环境变量的值为python文件名称名称,内部调用from_pyfile方法 app.config.from_json("json文件名称")
JSON文件名称,必须是json格式,因为内部会执行json.loads app.config.from_mapping({'DEBUG': True}) # 字典格式
方式三(最常用的):
app.config.from_object("python类或类的路径") 如:
app.config.from_object('pro_flask.settings.TestingConfig') settings.py中: class Config(object):
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config):
DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config):
DEBUG = True class TestingConfig(Config):
TESTING = True PS: 从sys.path中已经存在路径开始写 PS: settings.py文件默认路径要放在程序root_path目录,
如果instance_relative_config为True,则就是instance_path目录
路由系统
路由比较特殊,:是基于装饰器实现的,但是究其本质还是有add_url_rule实现的。
@app.route('/user/<username>') @app.route('/post/<int:post_id>') @app.route('/post/<float:post_id>') @app.route('/post/<path:path>') @app.route('/login', methods=['GET', 'POST'], endpoint='login')
常用路由系统有以上五种,所有的路由系统都是基于以下对应关系来处理(可以扩展以正则表达式,需继承BaseConverter并实现相应方法):
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
from flask import Flask, url_for
from werkzeug.routing import BaseConverter app = Flask(import_name=__name__) class RegexConverter(BaseConverter):
"""
自定义URL匹配正则表达式
""" def __init__(self, map, regex):
super(RegexConverter, self).__init__(map)
self.regex = regex def to_python(self, value):
"""
路由匹配时,匹配成功后传递给视图函数中参数的值
"""
return int(value) # 将字符串转换为int类型 def to_url(self, value):
"""
使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
"""
val = super(RegexConverter, self).to_url(value)
return val # 添加到flask中
app.url_map.converters['regex'] = RegexConverter @app.route('/index/<regex("\d+"):nid>')
def index(nid):
print(url_for('index', nid=nid))
return 'Index' if __name__ == '__main__':
app.run()
自定制路由扩展,对正则表达式的支持
上面的扩展正则的类要继承BaseConverter,并且需要实现to_python方法,to_python方法在路由配合成功传递给函数之前,在此中间执行,进行了一次封装,to_url是在使用url_for反向生成URL时,经过该函数处理,返回的值用于生成URL的参数。
路由示例:
from flask import Flask app = Flask(__name__) def auth(func):
def inner(*args, **kwargs):
print('before')
result = func(*args, **kwargs)
print('after')
return result return inner @app.route('/index', methods=['GET', 'POST'], endpoint='index')
@auth
def index():
print('index')
return 'Index' if __name__ == '__main__':
app.run()
装饰器的位置,放在router后面
路由的本质
def route(self, rule, **options):
def decorator(f):
endpoint = options.pop('endpoint', None)
self.add_url_rule(rule, endpoint, f, **options)
return f return decorator @setupmethod
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
options['endpoint'] = endpoint
methods = options.pop('methods', None) if methods is None:
methods = getattr(view_func, 'methods', None) or ('GET',)
if isinstance(methods, string_types):
raise TypeError('Allowed methods have to be iterables of strings, '
'for example: @app.route(..., methods=["POST"])')
methods = set(item.upper() for item in methods) required_methods = set(getattr(view_func, 'required_methods', ())) provide_automatic_options = getattr(view_func,
'provide_automatic_options', None) if provide_automatic_options is None:
if 'OPTIONS' not in methods:
provide_automatic_options = True
required_methods.add('OPTIONS')
else:
provide_automatic_options = False methods |= required_methods rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_options self.url_map.add(rule)
if view_func is not None:
old_func = self.view_functions.get(endpoint)
if old_func is not None and old_func != view_func:
raise AssertionError('View function mapping is overwriting an '
'existing endpoint function: %s' % endpoint)
self.view_functions[endpoint] = view_func
部分源码
@app.route('/index', methods=["GET", "POST"], endpoint='index')
def index():
return "Hello Index"
endpoint是别名,不写别名的话。别名默认就是被装饰的函数名。根据源码分析,也可以写成下面的形式(这样就跟Django的路由很像了):
def index():
return "Hello Index" app.add_url_rule('/index', 'index', index, methods=["GET", "POST"]) # url,别名,函数名,方法
Flask的CBV写法
Flask中的CBV原理也是反射,跟Django的CBV类似。Flask里面继承views.MethodView或者继承views.View然后自定义dispatch_request方法。
from flask import Flask, views app = Flask(__name__) def auth(func):
def inner(*args, **kwargs):
print('before')
result = func(*args, **kwargs)
print('after')
return result return inner def auth2(func):
def inner(*args, **kwargs):
print('before2')
result = func(*args, **kwargs)
print('after2')
return result return inner class IndexView(views.MethodView):
methods = ['GET']
decorators = [auth, auth2] # 装饰器的用法 decorators=[装饰器名,] def get(self):
return 'Index.GET' def post(self):
return 'Index.POST' app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name是别名 if __name__ == '__main__':
app.run()
CBV+两个装饰器修饰
from flask import Flask, views app = Flask(__name__) class IndexView(views.View):
methods = ['GET'] def dispatch_request(self):
return 'Index!' app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name是别名 if __name__ == '__main__':
app.run()
CBV继承View里面自定义dispatch_request
@app.route和app.add_url_rule参数:
def route(self, rule, **options):pass
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):pass
rule, URL规则 view_func, 视图函数名称 defaults = None, 默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}为函数提供参数 endpoint = None, 别名,用于反向生成URL,即: url_for('别名') methods = None, 允许的请求方式,如:["GET", "POST"] strict_slashes = None, 对URL最后的 / 符号是否严格要求,
如:
@app.route('/index', strict_slashes=False)
访问 http://www.xx.com/ndex/ 或 http://www.xx.com/index 均可 @app.route('/index', strict_slashes=True)
仅可访问 http://www.xx.com/index redirect_to = None, 重定向到指定地址(一般公司换网址了,旧网站也可能被顾客访问)
如:
@app.route('/index/<int:nid>', redirect_to='/home/<nid>')
或
def func(adapter, nid):
return "/home/888"
@app.route('/index/<int:nid>', redirect_to=func) subdomain = None, 子域名访问
from flask import Flask, views, url_for app = Flask(import_name=__name__)
app.config['SERVER_NAME'] = 'lcg.com:5000' # SERVER_NAME是固定的写法 @app.route("/", subdomain="admin")
def static_index():
"""Flask supports static subdomains
This is available at static.your-domain.tld"""
return "static.your-domain.tld" @app.route("/dynamic", subdomain="<name>")
def username_index(name):
"""Dynamic subdomains are also supported
Try going to user1.your-domain.tld/dynamic"""
return name + ".your-domain.tld"
模板语言
1.Flask使用的是Jinja2模板,所以其语法大部分和Django类似,Django的模板语法:http://www.cnblogs.com/0bug/p/7993588.html
2、自定义模板方法
创建一个函数并通过参数的形式传入render_template,渲染HTML标签,如:
from flask import Flask, render_template app = Flask(__name__) def hello():
return '<h1>hello lcg</h1>' @app.route('/test', methods=['GET', 'POST'])
def test():
return render_template('test.html', ww=hello) if __name__ == '__main__':
app.run() templates/test.html {{ww()|safe}}
方式一, |safe
from flask import Flask, render_template, Markup app = Flask(__name__) def hello():
return Markup('<h1>hello lcg</h1>') @app.route('/test', methods=['GET', 'POST'])
def test():
return render_template('test.html', ww=hello) if __name__ == '__main__':
app.run() templates/test.html
{{ww()}}
方式二,Markup(类似Django中的mark_safe)
3.宏定义
<body> {% macro func(name, type='text', value='') %}
<input type="{{ type }}" name="{{ name }}1" value="{{ value }}">
<input type="{{ type }}" name="{{ name }}2" value="{{ value }}">
<input type="{{ type }}" name="{{ name }}3" value="{{ value }}">
<input type="{{ type }}" name="{{ name }}4" value="{{ value }}">
{% endmacro %} {{ func('lcg') }} </body>
请求和响应相关
from flask import Flask
from flask import request
from flask import render_template
from flask import redirect
from flask import make_response
from flask import jsonify app = Flask(__name__) @app.route('/test', methods=['GET', "POST"])
def login():
# 请求相关信息
# request.method
# request.args
# request.form
# request.values
# request.cookies
# request.headers
# request.path
# request.full_path
# request.script_root
# request.url
# request.base_url
# request.url_root
# request.host_url
# request.host
# request.files
# obj = request.files['the_file_name']
# obj.save('/var/www/uploads/' + secure_filename(obj.filename)) # 响应相关信息
# return "字符串"
# return render_template('html模板路径',**{})
# return redirect('/index.html') # response = make_response('hello lcg')
# response = make_response(render_template('index.html'))
# response是flask.wrappers.Response类型
# response.set_cookie('key', 'value') # 加cookie
# response.delete_cookie('key')
# response.headers['X-Something'] = 'A value'
# return response return "内容" if __name__ == '__main__':
app.run()
--
Session
除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。
设置:session['username'] = 'xxx'
- 删除:session.pop('username', None)
from flask import Flask, session, redirect, url_for, escape, request app = Flask(__name__)
# set the secret key. keep this really secret:
app.secret_key = 'dsaba sca6789pinkNa:xL C[N' @app.route('/')
def index():
if 'username' in session:
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in' @app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form action="" method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
''' @app.route('/logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
if __name__ == '__main__':
app.run()
Session的基本用法
闪现,flash&get_flashed_messages
用途:对临时数据操作,如显示错误信息。
from flask import Flask, flash, get_flashed_messages app = Flask(__name__) app.secret_key = 'wertyuhgfdrtyujnbvghuij' # flash操作需要添加 secret_key @app.route('/set')
def setflash():
flash('世界感受痛苦吧') # 向某个地方设置(flash)一个值
return 'Hello World!' @app.route('/get')
def getflash():
data = get_flashed_messages() # 从某个地方获取(flash)设置过的值,并清除
print(data)
return 'Hello World!' if __name__ == '__main__':
app.run()
请求的扩展(类似Django中间件)
@app.before_request 每次请求进来之前执行
@app.after_request 每次请求进来之后执行
@app.errorhandler(404) 引发404错误时候执行
@app.before_first_request 第一次请求进来之前执行(如创建数据库连接池这样的业务)
@app.template_global() 调用方法{{函数名(参数1,参数2)}}
@app.template_filter() 调用方法{{参数1|函数名(参数2,参数3))}}
示例
from flask import Flask, Request, render_template app = Flask(__name__, template_folder='templates')
app.debug = True @app.before_first_request
def before_first_request1():
print('before_first_request1') @app.before_first_request
def before_first_request2():
print('before_first_request2') @app.before_request
def process_request1():
print('process_request1') @app.before_request
def process_request2():
print('process_request2') @app.after_request
def process_response1(response):
print('process_response1', response)
return response @app.after_request
def process_response2(response):
print('process_response2', response)
return response @app.errorhandler(404)
def page_not_found(error):
return 'This page does not exist', 404 @app.template_global()
def global_func(a1, a2):
return a1 + a2 @app.template_filter()
def filter_func(a1, a2, a3):
return a1 + a2 + a3 @app.route('/')
def hello_world():
return render_template('hello.html') if __name__ == '__main__':
app.run() '''
templates/hello.html <body> {{global_func(1,2)}} {{1|filter_func(2,3)}} </body>
'''
在process_request1处用return拦截:
@app.before_request
def process_request1():
print('process_request1')
return '被process_request1拦截'
访问错误,引发异常的时候执行相关的@app.errorhandler(错误码)修饰的函数:
用户认证用请求扩展的方式替代之前的装饰器(类似Django的中间件的用法):
@app.before_request
def process_request(*args, **kwargs):
if request.path == url_for('login'):
return None
if session.get("user_info"):
return None
return redirect(url_for('login'))
Flask的中间件
前面的初识flask那里已经介绍了,请求入口在app.run(), 实质上是执行run_simple(host, port, self, **options),这个run_simple就是Flask的WSGI工具箱Werkzeug中的run_simple的,它的第三个参数是一个加括号的也就是self(),也就是app()。而app又是Flask类的实例化对象,对象加扩至,执行的是对象的__call__()方法。我们在源码中就可以找到def __call__(self, environ, start_response):return self.wsgi_app(environ, start_response),中间件的原理就在这,用面向对象的思想重写__call__()方法:
from flask import Flask app = Flask(__name__) @app.route('/')
def hello():
return 'hello lcg' class MiddleWare:
def __init__(self, wsgi_app):
self.wsgi_app = wsgi_app def __call__(self, environ, start_response):
print('请求开始之前')
ret = self.wsgi_app(environ, start_response)
print('请求结束之后')
return ret if __name__ == "__main__":
app.wsgi_app = MiddleWare(app.wsgi_app)
app.run()
flask中间件写法,重写__call__()方法
蓝图(Blueprint)
蓝图实现了应用的模块化,使用蓝图让应用层次清晰,开发者可以很容易的开发和维护项目。
简单的蓝图:
from flask import Blueprint
from flask import render_template # 创建一个blueprint对象。第一个参数可看做该blueprint对象的姓名
# 在一个app里,姓名不能与其余的Blueprint对象姓名重复,也不能与函数名重复
# 第二个参数__name__用作初始化
account = Blueprint('account', __name__) @account.route('/login', methods=['GET', "POST"])
def login():
return render_template('login.html')
account.py
from flask import Blueprint blog_bp = Blueprint('blog_bp', __name__) @blog_bp.route('/blog', methods=['GET', "POST"])
def blog():
return 'blog'
blog.py
from flask import Blueprint user_bp = Blueprint('user_bp', __name__) @user_bp.route('/user', methods=['GET', "POST"])
def user():
return 'user'
user.py
from flask import Flask app = Flask(__name__, template_folder='templates',
static_folder='statics',
static_url_path='/static') from .views.account import account
from .views.blog import blog_bp
from .views.user import user_bp app.register_blueprint(account, url_prefix='/simple') # 注册蓝图,simple/login
app.register_blueprint(blog_bp, url_prefix='/simple')
app.register_blueprint(user_bp, url_prefix='/simple')
simple_bp/__init__.py
from simple_bp import app if __name__ == '__main__':
app.run()
run.py
复杂的项目(多个应用),蓝图写法:
from flask import Blueprint admin = Blueprint(
'admin',
__name__,
template_folder='templates',
static_folder='static'
) from . import views
admin/__init__.py
from . import admin @admin.route('/index')
def index():
return 'Admin.Index'
admin/views.py
from flask import Blueprint web = Blueprint(
'web',
__name__,
template_folder='templates',
static_folder='static'
) from . import views
Web/__init__.py
from . import web @web.route('/index')
def index():
return 'Web.Index'
Web/views.py
from flask import Flask
from .admin import admin
from .web import web app = Flask(__name__)
app.debug = True app.register_blueprint(admin, url_prefix='/admin')
app.register_blueprint(web)
complex_bp/__init__.py
from complex_bp import app if __name__ == '__main__':
app.run()
complex_bp/run.py
上下文管理(Threadlocal)
import threading class Foo(object):
def __init__(self):
self.name = 0 local_values = Foo() def func(num):
local_values.name = num
import time
time.sleep(1)
print(local_values.name, threading.current_thread().name) for i in range(20):
th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
th.start() 结果:
19 线程0
19 线程2
19 线程1
19 线程3
19 线程7
19 线程6
19 线程5
19 线程4
19 线程11
19 线程10
19 线程9
19 线程8
19 线程14
19 线程13
19 线程12
19 线程16
19 线程15
19 线程19
19 线程18
19 线程17
当多个线程改同一个变量的时候,引发的问题。
import threading local_values = threading.local() # threading.local对象,用于为每个线程开辟一块空间来保存它独有的值。 def func(num):
local_values.name = num
import time
time.sleep(1)
print(local_values.name, threading.current_thread().name) for i in range(20):
th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
th.start() 结果:
19 线程0
19 线程2
19 线程1
19 线程3
19 线程7
19 线程6
19 线程5
19 线程4
19 线程11
19 线程10
19 线程9
19 线程8
19 线程14
19 线程13
19 线程12
19 线程16
19 线程15
19 线程19
19 线程18
19 线程17
threading.local对象,用于为每个线程开辟一块空间来保存它独有的值
通过get_ident自定制一个类似threading.local的功能:
import threading
from _thread import get_ident class Local(object):
def __init__(self):
self.storage = {}
self.get_ident = get_ident def set(self, k, v):
ident = self.get_ident() # 获得线程的唯一标识
origin = self.storage.get(ident)
if not origin:
origin = {k: v}
else:
origin[k] = v
self.storage[ident] = origin def get(self, k):
ident = self.get_ident()
origin = self.storage.get(ident)
if not origin:
return None
return origin.get(k, None) local_values = Local() def task(num):
local_values.set('name', num)
import time
time.sleep(1)
print(local_values.get('name'), threading.current_thread().name) for i in range(20):
th = threading.Thread(target=task, args=(i,), name='线程%s' % i)
th.start() 结果:
0 线程0
1 线程1
3 线程3
2 线程2
5 线程5
4 线程4
6 线程6
9 线程9
10 线程10
7 线程7
8 线程8
13 线程13
14 线程14
11 线程11
15 线程15
12 线程12
19 线程19
16 线程16
18 线程18
17 线程17
get_ident()可获取线程唯一标识
获取协程唯一标识,需要先按照gevent :pip install gevent
import threading try:
from greenlet import getcurrent as get_ident # 协程
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident # 线程 class Local(object):
def __init__(self):
self.storage = {}
self.get_ident = get_ident def set(self, k, v):
ident = self.get_ident() # 获得线程的唯一标识
origin = self.storage.get(ident)
if not origin:
origin = {k: v}
else:
origin[k] = v
self.storage[ident] = origin def get(self, k):
ident = self.get_ident()
origin = self.storage.get(ident)
if not origin:
return None
return origin.get(k, None) local_values = Local() def task(num):
local_values.set('name', num)
import time
time.sleep(1)
print(local_values.get('name'), threading.current_thread().name) for i in range(20):
th = threading.Thread(target=task, args=(i,), name='线程%s' % i)
th.start() 结果:
0 线程0
6 线程6
5 线程5
4 线程4
3 线程3
2 线程2
1 线程1
7 线程7
10 线程10
9 线程9
8 线程8
13 线程13
12 线程12
11 线程11
15 线程15
17 线程17
19 线程19
18 线程18
16 线程16
14 线程14
自定制支持线程+支持协程
import threading try:
from greenlet import getcurrent as get_ident # 协程
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident # 线程 class Local(object): def __init__(self):
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident) def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name) def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value} def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name) local_values = Local() def task(num):
local_values.name = num
import time
time.sleep(1)
print(local_values.name, threading.current_thread().name) for i in range(20):
th = threading.Thread(target=task, args=(i,), name='线程%s' % i)
th.start() 结果:
0 线程0
8 线程8
6 线程6
9 线程9
5 线程5
4 线程4
2 线程2
7 线程7
3 线程3
1 线程1
12 线程12
11 线程11
14 线程14
10 线程10
13 线程13
19 线程19
17 线程17
16 线程16
15 线程15
18 线程18
按照面向对象思想__getattr__,__setattr__方式优化实现
关于__setattr__与__getattr__的补充:
当使用点号获取实例属性时,如果属性不存在就自动调用__getattr__方法__setattr__当设置类实例属性时自动调用,如j.name=5 就会调用__setattr__方法 self.[name]=5因为这个类是从dict继承来的,,是dict的超类所以 self[attr]=value 相当于调用dict的下标方法,与 a={} ; a[attr]=value意思一样。
__setattr__
class Foo(object): def __setattr__(self, *args, **kwargs):
print('__setattr__ in runing') def __getattr__(self, *args, **kwargs):
print('__getattr__ is runing') obj = Foo()
obj.name = 'lcg'
obj.age = '20' 结果==》》
__setattr__ in runing
__setattr__ in runing
__getattr__
class Foo(object): def __setattr__(self, *args, **kwargs):
print('__setattr__ in runing') def __getattr__(self, *args, **kwargs):
print('__getattr__ is runing') obj = Foo()
print(obj.name)
print(obj.age) 结果==》》
__getattr__ is runing
None
__getattr__ is runing
None
这里有一个坑:
class Foo(object):
def __init__(self):
self.name = 'lcg' def __setattr__(self, *args, **kwargs):
self.name = '0bug' # 此处无限递归
print('__setattr__ in runing') def __getattr__(self, *args, **kwargs):
print('__getattr__ is runing') obj = Foo() obj.name = 'lcg' # 引发异常:
# RecursionError: maximum recursion depth exceeded
解决方法:object.__setattr__(self, 'name', 'lcg')代替self.name = 'lcg'
class Foo(object):
def __init__(self):
object.__setattr__(self, 'name', 'lcg') def __setattr__(self, *args, **kwargs):
object.__setattr__(self, 'name', '0bug')
print('__setattr__ in runing') def __getattr__(self, *args, **kwargs):
print('__getattr__ is runing') obj = Foo() obj.name = 'lcg'
print(obj.name) # 结果==>
# __setattr__ in runing
# 0bug
Flask 框架的更多相关文章
- Flask 框架入门
Flask Flask是一个使用 Python 编写的轻量级 Web 应用框架.其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 . 安装 Flask 依赖两个外部库, We ...
- Flask框架获取用户IP地址的方法
本文实例讲述了python使用Flask框架获取用户IP地址的方法.分享给大家供大家参考.具体如下: 下面的代码包含了html页面和python代码,非常详细,如果你正使用Flask,也可以学习一下最 ...
- Python自动化运维之30、Flask框架
Flask 官网:http://flask.pocoo.org/ flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是 ...
- flask框架+pygal+sqlit3搭建图形化业务数据分析平台
一. 前言 先说下主要的框架和主要的图形库的特点:(个人见解) Django:python开发的一个重量级的web框架,集成了MVC和ORM等技术,设计之初是为了使开发复杂的.数据库驱动的网站变得简单 ...
- 2nd_SE-结对编程1-基于flask框架的四则运算生成器
0x00 Coding https://coding.net/u/nikochan/p/2nd_SE/git 0x01 写在前面 因为在上一个作业中,是基于python完成的Command程序.那么再 ...
- Flask框架
FLask框架的简单介绍 Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请 ...
- Python基于Flask框架配置依赖包信息的项目迁移部署小技巧
一般在本机上完成基于Flask框架的代码编写后,如果有接口或者数据操作方面需求需要把代码部署到指定服务器上. 一般情况下,使用Flask框架开发者大多数都是选择Python虚拟环境来运行项目,不同的虚 ...
- Flask框架(1)--基础
Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后 ...
- Flask框架搭建一个日程表
目录 前言 项目介绍 技术栈 Flask Web开发流程 一.搭建环境 1.1: 创建虚拟环境 1.2: 安装依赖包 1.3: 创建依赖包列表文件 1.4: 测试hello word 二.应用程序开发 ...
- Flask框架搭建REST-API服务
一.目的 为了能够将测试工具部署成RESTful-API服务,这样就能通过接口的方式提供统一测试工具服务,使用人员就不用构建application而产生的各种环境问题.使用问题. 适合人群:Pytho ...
随机推荐
- kafka.common.FailedToSendMessageException: Failed to send messages after 3 tries. 最无语的配置
注意: 本文不谈废话,低级问题请自行检查. 我使用Java版本的Kafka Producer生产数据,但是抛出了这个异常.百思不得其解,明明防火墙配置,ZooKeeper,Kafka配置都是没问题的啊 ...
- 七天学会ASP.NET MVC ——深入理解ASP.NET MVC
七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC 系列文章 七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC 七天学会ASP.NET MVC (二) ...
- 来自师兄的Django2.0笔记摘录
翻看一本书,觉得对于Django的日志处理.权限处理包括Django-guardian还没学清楚,然后书上关于mysql深入学习.elasticsearch.redis.celery分布式都没学过. ...
- CRM INBOX 结果增强功能
前段时间接到的需求:INBOX(ICCMP_INBOX)查询结果,多选后弹出选择用户的框,选择用户,带入到单据的PARTNER FUNC的工程师中,并修改单据状态. 其实标准的INBOX的Compon ...
- 移动应用调试之Inspect远程调试
移动应用调试之Inspect远程调试 一.准备工作 chrome浏览器,建议最新版本 如果你点击inspect打开的DevTools窗口一片空白,且刷新无效时,那极有可能是由于被墙的缘故. 二.Ins ...
- C语言:统计数字空格其他字符程序
#include <stdio.h> int main(){ char t; int num=0; int spac=0; int other=0; int sum=0; printf(& ...
- python 实现求一个集合的子集
概要 今天偶然看到有个关于数学中集合的问题,就突发奇想的想用python实现下求一个集合的子集. 准备 我当然先要复习下,什么是集合,什么是子集? 比较粗犷的讲法,集合就是一堆确定的东西,细致一点的讲 ...
- 《JavaScript Dom 编程艺术》读书笔记-第7章
动态创建标记~内容包括: 1. 传统技术:document.write 和innerHTML 2. 深入剖析DOM方法:createElemen.createTextNode.appendChild和 ...
- SpringCloud系列------Eureka-Server
一.概述: Spring Cloud针对服务注册与发现,进行了一层抽象,并提供了三种实现:Eureka , Consul , Zookeeper 本篇文章只对Eureka 进行介绍: (部分内容引用 ...
- 第一章:深入web请求过程
初学者,总结一下读书笔记,望海涵. 1.1 B/S网络架构概述 相比于C/S架构,B/S网络架构(Browser/Server)从前端到后端都得到了简化,都基于统一的应用层协议HTTP来交互数据,采用 ...