Flask 框架
装饰器知识回顾
http://www.cnblogs.com/0bug/p/7978595.html
普通装饰器格式:
1 2 3 4 5 6 7 8 9 10 | def wrapper(func): def inner( * args, * * kwargs): return func( * args, * * kwargs) return inner @wrapper # index = wrapper(index) def index(request): pass |
带参数的装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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" ) |
输出
1 2 3 4 5 | 您好 lcg祝您前程似锦 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 亲爱的园友您好 lcg祝您前程似锦 |
flask路由本质就是带参数的装饰器:
1 2 3 4 5 6 7 8 9 10 11 | @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的本质):
1 2 3 4 5 6 7 8 9 10 11 | 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示例:
1 2 3 4 5 6 7 8 9 10 11 | 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示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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__()方法中的具体细节,后面在做探讨。
1 2 3 | 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 = { 1: {'name': '漩涡鸣人', 'age': 19, 'gender': '男', 'text': '我一直都是说道做到的,因为这就是我的忍道'}, 2: {'name': '藤原佐为', 'age': 28, 'gender': '男', 'text': '我只知道自己执黑子时从来没输过!'}, 3: {'name': '李舜生', 'age': 18, '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 == '123': 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()

<!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>

<!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>

<!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>
配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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 , } |
方式一:
1 2 3 | app.config[ 'DEBUG' ] = True PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...) |
方式二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 }) # 字典格式 |
方式三(最常用的):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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实现的。
1 2 3 4 5 6 7 8 9 | @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并实现相应方法):
1 2 3 4 5 6 7 8 9 | 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()
路由的本质

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
1 2 3 | @app .route( '/index' , methods = [ "GET" , "POST" ], endpoint = 'index' ) def index(): return "Hello Index" |
endpoint是别名,不写别名的话。别名默认就是被装饰的函数名。根据源码分析,也可以写成下面的形式(这样就跟Django的路由很像了):
1 2 3 4 5 | 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()

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()
@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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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}}

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()}}
3.宏定义
1 2 3 4 5 6 7 8 9 10 11 12 | <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> |
请求和响应相关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | 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()
闪现,flash&get_flashed_messages
用途:对临时数据操作,如显示错误信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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中间件)
1 2 3 4 5 6 | @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 ))}} |
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | 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拦截:
1 2 3 4 | @app .before_request def process_request1(): print ( 'process_request1' ) return '被process_request1拦截' |
访问错误,引发异常的时候执行相关的@app.errorhandler(错误码)修饰的函数:
用户认证用请求扩展的方式替代之前的装饰器(类似Django的中间件的用法):
1 2 3 4 5 6 7 | @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()
蓝图(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')

from flask import Blueprint blog_bp = Blueprint('blog_bp', __name__) @blog_bp.route('/blog', methods=['GET', "POST"]) def blog(): return 'blog'

from flask import Blueprint user_bp = Blueprint('user_bp', __name__) @user_bp.route('/user', methods=['GET', "POST"]) def user(): return 'user'

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')

from simple_bp import app if __name__ == '__main__': app.run()
复杂的项目(多个应用),蓝图写法:

from flask import Blueprint admin = Blueprint( 'admin', __name__, template_folder='templates', static_folder='static' ) from . import views

from . import admin @admin.route('/index') def index(): return 'Admin.Index'

from flask import Blueprint web = Blueprint( 'web', __name__, template_folder='templates', static_folder='static' ) from . import views

from . import web @web.route('/index') def index(): return 'Web.Index'

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)

from complex_bp import app if __name__ == '__main__': app.run()
上下文管理(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
通过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
获取协程唯一标识,需要先按照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
关于__setattr__与__getattr__的补充:
当使用点号获取实例属性时,如果属性不存在就自动调用__getattr__方法__setattr__当设置类实例属性时自动调用,如j.name=5 就会调用__setattr__方法 self.[name]=5因为这个类是从dict继承来的,,是dict的超类所以 self[attr]=value 相当于调用dict的下标方法,与 a={} ; a[attr]=value意思一样。
__setattr__
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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__
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 |
这里有一个坑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了