闭包和装饰器
闭包:就是在一个外函数中定义了一个内部函数,内部函数引用了外函数的临时变量,并且外函数的返回值就是内函数的引用
装饰器:本质就是闭包,主要作用为已经存在的对象添加额外的功能,例如日志记录,数据校验等
匿名函数
lambda 通常用于需要一个函数,但是又不想费神命名一个函数的时候使用,可节约内存空间
lambda x,y:x+y
lambda 后面是传入的参数 :代表return
迭代器
基础概念:
1.iteration:迭代
2.iterable:可迭代对象 可重复迭代 满足如下其中之一的都是iterable
可以for循环:for i in iterable
可以按照index索引的对象:也就是定义了__getitem__ 方法 比如list str
定义了__iter__方法 可以随意返回
可以调用iter(obj)的对象 并且返回一个iterator
3.iterator:迭代器对象 只能迭代next一次 需要满足如下的迭代器协议
定义了__iter__方法 但是必须返回自身
定义了next方法,在python3.x中是__next__ 用来返回下一个值,并且当没有数据了,抛出StopIteration
可以保持当前的状态
str和list是iterable 但不是iterator
2.自定义iterator 与数据分离
自定义iterator 就是定义__iter__ 和next方法
```
class DataIter(object):
def __init__(self,*args):
self.data=list(args)
self.ind=0
def __iter__(self):
return self
def next(self):
if self.ind==len(self.data):
raise StopIteration
else:
data=self.data[self.ind]
self.ind+=1
return data
```
把iterator从数据中分离出来,分别定义一个iterable和iterator
```
class Data(object):
def __init__(self,*args):
self.data=list(args)
def __iter__(self):#并没有返回自身
return DataIterator(self)
```
数据可以复用,因为每次返回一个DataIterator,xrange的实现便是这种数据和迭代分离的形式,很省内存
为什么使用for可以迭代迭代器对象,因为for替我们做了next的活,以及接收stopIteration的处理
生成器
生成器是一种特殊的迭代器,生成器自动实现了“迭代器协议”(即__iter__和next方法),不需要再手动实现两方法。
与生成器不同的是生成器的实现方式不同,可以通过生成器表达式和生成器函数两种方式实现,代码更简洁
1.包含yield的函数
生成器函数跟普通函数只有一点不一样,就是把return换成yield,内部实现了迭代器协议,同时保持状态可以挂起
yield是数据的生产者,for等是数据的消费者
```
def add():
for i in range(10):
yield i
g = add()#并没有真实执行函数,只是返回了一个生成器对象
print(g) # <generator object add at 0x10f6110f8>
print(next(g)) # 0 真正执行函数,执行到yield一个返回值,然后就会挂起,保持当前的名字空间等状态,然后等待下一次的调用,从yield的下一行继续执行
print(next(g)) # 1
```
我们可以通过yield关键字来定义一个生成器函数,这个生成器函数返回值就是一个生成器对象;
yield可以理解为return,返回后面的值给调用者。不同的是return返回后,函数会释放,而生成器则不会。在直接调用next方法或用for语句进行下一次迭代时,生成器会从yield下一句开始执行,直至遇到下一个yield。
2.生成器表达式
语法:(返回值 for 元素 in 可迭代对象 if 条件)
列表解析式的中括号换成小括号
返回一个生成器 生成器也是一个对象 属于中间值
```
g=(i **2 for iin range(5))
print(g) #<generator object <genexpr> at 0x01167C30>
print(g.__next__()) #0
print(g.__next__()) #1
print(g.__next__()) #4
```
生成器表达式和列表解析式的区别
上下文管理器
>定义:在使用python编程中,有一个特殊的语句块,执行这个语句块之前需要先执行一些准备动作,当语句块执行完成后,需要继续执行一些收尾动作。如操作文件时,操作前打开文件,操作后关闭文件,对于这些情况,python提供了上下文管理器概念,可以通过上下文管理器来定义控制代码块执行前的准备动作以及执行后的收尾动作
原理:创建一个上下文管理器类型的时候,就需要实现__enter__和__exit__方法,在python中,可以通过with语句来方便的使用上下文管理器,with语句可以在代码块运行前进入一个运行时上下文(执行__enter__方法),并在代码块结束后退出该上下文(执行__exit__方法)
```
with open ("1.txt",mode='w',encoding='utf-8) as file:
file.write('hello world')
```
应用场景
文件操作、进程线程之间互斥对象、支持上下文其他对象
自定义类实现
```
class Sample():
def __enter__(self):
return self
#exc_type:错误的类型 exc_val:代码类型对应的值 exc_tb:代码中错误发生的位置
def __exit__(self,exc_type,exc_val,exc_tb):
print(exc_type,exc_value)
```
参数传递
参数传递类似于浅拷贝,传递的是值,对于不可变对象,他的引用是不可变的,所以传值之后,相当于新建了一个不可变对象 对于可变对象,传递的值,相当于副本,改变的时候引用里的值也会发生变化
深拷贝、浅拷贝
深拷贝:copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象,包括值与内存地址
浅拷贝:拷贝父对象,不会拷贝对象的内部的子对象。修改父对象的时候,不相互影响,修改子对象的时候,影响
三种形式,1:切片 a=b[:] 2:copy函数 a=copy.copy(b) 3、工厂函数 a=list(b)
比如a=[1,2,3,[4,5]] b=a.copy() 修改b.append(5) a的值不会变 修改b[3].append(6) a的值会变,因为[4,5]已经是子对象了,未被拷贝,a和b是指向同样的地址的
=: 引用赋值 a=b 修改a,b也会受影响 a相当于b的别名
is:比较两边的内存地址是否相同
==:比较两边的值是否相等
内存管理
三个方面:1、对象的引用计数机制,2、垃圾回收机制,3、内存池机制
1、对象引用计数机制
python使用计数机制,来追踪内存中的对象,所有对象都要引用计数
引用计数增加的情况
1:一个对象分配一个新的名称
2:将其放入一个容器中,如列表、元组、字典
引用计数减少的情况
1:使用del语句对对象别名显示的销毁
2:引用超出作用域或被重新赋值
对于不可变的数据类型,如字符串,数字,解释器会在程序中的不同部分共享内存,以便简约内存
2、垃圾回收机制
1:当一个对象的引用计数归零的时候,它将被垃圾收集机制处理掉
2:当两个对象a和b相互引用的时候,del语句可以减少a b的引用计数,并销毁用于引用底层对象的名称,然而由于每个对象都包含一个对其他对象的引用,因此引用计数不会归零,对象也不会销毁。(从而导致内存泄露)。为解决这一问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。
3、内存池机制
Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。
1,Pymalloc机制。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。
2,Python中所有小于256个字节的对象都使用pymalloc实现的分配器,而大的对象则使用系统的malloc。
3,对于Python对象,如整数,浮点数和List,都有其独立的私有内存池,对象间不共享他们的内存池。也就是说如果你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。
内存泄露
如果存在循环引用,并且被循环引用的对象定义了__del__方法,就会发生内存泄露,对象定义了__del__方法,这些对象变为uncollectable,垃圾回收机制无法收集这些对象
创建对象后,Python解释器默认调用__init__()方法。
当删除一个对象时,Python解释器也会默认调用一个方法,这个方法为__del__()方法,手动调用del还是由Python自动回收都会触发__del__方法执行。
当使用del删除变量指向的对象时,如果对象的引用计数不会1,比如3,那么此时只会让这个引用计数减1,即变为2,当再次调用del时,变为1,如果再调用1次del,此时会真的把对象进行删除,
但是del xxx 不会主动调用__del__方法,只有引用计数 == 0时,__del__()才会被执行,并且定义了__del_()的实例无法被Python的循环垃圾收集器收集,所以尽量不要自定义__del__()。一般情况下,__del__() 不会破坏垃圾处理器。
python2和python3的区别
1、Python3 使用 print 必须要以小括号包裹打印内容,比如 print('hi')
Python2 既可以使用带小括号的方式,也可以使用一个空格来分隔打印内容,比 如 print 'hi'
2、python2 range(1,10)返回列表,python3中返回迭代器,节约内存
3、python2中使用ascii编码,python中使用utf-8编码
4、python2中unicode表示字符串序列,str表示字节序列
python3中str表示字符串序列,byte表示字节序列
多态
作用:让具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容(功能)的函数。
特点:
1、只关心对象的实例方法是否同名,不关心对象所属的类型;
2、对象所属的类之间,继承关系可有可无;
3、多态的好处可以增加代码的外部调用灵活度,让代码更加通用,兼容性比较强;
4、多态是调用方法的技巧,不会影响到类的内部设计。
字典的实现原理
python中的字典底层依靠哈希表(hash table)实现
哈希表是key-value类型的数据结构, 可以理解为一个键值需要按照一定规则存放的数组, 而哈希函数就是这个规则
字典本质上是一个散列表(总有空白元素的数组, python至少保证1/3的数组是空的), 字典中的每个键都占用一个单元, 一个单元分为两部分, 分别是对键的引用和对值的引用, 使用hash函数获得键的散列值, 散列值对数组长度取余, 取得的值就是存放位置的索引
哈希冲突(数组的索引相同), 使用开放寻址法解决
由于哈希算法被计算的数据是无限的,而计算后的结果范围有限,因此总会存在不同的数据经过计算后得到的值相同,这就是哈希冲突。
从发生冲突的那个单元起,按照一定的次序,从哈希表中找到一个空闲的单元。然后把发生冲突的元素存入到该单元的一种方法。开放定址法需要的表长度要大于等于所需要存放的元素。
JSON和字典的区别
json是一种数据格式,是纯字符串
dict是一个完整的数据结构
json的key可以是有序、重复的;dict的key不可以重复
json的value只能是字符串、浮点数、布尔值或者null,或者它们构成的数组或者对象。
json任意key存在默认值undefined,dict默认没有默认值
格式就会有一些形式上的限制,比如json的格式要求必须且只能使用双引号作为key或者值的边界符号(值如果是数字可以不用加双引号),不能使用单引号,用单引号或者不用引号会导致读取数据错误,而且“key”必须使用边界符(双引号),但字典就无所谓了,可以使用单引号,也可以使用双引号。
重载和重写
答:重载是指函数名相同,参数列表不同的函数实现方法。它们的返回值可以不同,但返回值不可以作为区分不同重载函数的标志。
重写是指函数名相同,参数列表相同,只有方法体不同的实现方法,即函数花括号里面不一样,一般用于子类继承父类时对父类方法的重写,父类函数中必须要有virtual关键字。(子类的同名方法屏蔽了父类方法的现象称为隐藏。
python多线程
1.线程概念:
线程是指进程内的一个执行单元,也是进程内的可调度实体.
与进程的区别:
(1) 地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;
(2) 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
(3) 线程是处理器调度的基本单位,但进程不是.
(4) 二者均可并发执行.
简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
线程的划分尺度小于进程,使得多线程程序的并发性高。
另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
2.线程和进程间的区别
进程就是一个应用程序在处理机上的一次执行过程,它是一个动态的概念,而线程是进程中的一部分,进程包含多个线程在运行。
多线程可以共享全局变量,多进程不能。多线程中,所有子线程的进程号相同;多进程中,不同的子进程进程号不同。
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
3.python线程模块
python主要是通过thread和threading这两个模块来实现多线程支持。
python的thread模块是比较底层的模块,python的threading模块是对thread做了一些封装,可以更加方便的被使用。
但是python(cpython)由于GIL的存在无法使用threading充分利用CPU资源,如果想充分发挥多核CPU的计算能力需要使用multiprocessing模块(Windows下使用会有诸多问题)。
3.1创建线程
python3.x中通过threading模块创建新的线程有两种方法:
一种是通过threading.Thread(Target=executable Method)-即传递给Thread对象一个可执行方法(或对象)
第二种是继承threading.Thread定义子类并重写run()方法。第二种方法中,唯一必须重写的方法是run()
3.2线程同步
线程执行是乱序的,要使之有序,需要进行线程同步
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。
使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。
需要注意的是,Python有一个GIL(Global Interpreter Lock)机制,任何线程在运行之前必须获取这个全局锁才能执行,每当执行完100条字节码,全局锁才会释放,切换到其他线程执行。
多线程实现同步有四种方式:锁机制,信号量,条件判断和同步队列。
3.2.1锁机制
threading的Lock类,用该类的acquire函数进行加锁,用realease函数进行解锁
3.2.2线程同步队列queue
Python的queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。
queue模块中的常用方法:
queue.qsize() 返回队列的大小
queue.empty() 如果队列为空,返回True,反之False
queue.full() 如果队列满了,返回True,反之False
queue.full 与 maxsize 大小对应
queue.get([block[, timeout]])获取队列,timeout等待时间
queue.get_nowait() 相当Queue.get(False)
queue.put(item) 写入队列,timeout等待时间
queue.put_nowait(item) 相当Queue.put(item, False)
queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
queue.join() 实际上意味着等到队列为空,再执行别的操作
3.3 线程池
我们把任务放进队列中去,然后开N个线程,每个线程都去队列中取一个任务,执行完了之后告诉系统说我执行完了,然后接着去队列中取下一个任务,直至队列中所有任务取空,退出线程。
使用线程池:
由于线程预先被创建并放入线程池中,同时处理完当前任务之后并不销毁而是被安排处理下一个任务,因此能够避免多次创建线程,从而节省线程创建和销毁的开销,能带来更好的性能和系统稳定性。
服务器CPU核数有限,能够同时并发的线程数有限,并不是开得越多越好,以及线程切换是有开销的,如果线程切换过于频繁,反而会使性能降低
线程执行过程中,计算时间分为两部分:
CPU计算,占用CPU
不需要CPU计算,不占用CPU,等待IO返回,比如recv(), accept(), sleep()等操作,具体操作就是比如
访问cache、RPC调用下游service、访问DB,等需要网络调用的操作
https://blog.csdn.net/daiyu__zz/article/details/81912018
在Python中如何实现多线程?
一个线程就是一个轻量级进程,多线程能让我们一次执行多个线程。我们都知道,Python是多线程语言,其内置有多线程工具包。
Python中的GIL(全局解释器锁)确保一次执行单个线程。一个线程保存GIL并在将其传递给下个线程之前执行一些操作,这会让我们产生并行运行的错觉。但实际上,只是线程在CPU上轮流运行。当然,所有的传递会增加程序执行的内存压力。
多进程
通过multiprocessing 实现多进程
```
from multiprocessingimport Process
import time,random
def Test(name):
print("welcome to my house %s" % name)
time.sleep(random.randint(1,3))
print("see you next time %s" % name)
if __name__ =='__main__':
p1 = Process(target=Test,args=('鸣人',))
p2 = Process(target=Test,args=('佐助',))
p3 = Process(target=Test,args=('小樱',))
p1.start()
p2.start()
```
python数据库处理
https://www.liaoxuefeng.com/wiki/1016959663602400/1017803857459008
python常用方法
a[::-1]和a[i:j]
https://blog.csdn.net/mingyuli/article/details/81604795
b = a[i:j] 表示复制a[i]到a[j-1],以生成新的list对象
a = [0,1,2,3,4,5,6,7,8,9]
b = a[1:3] # [1,2]
b = a[i:j:s]表示:i,j与上面的一样,但s表示步进,缺省为1.
所以a[i:j:1]相当于a[i:j]
当s<0时,i缺省时,默认为-1. j缺省时,默认为-len(a)-1
所以a[::-1]相当于 a[-1:-len(a)-1:-1],也就是从最后一个元素到第一个元素复制一遍,即倒序。
a='python'
b=a[::-1]
print(b) #nohtyp
c=a[::-2]
print(c) #nhy
#从后往前数的话,最后一个位置为-1
d=a[:-1] #从位置0到位置-1之前的数
print(d) #pytho
e=a[:-2] #从位置0到位置-2之前的数
print(e) #pyth
reverse
a = [1,2,3]
a.reverse()
a=[3,2,1]
返回值为NA,作用于自身
list
当数组不为空时:
if len(list_temp)
if list_temp:
字符串操作
https://www.cnblogs.com/yaxin1989/p/6129188.html
join
l1=['a','b','c','d']
s1=''.join(l1) abcd
s2='a'.join(l1) aabacada
s3='4'.join(l1) a4b4c4d4
split
s2.split('a') ['a','b','c','d']
s3.split('4') ['a','b','c','d']
strip
lstrip rstrip 去掉字符串两端字符,默认空格
s='222abc22222'
s.strip('2') 'abc'
s.lstrip('2') 'abc22222'
s.rstrip('2') '222abc'
bin函数
https://www.runoob.com/python/python-func-bin.html
判断二进制中有多少个1
bin(n).count('1')#将n转换成二进制,然后数里面的1的个数
format(n,'b') #将n转换成二进制
判断是否是数字、字母
str1="123"
str2="ABC"
str3="1AB"
str1.isdigit()#判断是否是数字
str2.isalpha()#判断是否是字母
str3.isalnum()#判断是否是字母和数字组合
python的次方
10的2次方 10**2
import math
math.pow(10,2)
2**31表示2的31次幂