进程以及状态
1. 进程
程序:例如xxx.py这是程序,是一个静态的
进程:一个程序运行起来后,代码+用到的资源 称之为进程,它是操作系统分配资源的基本单元。
不仅可以通过线程完成多任务,进程也是可以的
2. 进程的状态
工作中,任务数往往大于cpu的核数,即一定有一些任务正在执行,而另外一些任务在等待cpu进行执行,因此导致了有了不同的状态。
- 就绪态:运行的条件都已经满足,正在等在cpu执行
- 执行态:cpu正在执行其功能
- 等待态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态
进程的创建-multiprocessing
multiprocessing模块就是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情
1. 创建进程
说明:
- 创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动
2. 进程pid
通过os中的getpid()方法能获取到当前运行进程的id。
3. Process语法结构如下
Process([group [, target [, name [, args [, kwargs]]]]])
- target:如果传递了函数的引用,可以认为这个子进程就执行这里的代码
- args:给target指定的函数传递的参数,以元组的方式传递
- kwargs:给target指定的函数传递命名参数
- name:给进程设定一个名字,可以不设定
- group:指定进程组,大多数情况下用不到
Process创建的实例对象的常用方法:
- start():启动子进程实例(创建子进程)
- is_alive():判断进程子进程是否还在活着
- join([timeout]):是否等待子进程执行结束,或等待多少秒
- terminate():不管任务是否完成,立即终止子进程
Process创建的实例对象的常用属性:
- name:当前进程的别名,默认为Process-N,N为从1开始递增的整数
- pid:当前进程的pid(进程号)
4. 给子进程指定的函数传递参数
运行结果:
5. 进程间不共享全局变量
运行结果:
进程和线程对比
功能
- 进程,能够完成多任务,比如 在一台电脑上能够同时运行多个QQ
- 线程,能够完成多任务,比如 一个QQ中的多个聊天窗口
定义的不同
- 进程是系统进行资源分配和调度的一个独立单位.
- 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
区别
- 一个程序至少有一个进程,一个进程至少有一个线程.
- 线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。
- 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
- 线程不能够独立执行,必须依存在进程中
- 可以将进程理解为工厂中的一条流水线,而其中的线程就是这个流水线上的工人
优缺点
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。
开闭原则:
在不修改原函数及其调用方式的情况下对原函数功能进行扩展
对代码的修改是封闭
不能修改被装饰的函数的源代码
不能修改被装饰的函数的调用方式
用函数的方式设想一下游戏里用枪的场景
装饰器(python里面的动态代理)
本质: 是一个闭包
组成: 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器
存在的意义: 在不破坏原有函数和原有函数调用的基础上,给函数添加新的功能
通用装饰器写法:
上边的场景用装饰器修改后
一个简单的装饰器实现
带有一个或多个参数的装饰器
动态传递一个或多个参数给装饰器
装饰器的返回值
多个装饰器同用一个函数
python列表类型
分类: python
列表类型简介
列表类型是一个容器,它里面可以存放任意数量、任意类型的数据。
例如下面的几个列表中,有存储数值的、字符串的、内嵌列表的。不仅如此,还可以存储其他任意类型。
python中的列表是一个序列,其内元素是按索引顺序进行存储的,可以进行索引取值、切片等操作。
列表结构
列表是可变对象,可以原处修改列表中的元素而不会让列表有任何元数据的变动。
从id的变动上看,修改列表的第一个元素时,列表本身的id没有改变,但列表的第一个元素的id已经改变。
看了下面列表的内存图示就很容易理解了。
上面是L = ["a", "b", "c"]列表的图示。变量名L存储了列表的内存地址,列表内部包含了类型声明、列表长度等元数据,还保存了属于列表的3个元素的内存地址。需要注意的是,列表元素不是直接存在列表范围内的,而是以地址的形式保存在列表中。
所以,修改列表中的元素时,新建一个元素"aa"(之所以新建,是因为字符串是不可变类型),列表本身并没有改变,只是将列表中指向第一个元素的地址改为新数据"aa"的地址。
因为修改列表数据不会改变列表本身属性,这种行为称为"原处修改"。
所以,列表有几个主要的的特性:
- 列表中可以存放、嵌套任意类型的数据
- 列表中存放的是元素的引用,也就是各元素的地址,因此是列表可变对象
- 列表是可变序列。所以各元素是有位置顺序的,可以通过索引取值,可以通过切片取子列表
构造列表
有两种常用的构造列表方式:
- 使用中括号[]
- 使用list()构造方法
使用(中)括号构建列表时,列表的元素可以跨行书写,这是python语法中各种括号类型的特性。
例如:
上面range()用于生成一系列数值,就像Linux下的seq命令一样。但是range()不会直接将数据生成出来,它返回的是一个可迭代对象,表示可以一个一个地生成这些数据,所以这里使用list()将range()的数据全部生成出来并形成列表。
中括号方式构造列表有一个很重要的特性:列表解析,很多地方也称为"列表推到"。例如:
list()是直接将所给定的数据一次性全部构造出来,直接在内存中存放整个列表对象。列表推导方式构造列表比list()要快,且性能差距还挺大的。
列表基本操作
列表支持+ *符号操作:
可以通过+=的方式进行二元赋值:
L1 += L2的赋值方式对于可变序列来说(比如这里的列表),性能要好于L1 = L1 + L2的方式。前者直接在L1的原始地址内进行修改,后者新创建一个列表对象并拷贝原始L1列表。但实际上,性能的差距是微乎其微的,前面说过列表中保存的是元素的引用,所以拷贝也仅仅只是拷贝一些引用,而非实际数据对象。
列表是序列,序列类型的每个元素都是按索引位置进行存放的,所以可以通过索引的方式取得列表元素:
当然,也可以按索引的方式给给定元素赋值,从而修改列表:
通过赋值方式修改列表元素时,不仅可以单元素赋值修改,还可以多元素切片赋值。
上面对列表的切片进行赋值时,实际上是先取得这些元素,删除它们,并插入新数据的过程。所以上面是先删除[1:3]的元素,再在这个位置处插入新的列表数据。
所以,如果将某个切片赋值为空列表,则表示直接删除这个元素或这段范围的元素。
但如果是将空列表赋值给单个索引元素,这不是表示删除元素,而是表示将空列表作为元素嵌套在列表中。
这两种列表赋值的区别,在理解了前文所说的列表结构之后应该不难理顺。
列表其它操作
列表是一种序列,所以关于序列的操作,列表都可以用,比如索引、切片、各种序列可用的函数(比如append()、extend()、remove()、del、copy()、pop()、reverse())等。详细内容参见:python序列操作
除了这些序列通用操作,列表还有一个专门的列表方法sort,用于给列表排序。
列表排序sort()和sorted()
sort()是列表类型的方法,只适用于列表;sorted()是内置函数,支持各种容器类型。它们都可以排序,且用法类似,但sort()是在原地排序的,不会返回排序后的列表,而sorted()是返回新的排序列表。
本文仅简单介绍排序用法。
例如列表L:
使用sort()和sorted()排序L,注意sort()是对L直接原地排序的,不是通过返回值来体现排序结果的,所以无需赋值给变量。而sorted()则是返回排序后的新结果,需要赋值给变量才能保存排序结果。
不难发现,sort()和sorted()默认都是升序排序的(A<B<...<Z<a<b<...<z)。它们都可以指定参数reverse=True来表示顺序反转,也就是默认得到降序:
在python 3.x中,sort()和sorted()不允许对包含不同数据类型的列表进行排序。也就是说,如果列表中既有数值,又有字符串,则排序操作报错。
sort()和sorted()的另一个参数是key,它默认为key=None,该参数用来指定自定义的排序函数,从而实现自己需要的排序规则。
例如,上面的列表不再按照默认的字符顺序排序,而是想要按照字符串的长度进行排序。所以,自定义这个排序函数:
然后通过指定key = sortByLen的参数方式调用sort()或sorted(),在此期间还可以指定reverse = True:
再例如,按照列表每个元素的第二个字符来排序。
更多的排序方式,参见:sorting HOWTO。比如指定两个排序依据,一个按字符串长度升序排,长度相同的按第2个字符降序排。用法其实很简单,不过稍占篇幅,所以本文不解释了。
列表迭代和解析
列表是一个序列,可以使用in测试,使用for迭代。
例如:
再说列表解析,它指的是对序列中(如这里的列表)的每一项元素应用一个表达式,并将表达式计算后的结果作为新的序列元素(如这里的列表)。
通俗一点的解释,以列表序列为例,首先取列表各元素,对每次取的元素都做一番操作,并将操作后得到的结果放进一个新的列表中。
因为解析操作是一个元素一个元素追加到新列表中的,所以也称为"列表推导",表示根据元素推导列表。
最简单的,将字符串序列中的各字符取出来放进列表中:
这里是列表解析,因为它外面使用的是中括号[],表示将操作后的元素放进新的列表中。可以将中括号替换成大括号,就变成了集合解析,甚至字典解析。但注意,没有直接的元组解析,因为元组的括号是特殊的,它会被认为是表达式的优先级包围括号,而不是元组构造符号。
取出元素对各元素做一番操作:
解析操作和for息息相关,且都能改写成for循环。例如,下面两个语句得到的结果是一致的:
但是解析操作的性能比for循环要更好,正符合越简单越高效的理念。
学过其他语言的人,估计已经想到了,解析过程中对各元素的表达式操作类似于回调函数。其实在python中有一个专门的map()函数,它以第一个参数作为回调函数,并返回一个可迭代对象。也就是说,也能达到和解析一样的结果。例如: