#####多线程、多进程和协程的区别与联系
多线程:
线程是进程的一个实体,是CPU进行调度的最小单位,他是比进程更小能独立运行的基本单位。
线程基本不拥有系统资源,只占用一点运行中的资源(如程序计数器,一组寄存器和栈),但是它可以与同属于一个进程的其他线程共享全部的资源。
提高程序的运行速率,上下文切换快,开销比较少,但是不够稳定,容易丢失数据,形成死锁。
多进程:
进程是系统进行资源分配的最小单位,每个进程都有自己的独立内存空间,不用进程通过进程间通信来通信。
但是进程占据独立空间,比较重量级,所以上下文进程间的切换开销比较大,但是比较稳定安全。
协程:
协程:是更小的执行单位,是一种轻量级的线程,协程的切换只是单纯的操作CPU的上下文,所以切换速度特别快,且耗能小。
gevent是第三方库,通过greenlet实现协程,其基本思想是:
当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。
因为创建对象时__new__方法执行,并且必须return 返回实例化出来的对象
cls.__instance是否存在,不存在的话就创建对象,存在的话就返回该对象,
来保证只有一个实例对象存在(单列),打印ID,值一样,说明对象同一个
class Singleton(object):
__instance=None
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance =object.__new__(cls)
return cls.__instance
a=Singleton(18,'A')
b=Singleton(22,'B')
print(id(a))
print(id(b))
函数装饰器:函数能作为参数传递给其他函数,可以被赋值给其他变量,
可以作为返回值,可以被定义在另外一个函数内;
类装饰器:类具有__call__方法,当使用 @ 形式将装饰器附加到函数上时,
就会调用此方法;
应用场景: 插入日志、性能测试、事务处理、缓存、权限校验等
map函数在对可迭代对象的每一项应用特定函数后,会返回map对象。
# 基本用法
def num_set(num):
return num**2
print(map(num_set, [1, 3, 5])) # 返回值:
print(list(map(num_set, [1, 3, 5]))) # 返回值:[1, 9, 25]
# 拓展用法
# 1. 可以在function处使用匿名函数lambda
list(map(lambda x, y: (x ** y, x+y), [2, 4, 6], [3, 2, 1])) # 返回值:[(8, 5), (16, 6), (6, 7)]
# 2. map函数还可以用来进行类型转换
# 例如将元组转换为列表 :
list(map(int, (1, 2, 3))) # 返回值:[1, 2, 3] 将字符串转换为列表
# 将字符串转为列表:
list(map(int,'1234')) # 返回值:[1, 2, 3, 4]
# 3. 可以提取字典中的key
list(map(int,{'1':2,'2':3,'3':4})) # 返回值:[1, 2, 3]
print(list(map(str,{'data1':2,'data2':3,'data3':4}))) # 返回值:['data1', 'data2', 'data3']
zip函数获取可迭代对象,将它们聚合到一个元组中,然后返回结果。
zip()函数的语法是zip(*iterables)
numbers = [1, 2, 3]
string = ['one', 'two', 'three']
result = zip(numbers,string)
print(set(result))
-------------------------------------
{(3, 'three'), (2, 'two'), (1, 'one')}
Lambda函数是不带名称的单行函数,可以具有n个参数,但只能有一个表达式。
也称为匿名函数。
a=lambda x,y:x+y
print(a(5,6))
str="hello"
print(str[::-1])
面试几乎必问之一,要求能手写装饰器
装饰器(Decorator)是Python中一个有趣的功能。
它用于向现有代码添加功能。这也称为元编程,因为程序的一部分在编译时会尝试修改程序的另一部分。
def addition(func):
def inner(a,b):
print("numbers are",a,"and",b)
return func(a,b)
return inner
@addition
def add(a,b):
print(a+b)
add(5,6)
---------------------------------
numbers are 5 and 6
sum: 11
列表推导式:
print([j for i in li for j in i])
numpy方法
import numpy as np
print(np.array(li).flatten().tolist())
a = [1,2,3,4,5,6,7,8,9,10]
odd, even = [el for el in a if el % 2==1], [el for el in a if el % 2==0]
print(odd,even)
> ([1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
remove:将删除列表中的第一个匹配值,它以值作为参数。
del:使用索引删除元素,它不返回任何值。
pop:将删除列表中顶部的元素,并返回列表的顶部元素。
append:在列表末尾添加新元素。
insert:在列表的特定位置添加元素。
extend:通过添加新列表来扩展列表。
Python内存由Python的私有headspace管理。所有的Python对象和数据结构都位于
一个私有堆中。私用堆的分配由Python内存管理器负责。Python还内置了一个的垃圾
收集器,可以回收未使用的内存并释放内存,使其可用于headspace。
Python 有自动垃圾回收但还是会有内存泄漏的情况,现在总结一下三种常见的内存泄漏场景。
1、无穷大导致内存泄漏
如果把内存泄漏定义成只申请不释放,那么借着 Python 中整数可以无穷大的这个特点,我们一行代码就可以完成内存泄漏了。
i = 1024 ** 1024 ** 1024
2、循环引用导致内存泄漏
引用记数器 是 Python 垃圾回收机制的基础,如果一个对象的引用数量不为 0 那么是不会被垃圾回收的,我们可以通过 sys.getrefcount 来得到给定对象的引用数量。
3、外面库导致内存泄漏
这种情况我也只遇到过一次,之前 mysql-connector-python 的内存泄漏,导致我的程序跑着跑着占用的内存就越来越大;最后我们返的 C 语言扩展禁用之后就没有问题了。
内存溢出是指向JVM申请内存空间时没有足够的可用内存了,就会抛出OOM即内存溢出。
内存泄漏是指,向JVM申请了一块内存空间,使用完后没有释放,由于没有释放,这块内存区域其他类加载的时候无法申请,
同时当前类又没有这块内存空间的内存地址了也无法使用,相当于丢了一块内存,这就是内存泄漏。
值得注意的是内存泄漏最终会导致内存溢出,很好理解,内存丢了很多最后当然内存不够用了。
class dict(dict):
def __new__(cls, *args, **kwargs):
return super().__new__(cls)
def __getattr__(self, name):
if name.startswith('__'):
raise AttributeError
return self.get(name)
def __setattr__(self, name, val):
self[name] = val
def __hash__(self):
return id(self)
可以**
浅拷贝:创建新对象,其内容是原对象的引用,只拷贝一层。
深拷贝:和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。
深拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关联。
REST:指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序
或设计就是RESTful。
1、协议:API与用户的通信协议,总是使用HTTPs协议。
2、域名:应该尽量将API部署在专用域名之下
3、版本:应该将API的版本号放入URL。
4、路径:路径又称"终点"(endpoint),表示API的具体网址。在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。
5、http 动词:常用的HTTP动词有下面五个
GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
DELETE(DELETE):从服务器删除资源。
6、过滤信息:如果记录数量很多,服务器不可能都将它们返回给用户。
API应该提供参数,过滤返回结果。
7、状态码:服务器向用户返回的状态码和提示信息
8、错误处理:如果状态码是4xx,就应该向用户返回出错信息
9、返回结果:针对不同操作,服务器向用户返回的结果应该符合以下规范。
GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档
10、Hypermedia API:
RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,
使得用户不查文档,也知道下一步应该做什么。
11、其他:
服务器返回的数据格式,应该尽量使用JSON,避免使用XML
在Python中,字典是通过散列表(哈希表)实现的。字典也叫哈希数组或关联数组,
所以其本质是数组。
1、开放寻址法
2、再哈希法
3、链地址法
4、公共溢出区
5、装填因子
相同: 都是访问资源的令牌, 都可以记录用户信息,都是只有验证成功后
不同点:服务端验证客户端发来的token信息要进行数据的查询操作;
JWT验证客户端发来的token信息就不用, 在服务端使用密钥校验就可以,
不用数据库的查询。
- 在浏览器中输入URL 或者IP 或者域名
- 如果输入的是域名就需要进行DNS解析成IP地址,通过IP来确认是哪个服务器
- 建立TCP 请求(即三次握手)
- 发送http请求
- 服务器处理请求,并将处理结果返回给浏览器
- 最后断开连接(即四次挥手)
- 浏览器根据返回结果进行处理以及页面渲染
防止出现请求超时脏链接
__init__
__new__
__call__
__str__
__repr__
主要用于避免动态创建属性,它用于节省对象中的内存空间。
静态结构不允许在任何时候动态创建对象,而是在初始创建之后不允许这样做。
如果要实例化(数百个,数千个)同一类的对象,
则需要使用 `__slots__` 。`__slots__` 仅作为内存优化工具存在。
类(Class)被视为对象的蓝图。类中的第一行字符串称为doc字符串,包含该类的简短描述。
在Python中,使用class关键字可以创建了一个类。一个类包含变量和成员组合,称为类成员。
对象(Object)是真实存在的实体。在Python中为类创建一个对象,我们可以使用obj = CLASS_NAME()
例如:obj = num()
使用类的对象,我们可以访问类的所有成员,并对其进行操作。
self表示类的实例。
通过使用self关键字,我们可以在Python中访问类的属性和方法。
注意,在类的函数当中,必须使用self,因为类中没有用于声明变量的显式语法。
面向对象编程,抽象(Abstraction)、封装(Encapsulation)、继承(Inheritance)、多态(Polymorphism)
抽象(Abstraction):将一个对象的本质或必要特征向外界展示,并隐藏所有其他无关信息的过程。
封装(Encapsulation)意味着将数据和成员函数包装在一起成为一个单元。它还实现了数据隐藏的概念。
多态(Polymorphism):子类可以定义自己的独特行为,并且仍然共享其父类/基类的相同功能或行为。
Python可以支持多重继承。多重继承意味着,一个类可以从多个父类派生。
GIL 是python的全局解释器锁,同一进程中假如有多个线程运行,
一个线程在运行python程序的时候会霸占python解释器(加了一把锁即GIL),
使该进程内的其他线程无法运行,等该线程运行完后其他线程才能运行。
如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。
所以在多线程中,线程的运行仍是有先后顺序的,并不是同时进行。
多进程中因为每个进程都能被系统分配资源,相当于每个进程有了一个python解释器,
所以多进程可以实现多个进程的同时运行,缺点是进程系统资源开销大
__init__是初始化方法,创建对象后,就立刻被默认调用了,可接收参数
1、__new__至少要有一个参数cls,代表当前类,此参数在实例化时由
Python解释器自动识别
2、__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时
要特别注意,可以return父类(通过super(当前类名, cls))__new__出来的实例,
或者直接是object的__new__出来的实例
3、__init__有一个参数self,就是这个__new__返回的实例,__init__
在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
4、如果__new__创建的是当前类的实例,会自动调用__init__函数,
通过return语句里面调用的__new__函数的第一个参数是cls来保证是当前类实例,
如果是其他类的类名,;那么实际创建返回的就是其他类的实例,
其实就不会调用当前类的__init__函数,也不会调用其他类的__init__函数。
Python代码执行缓慢的原因,是因为它是一种解释型语言。它的代码在运行时进行解释,
而不是编译为本地语言。
为了提高Python代码的速度,我们可以使用CPython、Numba,或者我们也可以对代码
进行一些修改。
1. 减少内存占用。
2. 使用内置函数和库。
3. 将计算移到循环外。
4. 保持小的代码库。
5. 避免不必要的循环
1. 易于编码
2. 免费和开源语言
3. 高级语言
4. 易于调试
5. OOPS支持
6. 大量的标准库和第三方模块
7. 可扩展性(我们可以用C或C++编写Python代码)
8. 用户友好的数据结构
1. Web开发
2. 桌面GUI开发
3. 人工智能和机器学习
4. 软件开发
5. 业务应用程序开发
6. 基于控制台的应用程序
7. 软件测试
8. Web自动化
9. 基于音频或视频的应用程序
10. 图像处理应用程序
1. 速度
2. 移动开发
3. 内存消耗(与其他语言相比非常高)
4. 两个版本的不兼容(2,3)
5. 运行错误(需要更多测试,并且错误仅在运行时显示)
6. 简单性
Python中主要有四种类型的数据结构
列表:列表是从整数到字符串甚至另一个列表的异构数据项的集合。列表是可变的。
列表完成了其他语言中大多数集合数据结构的工作。列表在[ ]方括号中定义。
例如:a = [1,2,3,4]
集合:集合是唯一元素的无序集合。集合运算如联合|,交集&和差异,可以应用于集合。
{}用于表示一个集合。
例如:a = {1,2,3,4}
元组:Python元组的工作方式与Python列表完全相同,只是它们是不可变的。
()用于定义元组。
例如:a =(1,2,3,4)
字典:字典是键值对的集合。它类似于其他语言中的hash map。
在字典里,键是唯一且不可变的对象。
例如:a = {'number':[1,2,3,4]}
在Python中使用单引号(' ')或双引号(" ")是没有区别的,都可以用来表示一个字符串。
这两种通用的表达方式,除了可以简化程序员的开发,避免出错之外,还有一种好处,
就是可以减少转义字符的使用,使程序看起来更简洁,更清晰。
break:在满足条件时,它将导致程序退出循环。
continue:将返回到循环的开头,它使程序在当前循环迭代中的跳过所有剩余语句。
pass:使程序传递所有剩余语句而不执行。
==比较两个对象或值的相等性。
is运算符用于检查两个对象是否属于同一内存对象。
list1=[1,2,3,4]
list2=[1,2,3,4]
print(id(list1))
print(id(list2))
print(list1==list2)
print("*"*50)
print(list1 is list2)
"""
140105464266496
140105464277120
True
**************************************************
False
"""
!=如果两个变量或对象的值不相等,则返回true。
is not是用来检查两个对象是否属于同一内存对象。```
dic={"name":"zs","age":18,"city":"深圳","tel":"1362626627"}
print(dict(sorted(dic.items(),key=lambda x:x[0],reverse=False)))
1、细节上的错误,通过print()打印,能执行到print()说明一般上面的代码没有问题,分段检测程序是否有问题,如果是js的话可以alert或console.log
2、如果涉及一些第三方框架,会去查官方文档或者一些技术博客。
3、对于bug的管理与归类总结,一般测试将测试出的bug用teambin等bug管理工具进行记录,然后我们会一条一条进行修改,修改的过程也是理解业务逻辑和提高自己编程逻辑缜密性的方法,我也都会收藏做一些笔记记录。
4、导包问题、城市定位多音字造成的显示错误问题