面试题回答

python语法和使用相关

什么是装饰器,怎么使用?

答:装饰器是一个用来增强或修改函数功能的设计模式。他的本质其实还是函数,只不过python为其添加了语法糖,可以通过@decorator来调用装饰器。一般可以用来进行函数执行前或后进行某些操作的执行,比如日志记录,权限认证等。
使用则是声明一个函数,参数是函数,返回值也是一个函数,如下:

def my_decoractor(my_func):
	def wrapper():
		# 记录日志
		my_func()
		# 其他操作
		return
	return wrapper


@my_decoractor()
def my_func():
	pass

# 本质跟该用法一样
myfunc = my_decoractor(my_func)
myfunc()

# 当装饰器需要传参,但装饰器函数只接受函数参数时,需要再套一层函数来包装,如下:
def outer(i):
	def my_decoractor(my_func):
		def wrapper(*args, **kwargs):
			if i == 0:
				print("参数异常")
			else:
				my_func()
		return wrapper
	return my_decorator

@outer(1)
def my_func():
	pass

什么是yield

答:yield是python中一种生成器的关键词,它是用于将一个函数变为生成器使用,当调用使用yield的函数时,它不会立刻返回值,而是要等下一次执行时才会返回。可以通过next方法获取生成器的值,for循环中也会自动调用该接口。所以它一般是用于读取大数据。

def func():
	yield 1
	yield 2

a = func()  # 此时执行还不会返回1
print(next(a))  # 1
print(next(a))  # 2
print(next(a))  # StopIteration报错

在这里插入图片描述

请你介绍下python的垃圾回收机制

答:垃圾回收机制是python用于内存管理的一个机制,它使使用者无需关心内存。
回答的重点应该是其使用了什么方法来管理不同生存周期的对象

请讲解下什么是python的asynio,有用过吗

asyncio是python3实现的单线程异步变成,用于异步处理I/O操作,提高系统响应。
await:挂起与等待,后面跟着的必须是可等待对象。核心作用是挂起当前协程,将控制权交还给事件循环。
异步方法前要先用async标记其为异步方法
asyncio.run(异步方法):运行异步方法,但一般使用中,用来运行最高层级的入口点 “main()” 函数

基本使用

import asyncio

# 异步方法
async def getServerInfo():
	# 模拟查询过程
	await asyncio.sleep(0.1)
	return {"name":"legend", "id":1}

async def main()
	serverInfo = await getServerInfo()
	print(serverInfo)

asyncio.run(main())  # 一般用来执行最高层级的main
asyncio.run(getServerInfo()) # 但是也可以单独调用异步方法

	

使用注意事项,只要使用了await的函数,都要async修饰。否则报错

import asyncio

async def nested():
    return 42

def main():
	a = await asyncio.run(nested())
	print(a)

main()

asyncio.create_task(异步方法):打包为一个 任务,该协程将自动排入日程准备立即运行
多个异步接口需要执行,使用create_task打包成一个任务

import asyncio

async def nested():
	print("3284236423")
	return 42

async def nested2():
	print("478624234")
	return [1,2,3,4]

async def main():
	task1 = asyncio.create_task(
		nested2()
	)

	task2 = asyncio.create_task(
		nested()
	)

	
asyncio.run(main())

结果:
在这里插入图片描述

  • 如果只是执行create_task,没有用await来接住他返回的值,那么就在执行完函数后就销毁了。
  • 而且他们的执行顺序不会因为await获取的时机不一样,而改变。主要是取决于各自谁先执行完
import asyncio

async def nested():
	print("3284236423")
	return 42

async def nested2():
	print("478624234")
	return [1,2,3,4]

async def main():
	task1 = asyncio.create_task(
		nested2()
	)

	task2 = asyncio.create_task(
		nested()
	)

	t1 = await task2
	print(t1)
	t2 = await task1
	print(t2)
	
asyncio.run(main())

# 执行耗时不同时
import asyncio

async def nested():
	await asyncio.sleep(0.2)
	print("3284236423")
	return 42

async def nested2():
	await asyncio.sleep(0.4)
	print("478624234")
	return [1,2,3,4]

async def main():
	task1 = asyncio.create_task(
		nested2()
	)

	task2 = asyncio.create_task(
		nested()
	)

	t1 = await task2
	print(t1)
	t2 = await task1
	print(t2)
	
asyncio.run(main())

结果
模拟耗时相同
在这里插入图片描述
耗时不同
在这里插入图片描述
asyncio.gather
批量执行task

事件循环,他是如何管理所有协程

事件循环是单线程调度器,管理所有协程的执行、切换和回调。
每个协程在事件循环中有三种状态:

# 协程状态
PENDING   = "pending"    # 等待执行
RUNNING   = "running"    # 正在执行
FINISHED  = "finished"   # 执行完成

# 事件循环维护一个状态表
{
    "task1": {"state": "RUNNING", "result": None},
    "task2": {"state": "PENDING", "result": None},
    "task3": {"state": "FINISHED", "result": "data"}
}

采用3个队列管理

# 事件循环内部有三个核心队列
class EventLoop:
    def __init__(self):
        self._ready = deque()        # 可执行队列
        self._scheduled = []         # 定时队列
        self._callbacks = []         # 回调队列
    
    def run_forever(self):
        while True:
            # 1. 执行所有可执行任务
            self._run_ready()
            
            # 2. 处理定时任务
            self._process_scheduled()
            
            # 3. 处理I/O事件
            self._run_once()
            
            # 4. 如果没有任务,退出
            if not self._ready and not self._scheduled:
                break
Task和Future的区别

JWT是什么,原理是怎样

JWT是一种用于各方之间传递安全信息的令牌格式。通常会在请求头中发送该jwt,服务器则验证其有效性及用户身份验证。

但是其是无状态,服务器端不保存该令牌。所以常用于分布式系统和微服务架构中。

他主要由三部分构成:

  • Header是描述令牌的元数据,通常包含使用的加密算法,令牌类型
  • Payload是声明。其中分为私有声明和公有声明。一般jwt的常规设置,比如过期时间、发行者、何时生效等,都是在公有声明中填写,而一些额外数据,则是在私有声明中填写。一般不要把敏感数据放在此处,比如用户密码。
  • signature签名,由Header和Payload用指定的加密算法生成的字符串,用以验证JWT的真实性和完整性。确保传输过程中令牌内容没有被篡改。

他的优点是包含了所有必要信息,因此验证时不需要查询数据库;以及它是基于JSON实现的,常见编程语言都支持。

面试你可能问
除了JWT的token外,还有其他登录鉴权的方式吗?
  1. session-cookie方式,该方法会在服务端创建一个session会话并将会话id通过Cookie发送给客户端,客户端保存该cookie,后续请求中携带该Cookie即可维持会话状态。但是其弊端是无法在别的服务器中使用,因为该会话只存储在第一次访问的服务器中,所以不适合分布式应用。且如果会话数量较大,也会增加服务器内存的消耗,甚至导致服务器宕机。

  2. OAuth2.0认证,它是一种开放授权协议,允许用户在不暴露用户名和密码的情况下,授权第三方访问其受保护的资源,比如支付宝api、微信支付等。优点是用户体验号,避免了多账户管理,安全性高。但缺点是依赖第三方服务,适用性较窄。

从网络角度,用户从输入网址到网页显示,期间发生了什么

首先通过浏览器会解析URL,根据请求信息生成对应的HTTP请求报文。

然后浏览器会检查本地缓存、操作系统缓存中是否有域名对应的ip地址。如果没有,就向配置的DNS服务器发送查询请求,等DNS返回IP地址。

然后使用TCP或UDP封装请求报文为TCP/UDP数据包。然后再封装源ip地址和目标ip地址的IP头部。

接着在IP头部前再封装一层MAC头部。

数据封装好后,通过网卡,将内存中的数据包转换成电信号,通过网线进行传输。

传输过程中经过交换机、路由器,最终到达目标设备。

目标设备接受到数据包后,按照之前数据封装顺序的倒序解包即可获得数据。

可以说下session、cookie、token的区别吗

session是有状态的会话机制,它保存在服务器中。他通过Cookie将session id发给浏览器,浏览器保存该Cookie并在后续请求中携带该cookie。

cookie是一种存储在用户浏览器端的小型数据文件,用于跟踪和保存用户状态信息。

Token是一种无状态的令牌,本质是一串加密字符串。用于身份验证和授权,服务器生成token后发送给客户端保存,后续客户端携带该token即可访问服务器。他和session的区别是,只要是拥有秘钥的服务器,都可以识别token,而session只有存储了session id的服务器才能识别

操作系统

什么是进程和线程

答:进程是资源配置的基本单元,进程之间互相独立,各自拥有自己的内存空间。

线程是CPU调度的基本单元,一个进程里可以开启多个线程。线程间共享进程的内存空间和资源,但是每个线程有自己独立的栈和寄存器。

可能问
他们的区别是什么

答:通信方式不同,进程之间的通信需要通过管道、消息队列、共享内存、套接字等。而同一个进程中的线程因为共享内存空间,因此可以直接读取内存,但是要注意使用同步机制避免数据错误。

请问你还听过协程吗?它是操作系统中存在吗?(或者问讲讲他是什么以及和进程线程有什么区别)

协程是程序员创造出来的,是一种用户态内的上下文切换技术。它能够在单线程内并发执行多个任务。可以成为轻量级线程。它是通过程序控制,而不是通过操作系统调度。通常在特定的点,比如I/O操作进行切换,这使得协程具有非抢占式的特性。它适用于高并发网络应用,比如聊天服务器、游戏等。

python中实现协程的方法有yield,asyncio装饰器(py3.4),async/await关键子(py3.5,主流推荐这个)

项目中因为使用的2.7版本,所以使用yield实现协程

你可以详细讲下async怎么使用吗?

asyncio是Python用于编写并发代码的库,提供了异步I/O、事件循环、协程和任务等功能。
我讲下基础的使用

import asyncio 

# 定义协程程序
async def test():
	await asyncio.sleep(2)
	return 1

async def main():
	response = await test()
	print(response)

# py3.7之前
# 获取事件循环队列
result = main()  # 此时程序还不会执行
loop = asyncio.get_event_loop()
# 加入队列
loop.run_until_complete(result)


#py3.7及之后版本
result = main()
asyncio.run(result)

服务器中断是什么,分别是什么?

指CPU在正常运行程序时,由于内部/外部事件(或由程序)引起CPU中断正在运行的程序,而转到
为中断事件服务的程序中去,服务完毕,再返回执行原程序的这一过程。
可以分为硬中断和软中断:

  • 硬中断:由于外部硬件设备,比如例如磁盘、网卡、键盘等设备在检测到某种事件时会向CPU发送中断信号。
  • 软中断:由操作系统内核程序触发的,用于处理一些不能被中断的工作或延迟执行的任务。

虚拟地址的意义

  • 虚拟内存技术扩大了各自进程的地址空间
  • 每个进程运行在各自的虚拟地址空间,互相不干扰对方。虚拟内存为特定的内存地址提供写保护。可以防止代码或数据被恶意篡改
  • 内存中可以保留多个进程,当一个进程等待在等待数据读入内存的过程中,可以将cpu资源交给另外一个进程使用,提高并发性

Django

ORM中Meta常用的字段作用

答:

class Meta:
    # ---常用
    db_table = 'table_name'             # 自定义表名
    index_together = ('tag1', 'tag2')   # 联合索引
    unique_together = ('tag3', 'tag4')  # 联合唯一索引
    verbose_name = 'table_name'         # /admin/中显示的表名称
    verbose_name_plural = verbose_name  #这个选项是指定,模型的复数形式是什么
    ordering = 'ordering_tag'           # 排序字段
    abstract =True                      # 抽象基类

如果想要了解其他非常用字段,那么可以前往官方文档说明

DateTimeField字段的auto_now_add和auto_now的区别

答:auto_now_add: 当数据被创建时,以当前时间作为默认值写入当前字段
auto_now:当数据被更新时,以当前时间作为值写入当前字段

简述下MVC和MTV模式

答:MVC表示model,view,control三层,分别是 模型、视图和控制器。模型负责业务对象与数据库的映射,视图负责与用户的交互,控制器负责接收用户的输入,调用模型和视图去完成用户的请求。该模式的好处是解决了业务逻辑、数据和界面显示的耦合问题,使得开发和维护更清晰和简单。

MTV模式表示model,template,view,分别是模型,模板,视图,模型也是负责业务对象与数据库的映射,模板使用用于页面渲染,视图是负责处理用户输入的数据,调用模型,并返回数据给模板渲染。该模式的好处也是为了对各组件进行解耦。

Django的生命周期(一个请求在Django中经历了什么流程)

答:

  1. 一般是用户通过浏览器向服务器发起一个请求,然后通过web服务器封装请求,例如uWsgi
  2. 接着经过中间件,对请求进行校验或者添加数据
  3. 通过URL控制器匹配相应的视图函数
  4. 视图函数进行业务逻辑处理,根据业务需求返回数据或者渲染页面
  5. 相应中间件对相应的数据进行处理
  6. wsgi服务器将相应的内容发给浏览器
可能会问:
什么是中间件

答:是用来处理Django请求和响应的钩子。用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。

Django中间件常用的方法有哪几个,分别是什么时候调用

中间件常用方法有process_request、process_response、process_exception、process_view、process_template_response。

分别是请求到达视图之前调用、响应数据后调用、视图有异常时调用、路由匹配完成后调用、调用TemplateResponse或者有render属性时调用

详解:

process_request

  • 执行顺序:
    • 按照配置文件中的中间件从上往下执行
    • 每一个请求来的时候都需要经过process_request方法
    • 该方法有一个request参数
  • 如果某一个中间件使用Response或者HttpResponse之类的函数执行返回逻辑,那么就中断当前请求的执行。比如进行权限验证拦截。
  • 如果中间件中没有定义该方法,则跳过检验下一个中间件
  • 该方法在每个请求到达视图之前被调用,可以对请求进行预处理。
class AuthenticationMiddleware:
    def process_request(self, request):
        # 在这里进行身份验证操作
        if not request.user.is_authenticated:
            # 如果用户未经身份验证,则返回HttpResponse或重定向到登录页面

process_response

  • 执行顺序
    • 按照注册的中间件从最后往上依次执行
  • 该方法必须返回HttpResponse对象
    • 默认是response
    • 支持自定义,比如登录请求响应时携带token
  • 如果中间件中没有定义该方法,则跳过检验下一个中间件
  • 它接收一个HttpRequest对象和HttpResponse对象作为参数,并且必须返回一个HttpResponse对象
class CustomResponseMiddleware:
    def process_response(self, request, response):
        # 在这里对响应进行处理
        response['X-Custom-Header'] = 'Custom Value'
        return response

process_view

  • 路由匹配成功后执行视图函数之前
  • 顺序是按照配置文件中注册的中间件从上而下的顺序执行
  • 该方法在请求到达视图之前被调用,在视图函数执行前执行。
    • 可以在此处进行一些操作
    • 如修改请求参数或进行记录等。
  • 它接收一个HttpRequest对象和一个视图函数作为参数,并且可以返回一个HttpResponse对象或None。如果返回None,则继续执行后续中间件,否则中断请求
class LoggingMiddleware:
    def process_view(self, request, view_func, view_args, view_kwargs):
        # 在这里记录日志
        logger.info(f"Request received: {request.path}")
        # 返回None,继续执行原视图函数
        return None

process_exception

  • 当视图函数中出现异常的情况下触发
  • 顺序是按照配置文件中注册了的中间件从下往上依次经过
  • 该方法在视图函数抛出异常时被调用。
    • 可以在此处捕获异常并进行处理
    • 例如返回一个定制的错误页面或进行日志记录等。
  • 它接收一个HttpRequest对象和一个异常对象作为参数,可以返回一个HttpResponse对象来替代原始的异常响应。
class ErrorHandlerMiddleware:
    def process_exception(self, request, exception):
        # 在这里处理异常
        if isinstance(exception, CustomException):
            # 如果自定义异常,返回一个定制的错误页面
            return render(request, 'error.html', {'error': str(exception)})
        else:
            # 默认情况,返回一个500服务器错误
            return HttpResponseServerError("Internal Server Error")

process_template_response

  • 返回的 HttpResponse 对象有 render 属性的时候才会触发
  • 顺序是按照配置文件中注册了的中间件从下往上依次经过
  • 该方法在视图函数返回一个TemplateResponse对象时调用。
    • 可以在此处修改模板响应
    • 例如添加全局的上下文数据或进行额外的渲染操作。
  • 它接收一个HttpRequest对象和一个TemplateResponse对象作为参数,并且必须返回一个TemplateResponse对象。
class GlobalContextMiddleware:
    def process_template_response(self, request, response):
        # 在这里添加全局的上下文数据
        response.context_data['global_data'] = "Global Value"
        return response

简述什么是FBV和CBV

答:FBV是指视图通过函数实现,CBV是指视图通过类实现,类实现本质上也是函数实现。

如果请求简单,可以通过FBV实现,如果请求复杂,比如需要处理GET、POST多种请求格式,那么使用CBV更方便。

from django.shortcuts import render,HttpResponse

# FBV

def user(request):
    #处理GET请求
    if request.method == 'GET':
        return HttpResponse('GET')
    
    # 处理POST请求
    if request.method == 'POST':
        return HttpResponse('POST')
    
# CBV

class UserView(View):

    def get(self,request,*args, **kwargs):
        #获取数据
        return HttpResponse('GET')

    def post(self,request,*args, **kwargs):
        #创建数据
        return HttpResponse('POST')

    def put(self,request,*args, **kwargs):
        #更新全部数据
        return HttpResponse('put')

    def delete(self,request,*args, **kwargs):
        #删除数据
        return HttpResponse('delete')

    def patch(self,request,*args, **kwargs):
        #更新局部数据,例如:用户信息的name这样的某几列
        return HttpResponse('patch')
可能会问:
你能讲讲为什么说CBV本质也是函数呢?

类实现在url匹配器中的调用是通过as_view()接口,通过阅读源码,as_view实际执行了dispatch,会对request对象进行封装,然后在其内部定制一些功能,比如权限认证、根据请求类型调用不同方法执行等。最后返回dispatch。

请解释一下 QuerySet 的特性,以及如何优化 QuerySet 的性能?

特性:

  • 惰性加载: QuerySet 的执行是惰性的,只有在真正需要数据时才会去查询数据库。
  • 链式调用: 可以对 QuerySet 进行链式调用,组合多个过滤条件。
  • 缓存机制: 第一次遍历 QuerySet 时,Django 会缓存查询结果,后续遍历会直接使用。
  • 缓存。

优化方法:

  • 使用 values() 或 values_list() 指定需要的字段,避免查询不必要的字段。
  • 使用 filter()、exclude() 等方法尽早过滤数据。
    使用 iterator() 处理大量数据,避免一次性加载所有数据到内存。
  • 避免在循环中执行数据库查询。
  • 使用 count()、exists() 等方法代替 len(queryset) 和 if queryset。

跨域请求如何处理

答:

浏览器有一个同源策略,即不允许不同源(同协议、同域名、同端口)的url相互访问,目的是为了保证用户信息的安全,防止恶意网站窃取数据。

但这对实际应用场景来说带来了一些不便,比如访问同一个网站的不同端口的数据时,会被拦截。

Django则提供了组件来完成该任务,django-cors-headers,他会在允许访问的网址http头中添加允许访问的字段,如下:
在这里插入图片描述

可能会问
Django如何防止csrf攻击

答:csrf攻击是指攻击者通过伪造来源网站发起请求,达到非法目的。

Django内置了CSRF保护机制,通过在视图函数中添加@csrf_protect装饰器或在表单中添加{% csrf_token %}来确保请求是合法的用户操作。它的实现机制如下:

  1. django第一次响应来自某个客户端的请求时,后端随机产生一个token值,把这个token保存在SESSION状态中;同时,后端把这个token放到cookie中交给前端页面;
  2. 下次前端需要发起请求(比如发帖)的时候把这个token值加入到请求数据或者头信息中,一起传给后端;Cookies:{csrftoken:xxxxx}
  3. 后端校验前端请求带过来的token和SESSION里的token是否一致;
请问你了解分布式系统下,跨域问题可以怎么解决吗?

我开发的项目基本上都是公司内部系统,基本上没有分布式场景的应用,但是我也研究过该情况下怎么处理。可以通过以下方式解决:

  • 配置nginx将所有机器的流量代理到统一域名,然后在nginx中进行分发
worker_processes  1;
events {
    worker_connections  1024;
}

http {
    include             mime.types;
    default_type        application/octet-stream;
    sendfile            on;
    keepalive_timeout   65;

    #gzip  on;

    # 配置第一个代理规则
    # 域名: example1.cn
    # 监听端口: 8081
    server {
        listen              8081;
        server_name         example1.cn;
        location / {
            proxy_pass      http://127.0.0.1:8080;
        }
    }

    # 配置第二个代理规则
    # 域名: example2.cn
    # 监听端口: 8081, 端口与第一个规则相同
    server {
        listen              8081;
        server_name         example2.cn;
        location / {
            proxy_pass      http://127.0.0.1:9000;
        }
    }
}
  • 使用redis缓存token,统一域名下的访问都到redis中获取token验证是否一致。

请问什么是celery,怎么接入Django使用?

答:celery是一个分布式异步任务队列框架,用于将耗时的任务从主程序分离,实现异步执行、定时任务调度和分布式计算。一般用它实现

  • 定时任务
  • 异步任务
    它是由Broker任务队列、Worker工作队列、Backend结果队列组成。客户端调用celery相关接口后,会将任务加入到Broker中,然后Worker会定时读取Broker中的任务,然后执行任务,最后将结果放到BackEnd中存储,客户端去BackEnd获取结果。
    在这里插入图片描述
    Django接入使用就需要先pip安装celery插件,然后在Django项目的主应用目录下创建celery入口程序,在里面配置celery的设置参数(包括broker、backend使用的组件、最多执行任务数量、任务结果输出格式)和发现项目中的任务,想要celery主动检测出项目目录中的celery任务,,需要使用celery的子应用下都要创建task.py文件。
    最后在主应用下初始化celery即可。后续编写异步任务,执行要使用share_task装饰函数,然后再调用异步函数的地方使用delay执行。

celery进阶使用(待补充)

supervisor:后台执行
flower:监控执行过程

Django项目如何生成第三方库列表供他人部署项目时安装

1.在当前项目文件根目录下生成requirements.txt文件
pip freeze > requirements.txt
2.执行requirements.txt文件
pip install -r requirements.txt

如何优化Django的数据库查询性能

  • 使用select_related/prefetch_related减少查询次数。

  • 避免在循环中查询(N+1问题)。

  • 利用only()/defer()限制字段加载。

  • 对高频查询字段添加数据库索引(db_index=True)。

  • 使用annotate()和aggregate()聚合数据替代Python计算。

Django的缓存机制

  • 内存缓存:如Memcached。

  • 文件缓存:缓存数据到文件系统。

  • 数据库缓存:使用数据库表存储缓存。

  • Redis缓存(推荐):通过django-redis库集成。

Flask

什么是Flask

答:flask是一个轻量级、灵活、扩展性强的web框架。他只提供简单的功能,如路由,请求和响应,模板渲染。它是用Jinja2模板引擎。他可以通过第三方插件来扩展功能,例如flask-sqlalchemy扩展orm。适合快速开发功能较少的小型系统。

可能会问:
和Django的区别

Django是一个功能更完善、体量更大的web框架。它相比flask提供更全面的功能,但也没有flask那么灵活。比如因为Django自带ORM,如果希望使用NoSQL数据库,比如mongodb,虽然也能用,但是等于自废Django的ORM。那么这种情况,个人感觉使用flask更灵活。

flask的生命周期

客户端—>wsgi应用程序->全局钩子–> 路由 --> 视图 --> 路由—> 全局钩子 —> wsgi应用程序—> 客户端

Flask中的蓝图(Blueprints)是什么?它们有什么作用?

蓝图是flask用来帮助你把一个大项目拆分成多个小模块,方便管理和维护。承担url和视图之间绑定关系的临时容器。

可能会问
运行机制
  • 在视图函数被蓝图的add_url_rule方法注册时,这个操作本质就是将视图和url地址的映射关系添加到蓝图的子路由列表deferred_functions中。
  • 蓝图对象根本没有路由表,当我们在蓝图中的视图函数上调用route装饰器(或者add_url_role函数)注册路由时,它只是在蓝图对象的内部的deferred_functions(子路由列表)中添加了一个路由项(路由项实际上就是一个绑定了视图和url地址的lambda匿名函数)
  • 执行app.register_blueprint()注册蓝图时,app应用实例对象会将从蓝图对象的 deferred_functions列表中循环取出每一个之前注册的路由项并把app应用实例对象自己作为参数执行路由项对应的lambda匿名函数,lambda匿名函数执行以后就会调用app.add_url_rule() 方法,这就将蓝图下子路由列表之前暂存的路由全部添加到了app应用实例对象的url_map总路由表中了,所以用户就可以在flask中访问到了蓝图中的视图。当然,能访问蓝图下的视图,自然也就可以通过视图调用其他的功能,例如:蓝图下的其他功能函数或其他的模型对象了
源码流程

请添加图片描述

Flask常用扩展库有哪些?

答:常用的扩展库有这些:

  • SQLAlchemy:用于ORM,对数据库进行操作
  • flask-Migrate:进行数据迁移
  • flask-WTF:处理表单数据
  • flask-login:用户认证
  • flask-mail:邮箱发送

ORM

WebSocket

可以简述下flask的上下文是什么吗?怎么使用?

答:flask上下文是用来管理请求和应用的状态。他分为请求上下文和应用上下文。
请求上下文是用来获取请求的相关数据,比如请求方式,请求地址,cookie等。在flask中就是使用request来保存http请求的数据,而使用session来管理会话的数据。这里有一点比较特别的,就是这两个对象只会创建一次,不会因为客户端不同的请求而创建多个对象。
应用上下文current_app则是存储flask应用实例对象的变量,可以用它来获取当前应用的配置文件current_app.config、连接什么数据库、当前flask应用在什么机器,用什么ip,内存有多大。

request只能在请求视图中使用

from flask import Flask, request, session, current_app, g

app = Flask(__name__)
app.config["SECRET\_KEY"] = "my secret key"

def test():
    print(request) # 请求上下文所提供的对象[request或session]只能被视图直接或间接调用!

@app.route("/")
def index():
    print(request)
    print(session)
    #外部函数在视图函数里面调用,也能使用request或session
    test()
    return "ok"

# 声明和加载配置
class Config(object):
    DEBUG = True
app.config.from_object(Config)

# 编写路由视图
@app.route(rule='/index1')
def index1():
    #打印应用上下文对象
    print("应用上下文对象:",app)

    #判断我们设置的app与falsk内置的current\_app是否相同
    print(app == current_app) #True

    # 应用上下文提供给我们使用的变量,也是只能在视图或者被视图调用的地方进行使用,
    # 但是应用上下文的所有数据来源于于app,每个视图中的应用上下文基本一样
    print(current_app.config)   # 获取当前项目的所有配置信息
    print(current_app.url_map)  # 获取当前项目的所有路由信息

    return "<h1>hello world!</h1>"

if __name__ == '\_\_main\_\_':
    # print(request) # 没有发生客户端请求时,调用request会超出请求上下文的使用范围!
    app.run(host="0.0.0.0", port=5000, debug=True)


flask如何处理报错或异常(主要问是怎么处理http异常)

答:使用装饰器app.errorhandler装饰异常处理函数,向装饰器传入报错或异常的状态码,然后可以在异常处理函数中定制http异常发生之后进行的操作,比如跳转页面。

@app.errorhandler(404)
def not_found_error(error):
    return 'This page does not exist', 404
可能会问:
flask是否可以主动抛http异常的方法

abort(状态码)

flask如何在请求前后执行一些操作呢?

答:flask可以使用请求全局钩子实现,它类似Django中的中间件,常用的钩子函数有:

  • before_first_request
    • 在处理第一个请求前执行[项目刚运行第一次被客户端请求时执行的钩子]
  • before_request
    • 在每一次请求前执行[项目运行后,每一次接收到客户端的request请求都会执行一次]
    • 如果在某修饰的函数中返回了一个响应,视图函数将不再被调用
  • after_request
    • 如果没有抛出错误,在每次请求后执行
    • 接受一个参数:视图函数作出的响应
    • 在此函数中可以对响应值在返回之前做最后一步修改处理
    • 需要将参数中的响应在此参数中进行返回
  • teardown_request:
    • 在每一次请求后执行
    • 接受一个参数:错误信息,如果有相关错误抛出
    • 需要设置flask的配置DEBUG=False,teardown_request才会接受到异常对象。

当多个不同实际的钩子存在,他们的执行顺序是以视图执行为中心点,before_request之前(请求过程)的先装饰先执行,after_request之后(响应过程),后装饰的先执行。

from flask import Flask, session

# 应用实例对象
app = Flask(__name__)

"""给app单独设置配置项"""
# 设置秘钥
app.config["SECRET_KEY"] = "my SECRET KEY"

@app.before_first_request
def before_first_request():
    """
    这个钩子会在项目启动后第一次被用户访问时执行
    可以编写一些初始化项目的代码,例如,数据库初始化,加载一些可以延后引入的全局配置
    """
    print("----before_first_request----")
    print("系统初始化的时候,执行这个钩子方法")
    print("会在接收到第一个客户端请求时,执行这里的代码")


@app.before_request
def before_request():
    """
    这个钩子会在每次客户端访问视图的时候执行
    # 可以在请求之前进行用户的身份识别,以及对于本次访问的用户权限等进行判断。..
    """
    print("----before_request----")
    print("每一次接收到客户端请求时,执行这个钩子方法")
    print("一般可以用来判断权限,或者转换路由参数或者预处理客户端请求的数据")


@app.after_request
def after_request(response):
    print("----after_request----")
    print("在处理请求以后,执行这个钩子方法")
    print("一般可以用于记录会员/管理员的操作历史,浏览历史,清理收尾的工作")

    response.headers["Content-Type"] = "application/json"
    response.headers["Company"] = "python.Edu..."

    # 必须返回response参数
    return response


@app.teardown_request
def teardown_request(exc):
    print("----teardown_request----")
    print("在每一次请求以后,执行这个钩子方法")
    print("如果有异常错误,则会传递错误异常对象到当前方法的参数中")
    # 在项目关闭了DEBUG模式以后,则异常信息就会被传递到exc中,我们可以记录异常信息到日志文件中
    print(f"错误提示:{exc}")  # 异常提示


@app.route("/")
def index():
    print("-----------视图函数执行了---------------")
    return "ok"

if __name__ == '__main__':
    # 启动项目的web应用程序
    app.run(host="0.0.0.0", port=5000, debug=False)

如何在Flask中实现跨域请求(CORS)?

答:使用flask-cors扩展来处理跨域请求。然后在项目app初始化,使用CORS封装app

from flask_cors import CORS
from flask import Flask

app = Flask(__name__)
CORS(app)

如何在Flask中处理后台任务?

答:可以使用Celery来处理后台任务。项目中引入celery,然后配置好celery的broker的url和存储结果的队列的url,在初始化项目实例对象处初始化

from celery import Celery
 
def make_celery(app):
    celery = Celery(
        app.import_name,
        backend=app.config['CELERY_RESULT_BACKEND'],
        broker=app.config['CELERY_BROKER_URL']
    )
    celery.conf.update(app.config)
    return celery
 
app.config.update(
    CELERY_BROKER_URL='redis://localhost:6379/0',
    CELERY_RESULT_BACKEND='redis://localhost:6379/0'
)
 
celery = make_celery(app)
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐