python基础内容
- 一、多线程与多进程
- 二、迭代器和生成器
- 三、闭包
- 四、装饰器和面向切面编程AOP
- 五、有和无序、可变变量和不可变变量
- 六、形参和实参
- 七、dict和set
- 八、for 和 while
- 九、赋值、浅拷贝和深拷贝
- 十、队列和列表
- 十、补丁
- 十一、描述器
- 十二、继承
- 十三、多态性
- 十四、try、raise、except、else和finally
- 十五、sort()和sorted()
- 十六、堆(heapq)
- 十七、静态函数, 类函数, 成员函数、属性函数的区别
- 十八、推导式
- 十九、map、reduce、filter、zip
- 二十、*args和**kargs
- 二十一、Local、global、nonlocal、Enclosing和Built-in
- 二十二、is和==
- 二十三、自省机制
- 二十四、lambda
- 二十五、单下划线和双下划线
- 二十六、__new__和__init__的区别
- 二十七、新式类和旧式类
- 二十八、read, readline和readlines
- 二十九、with
- 三十、range和xrange的区别
- 三十一、位运算
- 三十二、any和all
- 三十二、正数与负数的求模、整除运算
一、多线程与多进程
进程(Process):进程是正在执行的计算机程序的实例。每个进程都有自己的存储空间,用于存储正在运行的指令,以及用于需要存储和访问任何数据。
线程(Thread):cpu调度执行的最小单元,不能独立存在,依赖进程存在。线程是进程的组成部分,可以并行运行。一个进程中可以有多个线程,它们共享相同的内存空间,即父进程的内存空间。这意味着要执行的代码以及程序中声明的所有变量将由所有线程共享。
GIL线程全局锁:python为了保证线程的安全而采取的独立线程运行的限制, 也就是说 一个核只能在同一时间运行一个线程,使得多线程并不能实现真正的并发工作(伪并发,快速切换),每一次只能一个线程执行一个任务。只有多进程才能实现多核并发。
多核多线程适合单核I/O密集型任务:
IO传输过程分为:发送消息,等待返回消息。在等候返回消息时线程是不工作的(阻塞状态),此时可以切换到其它线程里,即python此时释放GIL, 其他线程得到GIL发送消息,…,这样保证了IO传输过程大量减少阻塞时间,提高io传输效率。
多核多进程适合CPU密集型任务:
程序使用CPU进行大量数据运算和处理的时候,单核CPU的处理能力有限,多核CPU同时开启,分开处理才能加快处理速度。而只有多进程才能真正实现多核并发。使用多线程的话,多线程有可能因为争夺资源而变慢。
二、迭代器和生成器
迭代器是一种可迭代的对象。普通的迭代器是将全部对象存储到内存里,然后可以通过迭代(next())逐个读取。
生成器是一种只能迭代一次的迭代器,不会将对象存储到内存里,是一种即生成即输出的迭代器。这种方法的优点是节约内存,在训练模型的时候遇到数据过多,显存不足的时候经常使用这种方法。
yield类似return,它能将一个函数返回成生成器,当迭代对象耗尽才会结束。
yeild的原理:python 的 generator 只保留栈帧上下文(yield暂停的方法),不保留调用栈,然后运行栈帧上下文的字节码,而且 generator 函数不允许 return。只有调用next()函数的时候才会执行函数语句,在for循环中会自动调用next()方法。函数执行过程中遇到一个yield会中断一次,返回一个迭代值,函数保存自己的变量和状态,下次迭代时从yield下一条语句继续执行(这个性质很重要),函数恢复之前状态,直到遇到下一个yield返回迭代值,这样循环。
a = (x*x for x in range(3)) 这样也是个生成器#小括号
(1)生成器(generator)能够迭代的关键是他有next()方法,工作原理就是通过重复调用next()方法,直到捕获一个异常。
(2)带有yield的函数不仅仅是只用于for循环,而且可用于某个函数的参数,只要这个函数的参数也允许迭代参数。
(3)send()和next()的区别就在于send可传递参数给yield表达式,这时候传递的参数就会作为yield表达式的值,而yield的参数是返回给调用者的值,也就是说send可以强行修改上一个yield表达式值。
(4)send()和next()都有返回值,他们的返回值是当前迭代遇到的yield的时候,yield后面表达式的值,其实就是当前迭代yield后面的参数。
(5)第一次调用时候必须先next()或send(),否则会报错,send后之所以为None是因为这时候没有上一个yield,所以也可以认为next()等同于send(None)
三、闭包
原理:两个函数嵌套,外部函数返回内部函数的引用,外部函数一定会传入参数,外部函数起的是交换引用的作用
def f1(a):
def f2(b):
....
return f2(a)
作用:闭包可以将其自己的代码和作用域以及外部函数的作用结合在一起。
闭包函数需要满足两点:函数内部定义的函数;引用了外部变量但非全局变量。
四、装饰器和面向切面编程AOP
python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。装饰器函数的外部函数传入要装饰的函数名字,返回经过修饰后函数的名字;内层函数(闭包)负责修饰被修饰函数
AOP:在运行时,编译时,类和方法加载时,动态地将代码切入到类的指定方法、指定位置上的编程思想
def f1(f): #f1为装饰器的名,f为装饰器传入的参数
def f2():
print(1)
f() #调用f
return f2
@f1
def a():
print(2)
a()
#output: 1
2
#通过将a()装饰成f1()的输入参数,然后调用a()的时候变成直接运行f1(),此时f1()就具有a()的功能
#假如再装饰个b(),那么调用b()时,f1()就会具有b()的功能
@f1
def b():
print(3)
b()
#output: 1
3
装饰器的优点是避免大量雷同代码,像上面的都要print(1),就可以把这部分代码写到装饰器里,然后将a()和b()装饰,之后调用a()和b()就都有这个功能了。
其实用@f1进行装饰就是替换x=f1(a)和x=f1(b),让代码更简洁
五、有和无序、可变变量和不可变变量
有/无序是指是否可索引
有序对象:list、tuple、str
无序对象:dict、set
可/不可变指对象存放在地址的值是否可以被改变
可变:list、set、dict
不可变:int、float、boole、str、tuple
提示:
- 不可变类型的数据作为实参传递, 修改形参不影响实参
- 可变类型的数据作为实参传递, 修改形参会导致实参跟着变化。但是形参赋值不会改变实参[可变传的是索引,所以修改形参,实参也会变,形参赋值指的是x=y+a(y是函数的形参,a为同类型的参数)这种方式,不是x=y这种简单的引用方式]。
六、形参和实参
形参(形式参数)
在函数定义中出现的参数可以看做是一个占位符,它没有数据,只能等到函数被调用时接收传递进来的数据,所以称为形式参数,简称形参。
实参(实际参数)
函数被调用时给出的参数包含了实实在在的数据,会被函数内部的代码使用,所以称为实际参数,简称实参。
- 形参变量只有在函数被调用时才会分配内存,调用结束后,立刻释放内存,所以形参变量只有在函数内部有效,不能在函数外部使用。
- 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的数据,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参,所以应该提前用赋值、输入等办法使实参获得确定值。
- 实参和形参在数量上、类型上、顺序上必须严格一致,否则会发生“类型不匹配”的错误。当然,如果能够进行自动类型转换,或者进行了强制类型转换,那么实参类型也可以不同于形参类型。
- 函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,而不能把形参的值反向地传递给实参;换句话说,一旦完成数据的传递,实参和形参就再也没有瓜葛了,所以,在函数调用过程中,形参的值发生改变并不会影响实参。
七、dict和set
异:
set()只有key,dict()以key->value进行映射存储,dict的value可以是任何类型
同:
无序,都是key的集合,key不允许重复,并且不能为list,可以是tuple(只能用不可变类型作为key);
都是hash结构,查找的时间复杂度为O(1);
功能函数:
set():
add(key),添加key,key重复忽略
remove(key),删除key,key不存在会报错
dict():
.clear()清空
del xxx直接删除整个字典
.pop(key,x)/del xxx[key] 删除,key不存在会报错,其中pop具有第二个参数,key不存在默认返回第二个参数,要是第二个参数不存在也会报错
update()/xxx[key]=value 更新,key存在则更新value,不存在会创建一组key->value的映射
get(key,x)/xxx[key] 获取value,key不存在会报错,其中get具有第二个参数,key不存在默认返回第二个参数,要是第二个参数不存在也会报错
setdefault(key, default=None)查找key对应的value,要是key不存在会创建key,然后初始值为default
copy()浅拷贝
fromkeys(seq,x) 以seq中的元素作为key创建字典,x为初始值,没x初始值为None
has_key(key)查找key存不存在,存在返回True,不存在False
items()以列表返回可遍历的(key, value) 元组数组
keys()以列表返回一个字典所有的key
values()以列表返回字典中的所有value
popitem() 删除字典中的最后一对key和value,并将其返回
八、for 和 while
for的对象在循环的时候是不可变的(无论是dict、list、set),while可变
九、赋值、浅拷贝和深拷贝
对象的赋值就是简单的对象引用,会重新创建一个指针指向该对象的地址。
浅拷贝会创建新对象,其内容非原对象本身的引用,而是原对象内第一层对象的引用。其实就是一个指针指向地址。
深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。因此它的时间和空间开销要高。申请新地址,然后赋值。
十、队列和列表
Queue: 先进先出队列
LifoQueue: 后进先出队列
PriorityQueue: 优先级队列
deque: 双边队列
队列其实就是个list,只是不同的队列具有不同的进出队规则,只有deque具有所有的list功能,并且它还具有左右边添加和删除元素的功能。
list: 有序,元素可重复;remove(x) 没有匹配项则报错;x.extend(y)可以将x扩展开兼并y内的元素;append()只能在末尾处添加;在其它位置添加用insert(index,x);pop(i)指定索引越界则报错,默认删除最后一个;list.clear() 等价于del a[:],清空;index(x) 返回第一个匹配值,或没有匹配值报错;count(x) 不存在的值报错;.reverse()翻转;copy(),只有一层时深拷贝,嵌套为浅拷贝,嵌套时内层list保存的是地址,copy的时候是将地址copy了;浅拷贝的方法:列表生成式、for循环遍历(只对第一层深拷贝)、切片;深拷贝:deepcopy()(y=copy.deepcopy(x))
十、补丁
补丁是指在运行时通过类外部的函数对类或模块进行动态修改
功能:在不改变系统原来的代码或内置函数的情况下,来替成我们的代码
第一种:
mod_1.py
def mod1_function():
print("mod1_function")
mod_2.py
def mod2_function():
print("mod2_function")
def monkey_patch_module_2():
import sys
import mod_1
#mod_1.mod1_function() #print mod1_function
sys.modules['mod_1'] = __import__("mod_2")
import mod_1
#使用mod_2 code替换mod_1,此时再import mod_1时则是import mod_2
mod_1.mod2_function() #print mod2_function
monkey_patch_module_2()
output: mod2_function
第二种:
import mod_1
import mod_2
def mokey_patch_m_2():
mod_1.__name__ = "mod_2" #此可有可无,但是防止乱,还是加上好。
mod_1.mod1_function = mod_2.mod2_function #将mod_1的mod1_function的功能替换成mod_2的mod2_function
mokey_patch_m_2()
mod_1.mod1_function()
output: mod2_function
十一、描述器
作用: 可以控制我们访问属性、方法的行为
- 一个类实现了
__get__
(getattr,获取属性)、__set__
(setattr,设置属性)、__delete__
(删除属性)三个任意一个方法都称为描述器 - 如果一个类的类属性设置为描述器,那么它被称为此描述器的owner属主
描述器的定义划分:
- 如果一个类仅仅实现了
__get__()
方法,称为非数据描述器non-data descriptor - 如果一个类实现了
__get__()
, ___set__()
方法,称为数据描述器data descriptor
非数据描述器
参考链接:https://zhuanlan.zhihu.com/p/121459013
# 示例
class Student1:
def __init__(self):
self.course = 'Python'
print('Student1.__init__')
class Student2:
stu1 = Student1() # Student1()返回的是Student1类的实例
def __init__(self):
print('Student2.__init__')
print(Student2.stu1.course)
# 创建Student2的实例对象
stu2 = Student2()
print(stu2.stu1.course)
# 示例:引入描述器
class Stduent1:
def __init__(self):
self.course = 'Python'
print('Stduent1.__init__')
def __get__(self, instance, owner):
print('self={} instance={} owner={}'.format(self, instance, owner))
class Stduent2:
stu1 = Stduent1()
def __init__(self):
print('Stduent2.__init__')
print(Stduent2.stu1.course)
# Stduent2.stu1会访问Stduent1的实例,默认会调用__get__方法,但是__get__方法没有将实例返回,因此,Stduent2.stu1.course会报错
stu2 = Stduent2()
print(stu2.stu1.course) # 一样的报错
# 示例 引入描述器
class Stduent1:
def __init__(self):
self.course = 'Python'
print('Stduent1.__init__')
def __get__(self, instance, owner):
# 这里的self为Stduent1的实例. instance为实例, 如果是类访问,那么instance为None. owner是调用者的类
print('self={} instance={} owner={}'.format(self, instance, owner))
return self # 返回Student1的实例self
class Stduent2:
stu1 = Stduent1()
def __init__