flask存储(python中flask如何降低内存)

seosqwseo2周前 (05-02)测评日记13

一、Flask博客实战 - 实现登录注册功能

上一节我们已经创建了一个用户应用,并创建了用户模型,那么我们这节就开始实现一个简单的用户登录注册功能!

登录注册功能Flask有一个非常优秀的扩展Flask-login,我们可以选择使用这个扩展来实现,但为了学习我们暂时不使用这个第三方扩展,而是选择使用session来实现!

首先,我们需要完善登录的html页面,路径为: app/auth/templates/login.html

代码详解:

这个登陆模板继承了 base.html的样式,这个 base.html中的模块及代码其实就是我们之前实现的首页,只是我们把他作为一个模板基类来继承他!

这段代码中其实就是写了一个输入账号密码的表单,其他多余的代码都是为了实现表单的样式而存在的!

这里要特别说明的是这个input表单必须设置name属性,因为后端要根据此name属性来获取用户输入的值!其他属性则需要大家自行去了解学习!

登录功能的后端逻辑视图,路径为: app/auth/views/auth.py

代码详解:- request.method=='POST'判断当前请求是否为post请求方式- error= None来初始化一个错误变量,如果未通过登录验证,把错误信息通过消息传送到页面提示用户

这段代码首先在数据库通过用户提交的用户名去查询该用户,用户不存在就会返回None返回错误提示,用户存在则判断密码是否正确,这里用到了一个 check_password_hash()的方法,这是用来将密文密码解密后与用户输入密码比对方法,与之对应的有一个 generate_password_hash()的方法用来加密明文密码保存到数据库!

这段代码则是如果没有返回任何错误提示,说明该提交的表单符合我们的要求,并且数据库也存在该用户信息,那么我们只需要清空session,重新将session中的user_id设置为当前登录的id即可!

因此在实现登录注册逻辑之前就必须引入这两个方法:

登录功能虽然实现了,但我们数据库目前还没有任何一个用户,所以此时就应该要去实现用户的注册功能,向数据库新增用户,大概的逻辑是,用户输入用户名及两次密码,先判断该用户是否已经存在,存在则提示更换用户名,不存在则向数据库创建该用户信息,并清空session,重新设置user_id的值为注册用户的id,以达到注册成功后自动登录的目的!

首先,我们需要完善注册的html页面,路径为: app/auth/templates/register.html

这是注册页面的html,大家自行理解下,这里着重说一个我们在视图中通过 flash()传递出来的消息,在模板中由以下代码接收!

注册功能的后端逻辑视图,路径为: app/auth/views/auth.py

这个注册的逻辑基本上涵盖了我们之前所有章节学到的知识点,这里就不再过多地去一一解释代码,大家可自行理解并完善注释!

通过登录和注册功能的实现,我们已经清楚地知道,用户是否登录其实是判断session会话中是否存在用户的id来决定,那么推出登录,我们只需要清除session会话中的用户id即可,这里我们直接选择清空session的方式实现推出功能!

bp.before_app_request()注册一个在视图函数之前运行的函数,无论请求什么 URL。都会先检查用户 ID是否存储在会话中,并从数据库获取该用户的数据,将其存储在 g.user上,该数据在请求期间持续。

注册完这个函数之后,我们就可以在base.html中的导航的右侧通过g.user的返回值,判断用户是否已经登录,显示不同的信息!

对于像下一章节我们要实现的用户中心以及管理后台,则必须是带有权限的访问,基本的权限应该是必须是登录用户,那么所以说对于那些未登录的用户我们需要拒绝访问的功能!

这个其实思路也非常简单,既然在实现模板中调用用户信息的时候,我们把当前登录的用户信息添加到了g对象,那么我们只需要判断g.user的返回值是否为None即可判断用户是否登陆!

到这里关于用户登录注册相关的基本权限问题我们就完成了,注意这些视图函数都在 app/auth/views/auth.py文件中!

二、python flask 怎么组织程序

1.初始化

所有的flask程序都必须创建一个程序实例

web服务器使用wsgi接口协议,把接收客户端的请求都转发给这个程序实例来进行处理。这个程序实例就是flask对象

from flask import Flask

app= Flask(__name__)

#__name__决定程序的根目录,以便以后能找到相对于程序根目录的资源文件位置

2.路由和视图函数

程序实例需要知道接收请求后,需要知道url请求应该运行哪些代码。所以保存了一个url和python函数的映射关系;这个映射关系就叫做路由

flask程序中路由的写法:

2.1#使用app.route装饰器,把修饰的函数注册为路由。例如

@app.route('/')def index(): return"<h1>Hello World</h1>"

#函数的名字不是必须写index的,只是和装饰器关联的时候写的函数名而已

#把index函数注册为程序根路径的处理程序。函数的返回值称为响应,是客户端接收的内容。

像index这样的函数称为试图函数,试图函数返回的响应可以是包含html的简单字符串,也可以是复杂的东西

2.2#可变url部分映射,使用特定的装饰器语法就可以

@app.route('/user/<name>')def user(name): return"<h1>hello%s</h1>"%(name)

装饰器中的<name>指定可变内容为name,name对user(name)函数中的传递参数,这2个部分内容必须一致

调用试图函数时候,flask会自动的将动态部分作为参数传入参数,这个函数中,参数用于生成个人的欢迎信息

#备注:路由中的动态部分默认使用字符串类型,可以使用int,float,path来定义;例如<int:id>;path类型也是字符串,但不把斜线视作分隔符,而将其当做动态片段的一部分

3.启动服务器

调用程序实例app的run方法启动flask集成开发的web服务器

if __name__=="__main__":

app.run(debug=True)

debug=True代表的是调试模式,这个flask自带的run方法开启的服务器不适合在生产中使用,此处只用来测试

4.一个完整的Flask程序

啥也不说,先上例子hello.py

from flask import Flask

app= Flask(__name__)

@app.route('/')def index(): return'<h1>HelloWorld</h1>'@app.route('/user/<name>')def user(name): return"<h1>hello%s</h1>"%nameif __name__=="__main__":

app.run(debug=True)

默认会开启服务器本机5000端口;127.0.0.1:5000

执行脚本python hello.py

浏览器测试

5.请求上下文

Flask使用请求上下文,临时把某些对象变为全局可用;例如

from flask import request

@app.route('/')def index():

user_agent= request.headers.get('User-Agent') return'<h1>your browser is%s</h1>'%(user_agent)

在这个视图函数中,我们把request当做全局变量使用,flask使用请求上下文让特定的变量在一个线程中全局可访问。于此同时却不会干扰其他线程

session:请求上下文;用户会话,用于存储请求之间需要“记住”的值的词典

激活请求上下文的后就可以使用request和session变量了

6.程序上下文

current_app:程序上下文;当前激活程序的程序实例

g:程序上下文;处理请求时用作临时存储的对象

7.请求映射关系表

接收请求,处理请求,,,之间有个映射表,要不然不知道该去执行什么代码。URL映射

from hello import appprint app.url_map

Map([<Rule'/'(HEAD, OPTIONS, GET)-> index>,

<Rule'/static/<filename>'(HEAD, OPTIONS, GET)-> static>,

<Rule'/user/<name>'(HEAD, OPTIONS, GET)-> user>])

8.请求钩子

有的时候在处理请求之前和之后,执行某些特定的代码是很有用的,这就用到了请求钩子

例如在请求之前创建数据库连接或者redis连接;或者是系统里面用户请求处理之前先验证用户的身份,是否激活,激活执行什么*作,没激活用户一直绑到固定页面去直到激活

为了避免每个试图函数中都使用重复的代码,flask提供了注册通用函数的功能;

也就是说只要写一个请求钩子-函数,整个程序实例全局都被应用了。

例如:在所有请求之前先验证下用户的认证状态

@before_app_requestdef before_request(): if current_user.is_authenticated:

current_user.ping() if not current_user.confirmed and request.endpoint[:5]!='auth.' and request.endpoint!='static': return redirect(url_for('auth.unconfirmed'))

常见的4种钩子:

before_first_request:注册一个函数,在处理第一个请求之前运行

before_request:注册一个函数,每次请求之前运行

after_request:注册一个函数,没有未处理的异常抛出,每次请求之后运行

teardown_request:注册一个函数,有未处理的异常抛出,每次请求之后运行

在请求钩子和视图函数之间共享数据一般使用程序上下文g;

例如before_request处理程序可以从数据库中加载已登录用户,将其保存到g.user中,随后调用试图函数,试图函数再从g.user中获取用户

9.基于Flask的http响应

flask调用试图函数处理请求,并把返回值作为响应的内容.大多情况下是一个简单的字符串或者json字符串;返回字符串常用于给对方提供接口的时候使用

http响应中很重要的一个内容是状态码,flask默认设置为200,这个代码表明请求已经被成功处理了

如果试图函数返回的响应需要不同的状态码,可以把状态码加到后面返回

例如

@app.route('/')def index(): return'<h1>Bad Request</h1>',400

试图函数返回的响应还可以接受第三个参数,第三个参数是一个字典类型的首部,可以添加到http响应中去,一般不用添加

如果不想返回这种好多个元素的元祖,可以使用Response对象来标准化下返回。

例如:创建一个响应对象,然后设置cookie

from flask import make_response

@app.route('/')def index():

response= make_response('<h1>This document carries a cookie!</h1>')

response.set_cookie('answer',42) return response

还有一种特殊的响应类型,flask提供了一种基于302的跳转响应,这个响应由redirect函数来提供。指向的地址由Location首部提供,重定向的响应可以使用3个值形式的返回值生成。也可以再Response对象中设定

例如:

from flask import redirect

@app.route('/')def index(): return redirect('htmple')

还有一种特殊的响应类型,flask提供了一种错误响应。这个由abort函数来提供。abort抛出404异常,抛出异常后把控制权移交给web服务器

例如:

from flask import abort

@app.route('/user/<id>')def get_user(id):

user= load_user(id) if not user:

abort(404) return'<h1>Hello,%s</h1>'%(user.name)

10.flask的扩展flask-script

这个例子主要是讲如何把flask扩展添加到程序中,并使用

例如下面你的例子是添加flask-script扩展,使用命令行参数增强程序的功能

使用命令行方式启动web服务器,而不是修改文件,给run方法传递参数

安装扩展

pip install flask-script

使用flask-script扩展,并把hello.py文件改为命令行参数启动的形式#添加的扩展默认会安装到flask.ext命名空间中

from flask import Flaskfrom flask.ext.script import Manager

app= Flask(__name__)

manager= Manager(app)

@app.route('/')def index(): return'<h1>HelloWorld</h1>'@app.route('/user/<name>')def user(name): return"<h1>hello%s</h1>"%nameif __name__=="__main__":

manager.run()

flask-script扩展中添加了一个Manager的类,以上例子中,这个扩展初始化的方法是,把程序实例作为参数传递给构造函数来初始化主类的实例。后续其他flask扩展也基本是这个套路

这样修改之后,程序就可以使用一组基本的命令行选项来启动和调试了

python hello.py shell#在flask应用上下文环境中运行python shell,方便测试和调试web环境

python hello.py runserver#运行flask开发服务器,app.run()

python hello.py-h#显示帮助信息

python hello.py runserver--help

usage: hello.py runserver [-h] [-t HOST] [-p PORT] [--threaded]

[--processes PROCESSES] [--passthrough-errors] [-d]

[-r]

python hello.py runserver-h 0.0.0.0-p 80#这样就开启了本机的80端口,别的机器可以远程访问了

三、python中flask如何降低内存

Dict

在小型程序中,特别是在脚本中,使用Python自带的dict来表示结构信息非常简单方便:

>>> ob={'x':1,'y':2,'z':3}

>>> x= ob['x']

>>> ob['y']= y

由于在Python 3.6中dict的实现采用了一组有序键,因此其结构更为紧凑,更深得人心。但是,让我们看看dict在内容中占用的空间大小:

>>> print(sys.getsizeof(ob))

240

如上所示,dict占用了大量内存,尤其是如果突然虚需要创建大量实例时:

实例数

对象大小

1 000 000

240 Mb

10 000 000

2.40 Gb

100 000 000

24 Gb

类实例

有些人希望将所有东西都封装到类中,他们更喜欢将结构定义为可以通过属性名访问的类:

class Point:

#

def __init__(self, x, y, z):

self.x= x

self.y= y

self.z= z

>>> ob= Point(1,2,3)

>>> x= ob.x

>>> ob.y= y

类实例的结构很有趣:

字段

大小(比特)

PyGC_Head

24

PyObject_HEAD

16

__weakref__

8

__dict__

8

合计:

56

在上表中,__weakref__是该列表的引用,称之为到该对象的弱引用(weak reference);字段__dict__是该类的实例字典的引用,其中包含实例属性的值(注意在64-**t引用平台中占用8字节)。从Python3.3开始,所有类实例的字典的键都存储在共享空间中。这样就减少了内存中实例的大小:

>>> print(sys.getsizeof(ob), sys.getsizeof(ob.__dict__))

56 112

因此,大量类实例在内存中占用的空间少于常规字典(dict):

实例数

大小

1 000 000

168 Mb

10 000 000

1.68 Gb

100 000 000

16.8 Gb

不难看出,由于实例的字典很大,所以实例依然占用了大量内存。

带有__slots__的类实例

为了大幅降低内存中类实例的大小,我们可以考虑干掉__dict__和__weakref__。为此,我们可以借助 __slots__:

class Point:

__slots__='x','y','z'

def __init__(self, x, y, z):

self.x= x

self.y= y

self.z= z

>>> ob= Point(1,2,3)

>>> print(sys.getsizeof(ob))

64

如此一来,内存中的对象就明显变小了:

字段

大小(比特)

PyGC_Head

24

PyObject_HEAD

16

x

8

y

8

z

8

总计:

64

在类的定义中使用了__slots__以后,大量实例占据的内存就明显减少了:

实例数

大小

1 000 000

64 Mb

10 000 000

640 Mb

100 000 000

6.4 Gb

目前,这是降低类实例占用内存的主要方式。

这种方式减少内存的原理为:在内存中,对象的标题后面存储的是对象的引用(即属性值),访问这些属性值可以使用类字典中的特殊描述符:

>>> pprint(Point.__dict__)

mappingproxy(

....................................

'x':,

'y':,

'z':})

为了自动化使用__slots__创建类的过程,你可以使用库namedlist()。namedlist.namedlist函数可以创建带有__slots__的类:

>>> Point= namedlist('Point',('x','y','z'))

还有一个包attrs(),无论使用或不使用__slots__都可以利用这个包自动创建类。

元组

Python还有一个自带的元组(tuple)类型,代表不可修改的数据结构。元组是固定的结构或记录,但它不包含字段名称。你可以利用字段索引访问元组的字段。在创建元组实例时,元组的字段会一次性关联到值对象:

>>> ob=(1,2,3)

>>> x= ob[0]

>>> ob[1]= y# ERROR

元组实例非常紧凑:

>>> print(sys.getsizeof(ob))

72

由于内存中的元组还包含字段数,因此需要占据内存的8个字节,多于带有__slots__的类:

字段

大小(字节)

PyGC_Head

24

PyObject_HEAD

16

ob_size

8

[0]

8

[1]

8

[2]

8

总计:

72

命名元组

由于元组的使用非常广泛,所以终有一天你需要通过名称访问元组。为了满足这种需求,你可以使用模块collections.namedtuple。

namedtuple函数可以自动生成这种类:

>>> Point= namedtuple('Point',('x','y','z'))

如上代码创建了元组的子类,其中还定义了通过名称访问字段的描述符。对于上述示例,访问方式如下:

class Point(tuple):

#

@property

def _get_x(self):

return self[0]

@property

def _get_y(self):

return self[1]

@property

def _get_z(self):

return self[2]

#

def __new__(cls, x, y, z):

return tuple.__new__(cls,(x, y, z))

这种类所有的实例所占用的内存与元组完全相同。但大量的实例占用的内存也会稍稍多一些:

实例数

大小

1 000 000

72 Mb

10 000 000

720 Mb

100 000 000

7.2 Gb

记录类:不带循环GC的可变更命名元组

由于元组及其相应的命名元组类能够生成不可修改的对象,因此类似于ob.x的对象值不能再被赋予其他值,所以有时还需要可修改的命名元组。由于Python没有相当于元组且支持赋值的内置类型,因此人们想了许多办法。在这里我们讨论一下记录类(recordclass,),它在StackoverFlow上广受好评()。

此外,它还可以将对象占用的内存量减少到与元组对象差不多的水平。

recordclass包引入了类型recordclass.mutabletuple,它几乎等价于元组,但它支持赋值。它会创建几乎与namedtuple完全一致的子类,但支持给属性赋新值(而不需要创建新的实例)。recordclass函数与namedtuple函数类似,可以自动创建这些类:

>>>Point= recordclass('Point',('x','y','z'))

>>>ob= Point(1, 2, 3)

类实例的结构也类似于tuple,但没有PyGC_Head:

字段

大小(字节)

PyObject_HEAD

16

ob_size

8

x

8

y

8

z

8

总计:

48

在默认情况下,recordclass函数会创建一个类,该类不参与垃圾回收机制。一般来说,namedtuple和recordclass都可以生成表示记录或简单数据结构(即非递归结构)的类。在Python中正确使用这二者不会造成循环引用。因此,recordclass生成的类实例默认情况下不包含PyGC_Head片段(这个片段是支持循环垃圾回收机制的必需字段,或者更准确地说,在创建类的PyTypeObject结构中,flags字段默认情况下不会设置Py_TPFLAGS_H**E_GC标志)。

大量实例占用的内存量要小于带有__slots__的类实例:

实例数

大小

1 000 000

48 Mb10 000 000

480 Mb

100 000 000

4.8 Gb

dataobject

recordclass库提出的另一个解决方案的基本想法为:内存结构采用与带__slots__的类实例同样的结构,但不参与循环垃圾回收机制。这种类可以通过recordclass.make_dataclass函数生成:

>>> Point= make_dataclass('Point',('x','y','z'))

这种方式创建的类默认会生成可修改的实例。

另一种方法是从recordclass.dataobject继承:

class Point(dataobject):

x:int

y:int

z:int

这种方法创建的类实例不会参与循环垃圾回收机制。内存中实例的结构与带有__slots__的类相同,但没有PyGC_Head:

字段

大小(字节)

PyObject_HEAD

16

ob_size

8

x

8

y

8

z

8

总计:

48

>>> ob= Point(1,2,3)

>>> print(sys.getsizeof(ob))

40

如果想访问字段,则需要使用特殊的描述符来表示从对象开头算起的偏移量,其位置位于类字典内:

mappingproxy({'__new__':,

.......................................

'x':,

'y':,

'z':})

大量实例占用的内存量在CPython实现中是小的:

实例数

大小

1 000 000

40 Mb

10 000 000

400 Mb

100 000 000

4.0 Gb

Cython

还有一个基于Cython()的方案。该方案的优点是字段可以使用C语言的原子类型。访问字段的描述符可以通过纯Python创建。例如:

cdef class Python:

cdef public int x, y, z

def __init__(self, x, y, z):

self.x= x

self.y= y

self.z= z

本例中实例占用的内存更小:

>>> ob= Point(1,2,3)

>>> print(sys.getsizeof(ob))

32

内存结构如下:

字段

大小(字节)

相关文章

JAV会议平板电视机多媒体教学一体机55英寸培训教育视频会议室大屏幕电子白板书写投屏触屏办公电视使用感受如何

JAV会议平板电视机多媒体教学一体机55英寸培训教育视频会议室大屏幕电子白板书写投屏触屏办公电视使用感受如何

很多小伙伴在关注JAV会议平板电视机多媒体教学一体机55英寸培训教育视频会议室大屏幕电子白板书写投屏触屏办公电视怎么样?质量好不好?使用测评如何?本文综合已购用户的客观使用...

海信电视65E5G测评使用介绍

海信电视65E5G测评使用介绍

很多小伙伴在关注海信电视65E5G怎么样?质量好不好?使用测评如何?本文综合已购用户的客观使用分享和相应的优惠信息,为大家推荐一款高性价比的产品,一起来看看吧。...

SHARP夏普(SHARP)42英寸使用反馈分享

SHARP夏普(SHARP)42英寸使用反馈分享

很多小伙伴在关注SHARP夏普(SHARP)42英寸怎么样?质量好不好?使用测评如何?本文综合已购用户的客观使用分享和相应的优惠信息,为大家推荐一款高性价比的产品,一起来看...

海信电视65E5G65英寸4K超清图文测评

海信电视65E5G65英寸4K超清图文测评

很多小伙伴在关注海信电视65E5G65英寸4K超清怎么样?质量好不好?使用测评如何?本文综合已购用户的客观使用分享和相应的优惠信息,为大家推荐一款高性价比的产品,一起来看看...

康佳电视75N66怎么样?质量测评好不好用?

康佳电视75N66怎么样?质量测评好不好用?

很多小伙伴在关注康佳电视75N66怎么样?质量好不好?使用测评如何?本文综合已购用户的客观使用分享和相应的优惠信息,为大家推荐一款高性价比的产品,一起来看看吧。...

FFALCON雷鸟鹏6好用吗

FFALCON雷鸟鹏6好用吗

很多小伙伴在关注FFALCON雷鸟鹏6怎么样?质量好不好?使用测评如何?本文综合已购用户的客观使用分享和相应的优惠信息,为大家推荐一款高性价比的产品,一起来看看吧。...