面试题02

跨域是什么?解决方案?
跨域是指在浏览器中,当前正在运行的Web应用程序试图访问不属于它自己域名下的资源。由于浏览器实现了同源策略,即限制页面中的脚本只能读取同源下的数据,因此跨域请求会被浏览器禁止。
跨域问题的解决方案有以下几种:
	JSONP: 利用script标签没有跨域限制的特性,通过动态创建script标签,将需要请求的数据作为参数传递到服务端,服务端将数据放在一个回调函数中返回给客户端,客户端再执行该回调函数。
	CORS: 跨域资源共享是一种机制,它使用HTTP响应头来告诉浏览器允许跨域访问。当浏览器发现某个资源的响应头中包含了CORS相关的字段时,它会允许跨域访问。
	代理: 通过服务器端转发请求,绕过浏览器的限制,实现跨域访问。客户端发起请求时,将请求发送到自己的服务器上,服务器再向目标服务器发送请求,将结果返回给客户端。
	iframe: 利用iframe标签没有跨域限制的特性,将需要请求的资源嵌入到一个iframe中,然后通过window.postMessage()方法来进行通信。
Django请求生命周期?
Django 请求生命周期通常分为以下阶段:
	URL解析阶段:Django将请求的URL解析成一个路由匹配的View函数。
	中间件阶段:Django会按照中间件类的顺序依次执行中间件的process_request方法。中间件用于在请求处理前后进行一些通用的处理,比如权限验证、请求日志记录、请求参数解析等。
	视图函数阶段:Django执行匹配到的视图函数,视图函数负责处理请求并返回响应。
	中间件阶段:Django会按照中间件类的顺序依次执行中间件的process_response方法。中间件用于在请求处理前后进行一些通用的处理,比如添加响应头信息、响应日志记录、响应内容加工等。
	模板渲染阶段:如果视图函数返回的响应中包含模板,Django将会根据模板渲染出HTML页面。
	中间件阶段:Django会按照中间件类的顺序依次执行中间件的process_template_response方法。这个方法会对模板渲染的结果进行一些通用的处理,比如添加页面的统计代码、替换页面中的URL地址等。
	响应阶段:Django将响应返回给客户端。在返回响应的过程中,Django会按照中间件类的顺序依次执行中间件的process_exception方法,处理请求处理过程中产生的异常。如果没有异常产生,请求处理结束。

需要注意的是,请求处理过程中还有一些其它的细节,比如请求参数的验证、数据库事务的管理等,这些细节也会影响请求的生命周期。
Django处理并发
Django是一个基于WSGI的Web框架,WSGI本身是单线程的,因此Django默认是不能处理并发请求的。但是,可以通过以下方式来实现Django的并发处理:
	使用WSGI服务器:将Django应用部署到WSGI服务器上,如uWSGI、Gunicorn等,这些服务器都支持多线程、多进程的方式来处理并发请求。通过设置服务器的worker进程数和线程数,可以实现并发处理。
	使用异步框架:Django提供了异步框架Django Channels,可以用于处理高并发请求。Channels基于ASGI协议,支持异步IO和多线程处理方式,可以用于实现WebSockets、长轮询等高并发应用场景。
	使用缓存:将一些计算密集型的操作结果缓存到内存中,如数据库查询结果、计算结果等,可以减少请求处理时间,提高并发处理能力。
	使用分布式任务队列:将一些非实时的任务异步处理,如邮件发送、文件上传等,可以使用分布式任务队列如Celery来处理,从而减轻Web服务器的并发压力。

需要注意的是,处理并发请求不仅仅是提高服务器的处理能力,还需要考虑系统的稳定性和安全性。例如,使用异步框架需要考虑线程安全和并发冲突问题,使用缓存需要考虑缓存一致性和缓存击穿等问题。
Django中的FBV和CBV
在Django中,视图(View)是处理请求并返回响应的核心组件。视图可以分为基于函数的视图(Function-Based View,FBV)和基于类的视图(Class-Based View,CBV)两种方式。

FBV是Django最早的视图方式,也是最简单的一种方式。FBV的视图函数接收请求并返回响应,通过函数的参数和返回值来进行请求和响应的处理。FBV的优点是简单、灵活,适用于小型应用和简单的业务逻辑。
例如:
"""
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, World!")
"""

CBV是Django新增的一种视图方式,相比FBV更加灵活和可复用。CBV将视图封装成类,通过继承和重载类的方法来实现请求和响应的处理。CBV的优点是代码复用性强,可以通过类的继承、Mixin的方式来实现通用的视图功能,适用于大型应用和复杂的业务逻辑。
例如:
"""
from django.views import View
from django.http import HttpResponse

class IndexView(View):
    def get(self, request, *args, **kwargs):
        return HttpResponse("Hello, World!")
"""

在CBV中,类的方法对应着HTTP请求方法,例如get()方法对应GET请求、post()方法对应POST请求。CBV还支持Mixin类的方式来扩展视图功能,例如LoginRequiredMixin可以用于限制需要登录才能访问的视图。

总之,FBV和CBV都是Django中常用的视图方式,开发者可以根据应用场景和个人喜好来选择使用。

DRF 认证、权限和访问频率

DRF(Django Rest Framework)提供了许多内置的认证、权限和访问频率限制选项,以确保API端点的安全性和可靠性。

认证(Authentication)
认证是验证用户身份的过程。DRF提供了多种认证方式,包括:
	BasicAuthentication:基本的HTTP认证方式,需要用户提供用户名和密码;
	TokenAuthentication:用户需要提供包含认证令牌的HTTP请求头;
	SessionAuthentication:使用Django的会话框架进行认证;
	JSONWebTokenAuthentication:使用JSON Web Tokens进行认证。
开发人员可以根据需要选择使用其中的任何一种或多种认证方式,或自定义认证方式。要启用认证,可以在视图或全局设置中设置DEFAULT_AUTHENTICATION_CLASSES。

权限(Permission)
权限控制是指限制哪些用户可以访问特定资源或执行特定操作。DRF提供了多种权限类,包括:
	AllowAny:允许所有用户访问资源或执行操作;
	IsAuthenticated:要求用户已通过认证;
	IsAdminUser:要求用户是管理员;
	DjangoModelPermissions:根据Django模型的权限来限制用户访问资源或执行操作;
	DjangoObjectPermissions:根据Django模型实例的权限来限制用户访问资源或执行操作。
可以在视图或全局设置中设置DEFAULT_PERMISSION_CLASSES来指定默认的权限类,也可以在单个视图或视图集中设置permission_classes属性来覆盖默认值。

访问频率(Throttling)
访问频率限制是指限制用户在一定时间内可以访问资源的次数。DRF提供了多种限制访问频率的类,包括:
	AnonRateThrottle:限制匿名用户访问频率;
	UserRateThrottle:限制已认证用户的访问频率;
	ScopedRateThrottle:根据作用域限制用户访问频率。
可以在视图或全局设置中设置DEFAULT_THROTTLE_CLASSES来指定默认的访问频率限制类,也可以在单个视图或视图集中设置throttle_classes属性来覆盖默认值。
如果你来设计login,简单的说一下思路
思路:
	首先需要确定登录的目的和要求,例如要求用户提供用户名和密码来验证其身份,或者使用第三方身份验证,例如社交媒体登录或OAuth身份验证等。
	确定存储用户身份验证信息的方式。可以使用关系型数据库,例如MySQL或PostgreSQL,也可以使用NoSQL数据库,例如MongoDB或Redis等。如果是存储敏感信息,需要考虑加密和安全性。
	设计用户界面,包括登录页面、注册页面、忘记密码页面等。需要考虑用户友好性和易用性,同时要保证安全性,例如防止跨站点脚本攻击(XSS)和SQL注入攻击等。
	考虑使用密码哈希和盐值等技术来增强密码的安全性,以及使用多因素身份验证(MFA)等技术来增强用户身份验证的安全性。
	设计API,以便第三方应用程序可以使用登录功能。需要考虑API的安全性,例如使用OAuth或JSON Web Token(JWT)身份验证等。
	为了防止暴力攻击和恶意登录,需要考虑限制登录尝试次数和失败次数,例如使用带有延迟的防抖动机制或Google的reCAPTCHA等技术。
	最后需要进行测试和审查,以确保登录功能符合要求并且安全可靠。

总之,设计登录功能需要考虑多个方面,包括用户界面、身份验证、安全性、API、防御攻击等,需要综合考虑和实施。
1000万个数据里面删掉1000条,如何优化
如果要从1000万个数据中删除1000条数据,可以考虑以下优化方法:
	使用索引:在执行删除操作之前,可以使用索引来定位要删除的数据,以提高查询效率。
	批量删除:一次性删除所有要删除的数据,而不是逐条删除,可以大大减少数据库操作的次数,提高效率。
	优化删除语句:使用DELETE语句时,可以优化语句的WHERE子句,避免使用NOT IN等低效的操作符,以及在WHERE子句中使用索引。
	建立索引:可以为删除操作的列建立索引,这可以大大提高删除操作的速度。
	删除不必要的约束:如果表中有不必要的约束,例如外键约束、唯一约束等,可以考虑在删除操作之前删除这些约束,以提高删除操作的效率。
	执行删除操作的时间:可以考虑在业务负载较轻的时间段执行删除操作,以减少对系统性能的影响。
	分区删除:如果表是分区的,可以仅删除特定分区中的数据,而不是整个表,以提高删除操作的效率。

总之,为了优化从1000万个数据中删除1000条数据的操作,需要从多个方面综合考虑,包括索引、批量删除、优化删除语句、建立索引、删除不必要的约束、执行删除操作的时间和分区删除等。
多台设备登录,怎么实现告知先登录设备账号异地登录
要实现多台设备登录时告知用户账号异地登录的功能,可以考虑以下方法:
	使用Session和Token:在用户登录时,生成Session和Token,并将其保存到数据库中。每次用户登录时,都会生成新的Session和Token。在用户登录时,可以验证Token是否与数据库中的Token匹配,如果不匹配,说明账号在其他设备上登录了,可以发送邮件或短信通知用户。
	IP地址检测:检测用户登录时的IP地址,如果IP地址与之前的登录IP地址不同,说明账号在异地登录了,可以发送邮件或短信通知用户。
	设备识别码检测:检测用户登录时的设备识别码,如果设备识别码与之前的登录设备识别码不同,说明账号在异地登录了,可以发送邮件或短信通知用户。
	使用浏览器通知:当用户登录时,可以使用浏览器通知功能,在其他设备登录时,可以通过浏览器通知弹出提示框告知用户。

总之,要实现账号异地登录的功能,需要在用户登录时记录一些用户信息,例如Session和Token、IP地址、设备识别码等,并进行比对。当账号在其他设备上登录时,可以通过邮件、短信或浏览器通知等方式告知用户。需要注意的是,要保护用户的隐私和安全性,在发送通知时需要采取一些安全措施,例如加密、身份验证等。
mysql字符集,排序规则
	字符集指的是字符编码集合,例如 UTF-8、GBK、GB2312 等,不同的字符集支持不同的字符集合和字符编码方式。MySQL 支持多种字符集,包括 UTF-8、GBK、GB2312 等,可以通过创建数据库或表时指定字符集来设置。
	排序规则指的是字符在进行排序时的规则,包括大小写敏感或不敏感、重音符号的处理、字符间距的处理等。MySQL 支持多种排序规则,可以根据具体的应用场景选择合适的排序规则。
常见的 MySQL 字符集和排序规则有:
	UTF-8 字符集和 utf8_general_ci 排序规则:支持 Unicode 编码的字符集和基于字符序列的排序规则,大小写不敏感。
	GBK 字符集和 gbk_chinese_ci 排序规则:支持中文字符集的字符集和基于字符序列的排序规则,大小写不敏感。
	GB2312 字符集和 gb2312_chinese_ci 排序规则:支持中文字符集的字符集和基于字符序列的排序规则,大小写不敏感。
	Latin1 字符集和 latin1_swedish_ci 排序规则:支持西欧语言字符集的字符集和基于字符序列的排序规则,大小写不敏感。
什么是redis事务?
Redis事务是一组Redis命令的集合,可以在一次操作中一起执行。事务中的命令要么全部被执行,要么全部不执行,这保证了事务的原子性。Redis事务的实现是通过将多个命令打包成一个原子操作来实现的。
Redis事务包含以下三个步骤:
	开启事务:使用MULTI命令可以开启一个事务。
	执行事务:在MULTI和EXEC命令之间输入的所有命令都将作为一个事务执行。如果执行过程中出现错误,Redis将不会执行任何事务,并且回滚任何对Redis数据所做的更改。
	提交事务:如果执行成功,可以使用EXEC命令提交事务。
Redis事务还支持以下几个命令:
	WATCH命令:用于监视一个或多个键,以便在执行事务时检测它们是否被其他客户端更改。
	UNWATCH命令:用于取消WATCH命令所监视的所有键。
	DISCARD命令:用于取消事务,放弃执行中的所有命令。

Redis事务是Redis的一个重要特性,可以确保多个命令的原子性,从而使得Redis可以实现更加复杂和安全的数据操作。
redis内存满了怎么办
当Redis的内存使用达到限制时,需要采取措施以避免Redis发生内存溢出错误,影响系统的正常运行。以下是一些应对Redis内存满的方法:
	执行内存清理:通过执行CLEAR命令或者删除一些不必要的数据,释放一些内存空间。
	启用数据过期:可以使用Redis的过期时间功能,设置一些键的过期时间,以便自动清理不需要的数据。
	使用Redis集群:可以将数据分布在多个Redis节点上,每个节点只存储部分数据,从而有效地减少单个节点的内存使用。
	增加内存容量:可以通过增加Redis服务器的内存容量来扩展Redis的内存空间。
	优化Redis配置:通过调整Redis的一些配置参数,例如减少数据在内存中的冗余存储,可以有效地减少Redis的内存使用。
	使用Redis持久化:可以使用Redis的持久化功能,将一些数据存储到磁盘中,释放一些内存空间。

需要根据具体情况采取不同的措施,可以根据Redis的日志和监控工具,及时发现内存满的情况,并及时采取应对措施。
对称加密与非对称加密区别?
对称加密和非对称加密是两种不同的加密方法,其主要区别在于密钥的使用方式和加密过程中的操作。
	对称加密(Symmetric encryption)是一种加密方式,使用相同的密钥(也称为秘密密钥)对明文和密文进行加密和解密。在对称加密中,发送方使用密钥将明文加密成密文,接收方使用相同的密钥对密文进行解密还原为明文。对称加密算法通常比非对称加密算法更快速,但在密钥分发方面存在一些安全问题,例如如何确保密钥的安全传输以及如何避免密钥被未授权的人员获取。
常见的对称加密算法包括DES、3DES、AES等。

	非对称加密(Asymmetric encryption)是另一种加密方式,使用两个不同的密钥,一个是公钥(public key),一个是私钥(private key)。公钥可以公开分享给任何人,用于加密明文,而私钥只有拥有者才能访问,用于解密密文。在非对称加密中,发送方使用接收方的公钥来加密明文,接收方使用自己的私钥对密文进行解密还原为明文。
	非对称加密算法比对称加密算法更安全,但通常比对称加密算法更慢,因此常用于加密较小的数据或者进行数字签名等特定场景。
常见的非对称加密算法包括RSA、DSA等。
soap和rpc 介绍
SOAP和RPC都是用于实现分布式系统中远程过程调用(Remote Procedure Call,RPC)的协议,它们的主要区别在于协议的实现方式和数据传输格式。

SOAP(Simple Object Access Protocol)是一种基于XML的协议,用于在分布式系统中进行通信和数据交换。SOAP协议使用HTTP作为底层传输协议,并通过XML格式对数据进行编码和传输。SOAP协议支持多种数据编码方式,例如XML Schema、XML Data、JSON等。SOAP协议具有可扩展性和跨平台性,并且可以通过WSDL(Web Services Description Language)定义服务接口和参数。SOAP协议适合于复杂的分布式系统和企业级应用,例如Web服务和SOA(Service Oriented Architecture)。

RPC(Remote Procedure Call)是一种基于传统的过程调用模式,用于在分布式系统中进行远程过程调用。RPC协议使用类似于本地过程调用的方式,将参数传递给远程服务,接收返回结果并进行处理。RPC协议通常使用TCP或UDP作为底层传输协议,并且使用二进制格式对数据进行编码和传输。RPC协议简单易用,并且适合于轻量级分布式系统和实时通信应用。

总之,SOAP和RPC都是用于实现分布式系统中远程过程调用的协议。SOAP协议使用HTTP作为底层传输协议,使用XML格式对数据进行编码和传输,适合于复杂的分布式系统和企业级应用;而RPC协议使用TCP或UDP作为底层传输协议,使用二进制格式对数据进行编码和传输,适合于轻量级分布式系统和实时通信应用。
python常用数据结构有哪些?请简要介绍一下
	列表(List):列表是一种有序、可变的序列结构,可以存储不同类型的元素。列表可以通过下标访问、修改和删除元素,也可以进行切片、拼接和排序等操作。
	元组(Tuple):元组是一种有序、不可变的序列结构,可以存储不同类型的元素。元组与列表类似,但是一旦创建就不能修改元素,因此更适合于保存不变的数据。
	集合(Set):集合是一种无序、不重复的元素集合,可以进行交、并、差和对称差等操作。集合元素必须是不可变的,例如数字、字符串、元组等。
	字典(Dictionary):字典是一种无序、可变的键值对结构,可以用来存储和查找数据。字典的键必须是不可变的类型,例如数字、字符串、元组等,值可以是任意类型的对象。
	堆(Heap):堆是一种特殊的二叉树结构,具有堆序性质。堆常用于实现优先队列和排序算法,Python中提供了heapq模块来支持堆的操作。
	队列(Queue):队列是一种先进先出(FIFO)的数据结构,可以用来实现消息队列、任务队列等。Python中提供了queue模块来支持队列的操作,包括FIFO队列、LIFO队列、优先级队列等。
简要描述Python中单引号、双引号、三引号的区别
在Python中,单引号、双引号、三引号都可以用来表示字符串,它们的主要区别如下:
	单引号和双引号:单引号和双引号都可以用来表示单行字符串,二者没有本质区别,只是在某些特殊情况下使用其中之一会更方便,例如字符串本身包含单引号或双引号时。
	三引号:三引号可以用来表示多行字符串或包含换行符的单行字符串。三引号字符串可以跨越多行,不需要使用转义字符,可以包含单引号、双引号和换行符等特殊字符。三引号字符串通常用于表示文档字符串或长字符串常量。

举例如下:
# 单引号和双引号表示单行字符串
s1 = 'hello'
s2 = "world"

# 三引号表示多行字符串
s3 = '''This is a 
       multi-line 
       string.'''

# 三引号表示包含换行符的单行字符串
s4 = """This is a 
        multi-line 
        string."""

# 三引号表示文档字符串
def foo():
    """
    This is a docstring.
    """
    pass

需要注意的是,在Python中,单引号和双引号可以互相嵌套,但是不能嵌套三引号。如果需要在三引号字符串中使用单引号或双引号,可以使用转义字符,例如\"表示双引号,\'表示单引号。
Python里面如何拷贝一个对象?(赋值、浅拷贝、深拷贝的区别)
在Python中,拷贝对象有三种方式:赋值、浅拷贝和深拷贝。
	赋值:使用赋值语句可以将一个对象的引用赋给另一个变量,这两个变量将引用同一个对象。当其中一个变量修改对象时,另一个变量也会受到影响。
	浅拷贝:使用浅拷贝可以创建一个新对象,这个新对象与原对象的值相同,但是引用的对象仍然是原对象引用的对象。也就是说,浅拷贝会创建一个新的对象,但是新对象中的某些元素仍然是原对象中元素的引用,因此当原对象中引用的对象发生改变时,新对象中相应的元素也会发生改变。
	深拷贝:使用深拷贝可以创建一个新对象,这个新对象与原对象的值相同,但是引用的对象也是全新创建的对象。也就是说,深拷贝会递归地复制原对象及其引用的对象,直到所有引用的对象都是全新创建的为止。因此,当原对象中引用的对象发生改变时,新对象中相应的元素不会发生改变。
	
import copy

# 赋值
a = [1, 2, 3]
b = a  # b引用了a所引用的对象
a[0] = 0
print(b)  # [0, 2, 3]

# 浅拷贝
a = [[1, 2], [3, 4]]
b = copy.copy(a)  # 创建一个新对象,并将a中的元素的引用复制给b
a[0][0] = 0
print(b)  # [[0, 2], [3, 4]]

# 深拷贝
a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)  # 创建一个新对象,并递归地复制a中的元素及其引用的对象
a[0][0] = 0
print(b)  # [[1, 2], [3, 4]]
请写出一段Python代码实现删除一个list里面的重复元素
mylist = [1, 2, 3, 2, 4, 3, 5, 1]
mylist = list(set(mylist))
print(mylist)  # [1, 2, 3, 4, 5]
Python语言中,这两个参数是什么意思:args,*kwargs?
在 Python 中,args 和 *kwargs 都是用来处理函数参数的机制。

	args 是一个用于接收任意数量非关键字参数的元组,它可以让函数接收不定长度的参数。在函数定义时,使用 *args 表示定义一个接收任意数量非关键字参数的元组,当函数被调用时,实际传入的参数会被打包成一个元组,然后作为 args 参数传入函数中。例如:

def myfunc(*args):
    for arg in args:
        print(arg)

myfunc(1, 2, 3)  # 1\n 2\n 3\n

上面的示例中,myfunc 函数定义了一个 *args 参数,它可以接收任意数量的参数并打印出来。在调用 myfunc 函数时,传入的参数会被打包成一个元组 (1, 2, 3),然后作为 args 参数传递给 myfunc 函数。

	kwargs 是一个用于接收任意数量关键字参数的字典,它可以让函数接收任意数量的关键字参数。在函数定义时,使用 **kwargs 表示定义一个接收任意数量关键字参数的字典,当函数被调用时,实际传入的参数会被打包成一个字典,然后作为 kwargs 参数传入函数中。例如:
    
def myfunc(**kwargs):
    for key, value in kwargs.items():
        print(key, value)

myfunc(name='Tom', age=18, gender='male')  # name Tom\n age 18\n gender male\n

在 Python 中,args 和 *kwargs 都是用来处理函数参数的机制。

args 是一个用于接收任意数量非关键字参数的元组,它可以让函数接收不定长度的参数。在函数定义时,使用 *args 表示定义一个接收任意数量非关键字参数的元组,当函数被调用时,实际传入的参数会被打包成一个元组,然后作为 args 参数传入函数中。例如:

python
Copy code
def myfunc(*args):
    for arg in args:
        print(arg)

myfunc(1, 2, 3)  # 1\n 2\n 3\n
上面的示例中,myfunc 函数定义了一个 *args 参数,它可以接收任意数量的参数并打印出来。在调用 myfunc 函数时,传入的参数会被打包成一个元组 (1, 2, 3),然后作为 args 参数传递给 myfunc 函数。

kwargs 是一个用于接收任意数量关键字参数的字典,它可以让函数接收任意数量的关键字参数。在函数定义时,使用 **kwargs 表示定义一个接收任意数量关键字参数的字典,当函数被调用时,实际传入的参数会被打包成一个字典,然后作为 kwargs 参数传入函数中。例如:

python
Copy code
def myfunc(**kwargs):
    for key, value in kwargs.items():
        print(key, value)

myfunc(name='Tom', age=18, gender='male')  # name Tom\n age 18\n gender male\n
上面的示例中,myfunc 函数定义了一个 **kwargs 参数,它可以接收任意数量的关键字参数并打印出来。在调用 myfunc 函数时,传入的参数会被打包成一个字典 {'name': 'Tom', 'age': 18, 'gender': 'male'},然后作为 kwargs 参数传递给 myfunc 函数。

posted on 2023-03-08 16:54  cloud_wh  阅读(16)  评论(0编辑  收藏  举报

导航