Python 【基础常识概念】

  • 2020 年 3 月 12 日
  • 筆記

深浅拷贝

浅copy与deepcopy

    • 浅copy: 不管多么复杂的数据结构,浅拷贝都只会copy一层
    • deepcopy : 深拷贝会完全复制原变量相关的所有数据,在内存中生成一套完全一样的内容,我们对这两个变量中任意一个修改都不会影响其他变量

深浅拷贝之间的区别?

  • 对象的赋值就是简单的引用,a = [1,2,3], b=a, 在上述情况下,a和b是一样的,他们指向同一片内存,b不过是a的别名,是引用,我们可以使用b is a 去判断,返回True,表名他们地址相同内容也相同,也可以使用id()函数来查看.看两个列表地址是否相同.
  • 深拷贝就是将一个对象拷贝到另一个对象中,这意味着如果你对一个对象的拷贝做出改变时,不会影响原对象。在Python中,我们使用函数deepcopy()执行深拷贝
import copy  b=copy.deepcopy(a)

avatar

  • 而浅拷贝则是将一个对象的引用拷贝到另一个对象上,所以如果我们在拷贝中改动,会影响到原对象。我们使用函数function()执行浅拷贝、切片操作是浅拷贝
b=copy.copy(a)

可变与不可变

  • 可变类型(mutable):列表,字典
  • 不可变类型(unmutable):数字,字符串,元组
  • 这里的可变不可变,是指内存中的那块内容(value)是否可以被改变。如果是不可变类型,在对对象本身操作的时候,必须在内存中新申请一块区域(因为老区域#不可变#)。如果是可变类型,对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的address会保持不变,但区域会变长或者变短。
copy.copy()  # 浅拷贝  copy.deepcopy()  # 深拷贝

  • 浅拷贝是新创建了一个跟原对象一样的类型,但是其内容是对原对象元素的引用。这个拷贝的对象本身是新的,但内容不是。拷贝序列类型对象(列表元组)时,默认是浅拷贝。

 

垃圾回收机制

引计数

  • 原理
    • 当一个对象的引用被创建或者复制时,对象的引用计数加1;当一个对象的引用被销毁时,对象的引用计数减1.
    • 当对象的引用计数减少为0时,就意味着对象已经再没有被使用了,可以将其内存释放掉。
  • 优点
    • 引用计数有一个很大的优点,即实时性,任何内存,一旦没有指向它的引用,就会被立即回收,而其他的垃圾收集技术必须在某种特殊条件下才能进行无效内存的回收。
  • 缺点
    • 引用计数机制所带来的维护引用计数的额外操作与Python运行中所进行的内存分配和释放,引用赋值的次数是成正比的,
    • 显然比其它那些垃圾收集技术所带来的额外操作只是与待回收的内存数量有关的效率要低。
    • 同时,因为对象之间相互引用,每个对象的引用都不会为0,所以这些对象所占用的内存始终都不会被释放掉。

标记-清除

  • 它分为两个阶段:第一阶段是标记阶段,GC会把所有的活动对象打上标记,第二阶段是把那些没有标记的对象非活动对象进行回收。
  • 对象之间通过引用(指针)连在一起,构成一个有向图
  • 从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。
  • 根对象就是全局变量、调用栈、寄存器。

             

  • 在上图中,可以从程序变量直接访问块1,并且可以间接访问块2和3,程序无法访问块4和5
  • 第一步将标记块1,并记住块2和3以供稍后处理。
  • 第二步将标记块2,第三步将标记块3,但不记得块2,因为它已被标记。
  • 扫描阶段将忽略块1,2和3,因为它们已被标记,但会回收块4和5。

分代回收

  • 分代回收是建立在标记清除技术基础之上的,是一种以空间换时间的操作方式。
  • Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代)
  • 他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。
  • 新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发
  • 把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推
  • 老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。

 

三次握手四次挥手

TCP协议

  • 可靠传输, TCP数据包没有长度限制, 理论上可以无限长, 但是为了保证网络的效率,
  • 通常TCP数据包的长度不会超过IP数据包的长度, 以确保单个TCP数据包不必再分割

UDP协议

  • 不可靠传输, “”报头””部分一共只有8个字节, 总长度不超过65535字节, 正好放进一个IP数据包

三次握手

  • 置位概念: 根据TCP的包头字段, 存在3个重要的表示ACK, SYN, FIN
  • ACK: 表示验证字段
  • SYN: 位数置1, 表示建立TCP连接
  • FIN:位数置1,表示断开TCP连接

 三次握手过程说明

  1. 由客户端发送建立TCP连接的请求报文, 其中报文中包含seq序列号, 是由发送端随机生成的,并且将报文中的SYN字段置为1, 表示需要建立TCP连接 (SYN=1, seq=x, x为随机生成数值)
  2. 由服务端回复客户端发送的TCP连接请求报文, 其中包含seq序列号, 是由回复端随机生成的, 并且将SYN置为1,而且会产生ACK字段, ACK字段数值是在客户端发送过来的序列号seq的基础上加1进行回复,以便客户端收到信息时,知晓自己的TCP建立请求已得到验证 (SYN=1, ACK=x+1, seq=y, y为随机生成数值) 这里的ACK加1可以理解为是确认和谁建立连接
  3. 客户端收到服务端发送的TCP建立验证请求后, 会使自己的序列号加1表示, 并且再次回复ACK验证请求, 在服务端发过来的seq上加1进行回复(SYN=1, ACK=y+1, seq=x+1)

四次挥手 

四次挥手过程说明

  1. 客户端发送断开TCP连接请求的报文, 其中报文中包含seq序列号, 是由发送端随机生成的, 并且还将报文中的FIN字段置为1, 表示需要断开TCP连接 (FIN=1, seq=x, x由客户端随机生成)
  2. 服务端会回复客户端发送的TCP断开请求报文, 其包含seq序列号, 是由回复端随机生成的,而且会产生ACK字段, ACK字段数值是在客户端发过来的seq序列号基础上加1进行回复,以便客户端收到信息时, 知晓自己的TCP断开请求已经得到验证 (FIN=1, ACK=x+1, seq=y, y由服务端随机生成)
  3. 服务端在回复完客户端的TCP断开请求后,不会马上进行TCP连接的断开,服务端会先确保断开前,所有传输到A的数据是否已经传输完毕,一旦确认传输数据完毕,就会将回复报文的FIN字段置1,并且产生随机seq序列号。(FIN=1,ACK=x+1,seq=z,z由服务端随机生成)
  4. 客户端收到服务端的TCP断开请求后,会回复服务端的断开请求,包含随机生成的seq字段和ACK字段,ACK字段会在服务端的TCP断开请求的seq基础上加1,从而完成服务端请求的验证回复。(FIN=1,ACK=z+1,seq=h,h为客户端随机生成)至此TCP断开的4次挥手过程完毕

11种状态

  1. 一开始,建立连接之前服务器和客户端的状态都为CLOSED; 
  2. 服务器创建socket后开始监听,变为LISTEN状态; 
  3. 客户端请求建立连接,向服务器发送SYN报文,客户端的状态变味SYN_SENT; 
  4. 服务器收到客户端的报文后向客户端发送ACK和SYN报文,此时服务器的状态变为SYN_RCVD; 
  5. 然后,客户端收到ACK、SYN,就向服务器发送ACK,客户端状态变为ESTABLISHED; 
  6. 服务器端收到客户端的ACK后变为ESTABLISHED。此时3次握手完成,连接建立!

由于TCP连接是全双工的,断开连接会比建立连接麻烦一点点。 

  1. 客户端先向服务器发送FIN报文,请求断开连接,其状态变为FIN_WAIT1; 
  2. 服务器收到FIN后向客户端发送ACK,服务器的状态围边CLOSE_WAIT; 
  3. 客户端收到ACK后就进入FIN_WAIT2状态,此时连接已经断开了一半了如果服务器还有数据要发送给客户端,就会继续送; 
  4. 直到发完数据,就会发送FIN报文,此时服务器进入LAST_ACK状态; 
  5. 客户端收到服务器的FIN后,马上发送ACK给服务器,此时客户端进入TIME_WAIT状态; 
  6. 再过了2MSL长的时间后进入CLOSED状态。服务器收到客户端的ACK就进入CLOSED状态。 
  7. 至此,还有一个状态没有出来:CLOSING状态。 

CLOSING状态表示:

  • 客户端发送了FIN,但是没有收到服务器的ACK,却收到了服务器的FIN,这种情况发生在服务器发送的ACK丢包的时候,因为网络传输有时会有意外。

LISTEN  --------------- # 等待从任何远端TCP 和端口的连接请求。    SYN_SENT  ----------- # 发送完一个连接请求后等待一个匹配的连接请求。    SYN_RECEIVED  ------ # 发送连接请求并且接收到匹配的连接请求以后等待连接请求确认。    ESTABLISHED  -------- # 表示一个打开的连接,接收到的数据可以被投递给用户。连接的数据传输阶段的正常状态。    FIN_WAIT_1  ----------- # 等待远端TCP 的连接终止请求,或者等待之前发送的连接终止请求的确认。    FIN_WAIT_2  ---------- #  等待远端TCP 的连接终止请求。    CLOSE_WAIT  --------- #  等待本地用户的连接终止请求。    CLOSING  ------------- #   等待远端TCP 的连接终止请求确认。    LAST_ACK  ------------ #  等待先前发送给远端TCP 的连接终止请求的确认(包括它字节的连接终止请求的确认)    TIME_WAIT  ----------- #  等待足够的时间过去以确保远端TCP 接收到它的连接终止请求的确认。    CLOSED  --------------- # 不在连接状态(这是为方便描述假想的状态,实际不存在)

11种状态解析

TIME_WAIT 两个存在的理由:

  • 可靠的实现tcp全双工连接的终止;
  • 允许老的重复分节在网络中消逝。

 

高阶函数

  • map函数
    • 一般情况map()函数接收两个参数,一个函数(该函数接收一个参数),一个序列,将传入的函数依次作用到序列的每个元素,并返回一个新的Iterator(迭代器)。 例如有这样一个list:[‘pYthon’, ‘jaVa’, ‘kOtlin’],现在要把list中每个元素首字母改为大写,其它的改为小写,可以这样操作:

>>> def f(s):  ...    return s.title()  ...  >>> l = map(f, ['pYthon', 'jaVa', 'kOtlin'])  >>> list(l)  ['Python', 'Java', 'Kotlin']

map

lists= [11,22,33,44,55]  ret = map(lambda x:x if x % 2 != 0 else x + 100,lists)  print(list(ret))  # 运行结果: [11, 122, 33, 144, 55]

利用map,lambda表达式将所有偶数元素加100

lists= [11,22,33,44,55]  def add(num):      if num%2 == 0:          return num      else:          return num + 100  rs = map(add, lists)

自定义函数代替lambda实现相同功能

  • reduce函数
    • 和map()用法类似,reduce把传入的函数作用在一个序列上,但传入的函数需要接收两个参数,传入函数的计算结果继续和序列的下一个元素做累积计算。

# 例如有一个list,里边的元素都是字符串,要把它拼接成一个字符串:    >>> from functools import reduce  >>> def f(x, y):  ...    return x + y  ...  >>> reduce(f, ['ab', 'c', 'de', 'f'])  'abcdef'

reduce

from functools import reduce  def f(x, y):   return x + y    print(reduce(f, [1, 3, 5, 7, 9]))  # 25  # 1、先计算头两个元素:f(1, 3),结果为4;  # 2、再把结果和第3个元素计算:f(4, 5),结果为9;  # 3、再把结果和第4个元素计算:f(9, 7),结果为16;  # 4、再把结果和第5个元素计算:f(16, 9),结果为25;  # 5、由于没有更多的元素了,计算结束,返回结果25。    print( reduce(lambda x, y: x + y, [1, 3, 5, 7, 9])  )  # 25

使用reduce进行求和运算

'''使用reduce将字符串反转'''  s = 'Hello World'  from functools import reduce    result = reduce(lambda x,y:y+x,s)  # 1、第一次:x=H,y=e  => y+x = eH  # 2、第二次:x=l,y=eH  => y+x = leH  # 3、第三次:x=l,y=leH  => y+x = lleH  print( result )      # dlroW olleH

使用reduce将字符串反转

  • filter函数
    • filter()同样接收一个函数和一个序列,然后把传入的函数依次作用于序列的每个元素,如果传入的函数返回true则保留元素,否则丢弃,最终返回一个Iterator。

例如一个list中元素有纯字母、纯数字、字母数字组合的,我们要保留纯字母的:    >>> def f(s):  ...    return s.isalpha()  ...  >>> l = filter(f, ['abc', 'xyz', '123kg', '666'])  >>> list(l)  ['abc', 'xyz']

filter

  • sorted函数
    • sorted()函数就是用来排序的,同时可以自己定义排序的规则。

>>> sorted([6, -2, 4, -1])  [-2, -1, 4, 6]    >>> sorted([6, -2, 4, -1], key=abs)  [-1, -2, 4, 6]    >>> sorted([6, -2, 4, -1], key=abs, reverse=True)  [6, 4, -2, -1]    >>> sorted(['Windows', 'iOS', 'Android'])  ['Android', 'Windows', 'iOS']    >>> d = [('Tom', 170), ('Jim', 175), ('Andy', 168), ('Bob', 185)]  >>> def by_height(t):  ...     return t[1]  ...  >>> sorted(d, key=by_height)   [('Andy', 168), ('Tom', 170), ('Jim', 175),  ('Bob', 185)]

sorted

sorted和sort区别

  • sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
  • sort 是对已经存在的列表进行操作,无返回值,而 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。

sorted使用

  • sorted 语法:sorted(iterable, cmp=None, key=None, reverse=False)
    • iterable — 可迭代对象。
    • cmp — 比较的函数
    • key — 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
    • reverse — 排序规则,reverse = True 降序 , reverse = False 升序(默认)。

students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]  print( sorted(students, key=lambda s: s[2], reverse=False) )    # 按年龄排序  # 结果:[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

sorted对列表排序

d = {'k1':1, 'k3': 3, 'k2':2}  # d.items() = [('k1', 1), ('k3', 3), ('k2', 2)]  a = sorted(d.items(), key=lambda x: x[1])  print(a)            # [('k1', 1), ('k2', 2), ('k3', 3)]

sorted对字典排序

 

is和==区别

  • is不仅数据一样内存地址也一样
  • == 只判断数据和数据类型一样即可
  • 首先要知道Python中对象包含的三个基本要素,分别是:id(身份标识)、type(数据类型)和value(值)。
  • is和==都是对对象进行比较判断作用的,但对对象比较判断的内容并不相同。下面来看看具体区别在哪。
  • ==比较操作符和is同一性运算符区别
  • ==是python标准操作符中的比较操作符,用来比较判断两个对象的value(值)是否相等,例如下面两个字符串间的比较:

>>> a = 'cheesezh'  >>> b = 'cheesezh'  >>> a == b  True

示例1

  • is也被叫做同一性运算符,这个运算符比较判断的是对象间的唯一身份标识,也就是id是否相同。通过对下面几个list间的比较,你就会明白is同一性运算符的工作原理:

>>> x = y = [4,5,6]  >>> z = [4,5,6]  >>> x == y  True  >>> x == z  True  >>> x is y  True  >>> x is z  False  >>>  >>> print id(x)  3075326572  >>> print id(y)  3075326572  >>> print id(z)  3075328140

示例2

  • 前二个例子都是True,这什么最后一个是False呢?x、y和z的值是相同的,所以前两个是True没有问题。至于最后一个为什么是False,看看三个对象的id分别是什么就会明白了。

下面再来看一个例子,例3中同一类型下的a和b的(a==b)都是为True,而(a is b)则不然。

>>> a = 1 #a和b为数值类型  >>> b = 1  >>> a is b  True  >>> id(a)  14318944  >>> id(b)  14318944  >>> a = 'cheesezh' #a和b为字符串类型  >>> b = 'cheesezh'  >>> a is b  True  >>> id(a)  42111872  >>> id(b)  42111872  >>> a = (1,2,3) #a和b为元组类型  >>> b = (1,2,3)  >>> a is b  False  >>> id(a)  15001280  >>> id(b)  14790408  >>> a = [1,2,3] #a和b为list类型  >>> b = [1,2,3]  >>> a is b  False  >>> id(a)  42091624  >>> id(b)  42082016  >>> a = {'cheese':1,'zh':2} #a和b为dict类型  >>> b = {'cheese':1,'zh':2}  >>> a is b  False  >>> id(a)  42101616  >>> id(b)  42098736  >>> a = set([1,2,3])#a和b为set类型  >>> b = set([1,2,3])  >>> a is b  False  >>> id(a)  14819976  >>> id(b)  14822256

示例3

通过例3可看出,只有数值型和字符串型的情况下,a is b才为True,当a和b是tuple,list,dict或set型时,a is b为False。

 

读写文件

open函数用来打开文件

  • open(name[, mode[, buffering]])  打开文件可传的参数
    • open函数使用一个文件名作为唯一的强制参数,然后返回一个文件对象。
    • 模式(mode)和缓冲(buffering)参数都是可选的
  • 打开文件的模式有
    • r — 只读模式(默认)。
    • w — 只写模式。【不可读;不存在则创建;存在则删除内容;】
    • a — 追加模式。【可读; 不存在则创建;存在则只追加内容;】
    • 注:  “+” 表示可以同时读写某个文件
    • w — 只写模式。【不可读;不存在则创建;存在则删除内容;】
    • w+ — 写读
    • a+ — 同a
  • with语句
    • 作用:将打开文件写在with中当对文件操作完成后with语句会自动帮关闭文件,避免忘记写f.close()

with open("data1.txt",'r',encoding = 'utf-8') as f:      for line in f:          print(line)

with读文件

三种读操作比较

  • readline()每次读取一行,当前位置移到下一行
  • readlines()读取整个文件所有行,保存在一个列表(list)变量中,每行作为一个元素
  • read(size)从文件当前位置起读取size个字节,如果不加size会默认一次性读取整个文件(适用于读取小文件)

#1. read()一次读取所有内容  '''aaa111  bbb222'''  f = open(r"data.txt")  print(f.read())  f.close()    #2. readline(),每次只读取一行,光标下移  '''  0: aaa111    1: bbb222  '''  f = open(r"data.txt")  for i in range(2):      print(str(i) + ": " + f.readline(),)    #3. 一次读取所有,每行作为列表的一个值  '''['aaa111n', 'bbb222n']'''  f = open(r"data.txt")  print(f.readlines())

三种读操作举例

读取大文件正确方式

  • 我们使用了一个 while 循环来读取文件内容,每次最多读取 8kb 大小
  • 这样可以避免之前需要拼接一个巨大字符串的过程,把内存占用降低非常多。 

#!/usr/bin/python  # -*- coding: utf-8 -*-  def read_big_file_v(fname):      block_size = 1024 * 8      with open(fname,encoding="utf8") as fp:          while True:              chunk = fp.read(block_size)              # 当文件没有更多内容时,read 调用将会返回空字符串 ''              if not chunk:                  break              print(chunk)  path = r'C:aaalutingedc-backendtttt.py'  read_big_file_v(path)

python读取大文件

 使用read()读文件

  • read(n)读取指定长度的文件

f = open(r"somefile.txt")  print(f.read(7))        # Welcome        先读出 7 个字符  print(f.read(4))        #‘ to ‘                接着上次读出 4 个字符  f.close()

read读取指定长度字符串

  • seek(offset[, whence])          随机访问
  • 作用:从文件指定位置读取或写入

f = open(r"somefile.txt", "w")  f.write("01234567890123456789")  f.seek(5)  f.write("Hello, World!")  f.close()  f = open(r"somefile.txt")  print(f.read())                 # 01234Hello, World!89

从指定位置写入

  • tell                                        返回当前读取到文件的位置下标

f = open(r"somefile.txt")  f.read(1)  f.read(2)  print(f.tell())             # 3     3就是读取到文件的第三个字符

返回读取位置下标

readline()读文件

  • 作用:readline 的用法,速度是fileinput的3倍左右,每秒3-4万行,好处是 一行行读 ,不占内存,适合处理比较大的文件,比如超过内存大小的文件

f1 = open('test02.py','r')  f2 = open('test.txt','w')  while True:      line = f1.readline()      if not line:          break      f2.write(line)  f1.close()  f2.close()

readline读取大文件

readlines()读文件

  • 作用:readlines会把文件都读入内存,速度大大增加,但是木有这么大内存,那就只能乖乖的用readline

f1=open("readline.txt","r")  for line in f1.readlines():      print(line)

readlines读文件

将data1.txt中内容读取并写入到data2.txt中

f1 = open('data1.txt','r')  f2 = open('data2.txt','w')    for line in f1:      f2.write(line)    f1.close()  f2.close()

将data1.txt内容读取到data2.txt

使用eval()方法将文件读取成字典 

f = open('data1.txt')  f1 = (f.read())  data = eval(f1)  f.close()  print(data)         # 运行结果: {'k2': 'v2', 'k3': 'v3', 'k1': 'v1'}

将文件读取成字典

将文件内容读取成列表

lock = []  f = open("password.txt")  for name in f.readlines():      lock.append(name.strip('n'))  print(lock)  运行结果: ['aaa 111', 'bbb 222', 'ccc 333']

内容读取成列表

 

闭包

闭包概念    

  • 在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用,这样就构成了一个闭包
  • 一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。
  • 但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

闭包特点    

  • 必须有一个内嵌函数
  • 内嵌函数必须引用外部函数中的变量
  • 外部函数的返回值必须是内嵌函数

#闭包函数的实例  def outer( a ):      b = 10      def inner():          # 在内函数中 用到了外函数的临时变量          print(a+b)        # 外函数的返回值是内函数的引用      return inner    if __name__ == '__main__':      demo = outer(5)      demo()    # 15    # 在这里我们调用外函数传入参数5  # 此时外函数两个临时变量 a是5 b是10 ,并创建了内函数,然后把内函数的引用返回存给了demo  # 外函数结束的时候发现内部函数将会用到自己的临时变量,这两个临时变量就不会释放,会绑定给这个内部函数  # 我们调用内部函数,看一看内部函数是不是能使用外部函数的临时变量  # demo存了外函数的返回值,也就是inner函数的引用,这里相当于执行inner函数

闭包实例

闭包中内函数修改外函数局部变量 

  • 在基本的python语法当中,一个函数可以随意读取全局数据,但是要修改全局数据的时候有两种方法:
    • global 声明全局变量
    • 全局变量是可变类型数据的时候可以修改
  • 在闭包情况下使用下面两种方法修改
    • 在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。
    • 在python2中,没有nonlocal这个关键字,我们可以把闭包变量改成可变类型数据进行修改,比如列表。

#修改闭包变量的实例  def outer( a ):      b = 10            # a和b都是闭包变量      c = [a]           # 这里对应修改闭包变量的方法2      def inner():          # 方法一: nonlocal关键字声明(python3)          nonlocal  b          b+=1            # 方法二: 把闭包变量修改成可变数据类型 比如列表(python2)          c[0] += 1          print(c[0])          print(b)      return inner         # 外函数的返回值是内函数的引用    if __name__ == '__main__':      demo = outer(5)      demo()             # 6  11

闭包中内函数修改外函数局部变量

 

上下文管理with

什么是with语句

  • with是一种上下文管理协议,目的在于从流程图中把 try,except 和finally 关键字和资源分配释放相关代码统统去掉,简化try….except….finlally的处理流程。
  • 所以使用with处理的对象必须有enter()和exit()这两个方法
  • with通过enter方法初始化(enter方法在语句体执行之前进入运行)
  • 然后在exit中做善后以及处理异常(exit()方法在语句体执行完毕退出后运行)

with语句使用场景

  • with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源
  • 比如文件使用后自动关闭、线程中锁的自动获取和释放等。

with处理文件操作的实例

with open('/etc/passwd') as f:      for line in f:          print(line)    # 这段代码的作用:打开一个文件,如果一切正常,把文件对象赋值给f,然后用迭代器遍历文件中每一行,当完成时,关闭文件;    # 而无论在这段代码的任何地方,如果发生异常,此时文件仍会被关闭。

 

字符编码

各种编码由来

  • ASCII : 不支持中文(一个字母一个字节:a/b/c)
  • GBK : 是中国的中文字符,其包含了简体中文和繁体中文的字符
  • Unicode : 万国编码(Unicode 包含GBK)

Unicode(每个字母需要用两个字节:a/b/c)

  • 存储所有字符串都用连个字节
  • Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码
  • 规定所有的字符和符号最少由 16 位来表示(2个字节),即:2 **16 = 65536
  • 这里还有个问题:使用的字节增加了,那么造成的直接影响就是使用的空间就直接翻倍了

Utf-8 : 可变长码, 是Unicode 的扩展集

  • UTF-8编码:是对Unicode编码的压缩和优化,他不再使用最少使用2个字节,而是将所有的字符和符号进行分类
  • ascii码中的内容用1个字节保存、欧洲的字符用2个字节保存,东亚的字符用3个字节保存…
  • 存一个a字母用一个字节,存一个中文用三个字节

 

python2与python3的区别 

  • Python2默认 编码方式为ASCII, Python3 默认编码方式为UTF-8(是Unicode 的扩展集)
  • python2中字符串有str和unicode两种类型, python3 中字符串有str和字节(bytes) 两种类型
  • python3中不再支持u中文的语法格式
  • 异常处理 Python2中try:…except Exception, e:…,在Python3中改为了try:…except Exception as e:…
  • Python3中不再使用xrange方法,只有range方法。
  • range在Python2中返回列表,而在Python3中返回range可迭代对象。
  • 在Python2中有两个不等运算符!=和<>,在Python3中去掉了<>,只有!=符号表示不等
  • 在Python2中双反引号`可以替代repr函数,在Python3中去掉了双反引号的表是方法,只能用repr`方法。
  • 在Python2中long是比int取值范围更大的整数,Python3中取消了long类型,int的取值范围扩大到之前的long类型范围。
  • 列表推导 不再支持[n for n in a,b]语法,改为[n for n in (a,b)]或[n for n in [a,b]]
  • python 2 中通过input输入的类型是int,只有通过raw_input()输入的类型才是str。
  • python 3中通过input输入的类型都是str,去掉了row_input()方法。
  • python2和python3中编码转换

    • 在python3中字符串默认是unicode所以不需要decode(),直接encode成想要转换的编码如gb2312
    • 在python2中默认是ASCII编码,必须先转换成Unicode,Unicode 可以作为各种编码的转换的中转站

 

常用模块

re模块(一)

  • 常用正则表达式符号
    • 通配符( . 
      • 作用:点(.)可以匹配除换行符以外的任意一个字符串
      • 例如:‘.ython’ 可以匹配‘aython’ ‘bython’ 等等,但只能匹配一个字符串
  • 转义字符(  )
    • 作用:可以将其他有特殊意义的字符串以原本意思表示
    • 例如:‘python.org’ 因为字符串中有一个特殊意义的字符串(.)所以如果想将其按照普通意义就必须使用这样表示:  ‘python.org’ 这样就只会匹配‘python.org’ 了
    • 注:如果想对反斜线()自身转义可以使用双反斜线(\)这样就表示 ’’
  • 字符集
    • 作用:使用中括号来括住字符串来创建字符集,字符集可匹配他包括的任意字串
      • ‘[pj]ython’ 只能够匹配‘python’  ‘jython’
      • ‘[a-z]’ 能够(按字母顺序)匹配a-z任意一个字符
      • ‘[a-zA-Z0-9]’ 能匹配任意一个大小写字母和数字    
      • ‘[^abc]’ 可以匹配任意除a,b和c 之外的字符串
  • 管道符
    • 作用:一次性匹配多个字符串
    • 例如:’python|perl’ 可以匹配字符串‘python’ 和 ‘perl’
  • 可选项和重复子模式(在子模式后面加上问号?)
    • 作用:在子模式后面加上问号,他就变成可选项,出现或者不出现在匹配字符串中都是合法的
    • 例如:r’(aa)?(bb)?ccddee’ 只能匹配下面几种情况
      • ‘aabbccddee’
      • ‘aaccddee’
      • ‘bbccddee’
      • ‘ccddee’
  • 字符串的开始和结尾
    • ‘w+’ 匹配以w开通的字符串
    • ‘^http’ 匹配以’http’ 开头的字符串
    • ‘ $com’ 匹配以‘com’结尾的字符串
  • 最常用的匹配方法
    • d     匹配任何十进制数;它相当于类 [0-9]。
    • D     匹配任何非数字字符;它相当于类 [^0-9]。
    • s     匹配任何空白字符;它相当于类 [ fv]。
    • S     匹配任何非空白字符;它相当于类 [^ fv]。
    • w     匹配任何字母数字字符;它相当于类 [a-zA-Z0-9_]。
    • W     匹配任何非字母数字字符;它相当于类 [^a-zA-Z0-9_]。
    • w*    匹配所有字母字符
    • w+    至少匹配一个字符

'.'         # 默认匹配除n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行   2 '^'         # 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","nabcneee",flags=re.MULTILINE)   3 '$'         # 匹配字符结尾,或e.search("foo$","bfoonsdfsf",flags=re.MULTILINE).group()也可以   4 '*'        #  匹配*号前的字符0次或多次,re.findall("ab*","cabb3abcbbac")  结果为['abb', 'ab', 'a']   5 '+'         # 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb']   6 '?'         # 匹配前一个字符1次或0次   7 '{m}'       # 匹配前一个字符m次   8 '{n,m}'     # 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb']   9 '|'         # 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC'  10 '(...)'     # 分组匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 结果 abcabca456c  11  12 'A'       # 只从字符开头匹配,re.search("Aabc","alexabc") 是匹配不到的  13 'Z'       # 匹配字符结尾,同$  14 'd'       # 匹配数字0-9  15 'D'       # 匹配非数字  16 'w'       # 匹配[A-Za-z0-9]  17 'W'       # 匹配非[A-Za-z0-9]  18 's'        # 匹配空白字符、t、n、r , re.search("s+","abtc1n3").group() 结果 't'  19 b           # 匹配一个单词边界,也就是指单词和空格间的位置,如,“erb”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”  20 B          #  匹配非单词边界。“erB”能匹配“verb”中的“er”,但不能匹配“never”中的“er”

re模块更详细表达式符号

函数                                          描述    compile(pattern[, flags])         # 根据正则表达式字符串创建模式对象    search(pattern, string[, flags]) # 在字符串中寻找模式    match(pattern, 常用模块[, flags]) # 在字符串的开始处匹配模式    split(pattern, string[, maxsplit=0]) # 根据模式的匹配项来分割字符串    findall(pattern, string)     # 列出字符串中模式的所有匹配项并以列表返回    sub(pat, repl, string[, count=0])     # 将字符串中所有pat的匹配项用repl替换  escape(string)     # 将字符串中所有特殊正则表达式字符转义 

re.compile(pattern[, flags])

  • 把一个正则表达式pattern编译成正则对象,以便可以用正则对象的match和search方法
  • 用了re.compile以后,正则对象会得到保留,这样在需要多次运用这个正则对象的时候,效率会有较大的提升

import re  mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')  ret = re.match(mobile_re,'18538762511')  print(ret)            # <_sre.SRE_Match object; span=(0, 11), match='18538652511'>

re.compile使用

search(pattern, string[, flags]) 和 match(pattern, string[, flags])

  • match :只从字符串的开始与正则表达式匹配,匹配成功返回matchobject,否则返回none;
  • search :将字符串的所有字串尝试与正则表达式匹配,如果所有的字串都没有匹配成功,返回none,否则返回matchobject;

import re  a =re.match('www.bai', 'www.baidu.com')  b = re.match('bai', 'www.baidu.com')  print(a.group())                                # www.bai  print(b)                                        # None    # 无论有多少个匹配的只会匹配一个  c = re.search('bai', 'www.baidubaidu.com')  print(c)                                        # <_sre.SRE_Match object; span=(4, 7), match='bai'>  print(c.group())                                # bai

match与search使用比较

split(pattern, string[, maxsplit=0])

  • 作用:将字符串以指定分割方式,格式化成列表

import re  text = 'aa 1bb###2cc3ddd'  print(re.split('W+', text))        # ['aa', '1bb', '2cc3ddd']  print(re.split('W', text))          # ['aa', '1bb', '', '', '2cc3ddd']  print(re.split('d', text))           # ['aa ', 'bb###', 'cc', 'ddd']  print(re.split('#', text))            # ['aa 1bb', '', '', '2cc3ddd']  print(re.split('#+', text))          # ['aa 1bb', '2cc3ddd']

split使用

findall(pattern, string)

  • 作用:正则表达式 re.findall 方法能够以列表的形式返回能匹配的子串

import re  p = re.compile(r'd+')  print(p.findall('one1two2three3four4'))             # ['1', '2', '3', '4']  print(re.findall('o','one1two2three3four4'))        # ['o', 'o', 'o']  print(re.findall('w+', 'he.llo, wo#rld!'))         # ['he', 'llo', 'wo', 'rld']

findall使用

sub(pat, repl, string[, count=0])

  • 替换,将string里匹配pattern的部分,用repl替换掉,最多替换count次然后返回替换后的字符串
  • 如果string里没有可以匹配pattern的串,将被原封不动地返回
  • repl可以是一个字符串,也可以是一个函数
  • 如果repl是个字符串,则其中的反斜杆会被处理过,比如 n 会被转成换行符,反斜杆加数字会被替换成相应的组,比如 6 表示pattern匹配到的第6个组的内容

import re  test="Hi, nice to meet you where are you from?"  print(re.sub(r's','-',test))          # Hi,-nice-to-meet-you-where-are-you-from?  print(re.sub(r's','-',test,5))        # Hi,-nice-to-meet-you-where are you from?  print(re.sub('o','**',test))           # Hi, nice t** meet y**u where are y**u fr**m?

sub使用

escape(string)

  • re.escape(pattern) 可以对字符串中所有可能被解释为正则运算符的字符进行转义的应用函数。
  • 如果字符串很长且包含很多特殊技字符,而你又不想输入一大堆反斜杠,或者字符串来自于用户(比如通过raw_input函数获取输入的内容),且要用作正则表达式的一部分的时候,可以用这个函数

import re  print(re.escape('www.python.org'))

escape使用

re模块中的匹配对象和组 group()

  • group方法返回模式中与给定组匹配的字符串,如果没有给定匹配组号,默认为组0
  • m.group() == m.group(0) == 所有匹配的字符

import re  a = "123abc321efg456"  print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0))    # 123abc321  print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).groups())    # ('123', 'abc', '321')  print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1))    # 123  print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2))    # abc  print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3))    # 321      import re  m = re.match('(..).*(..)(..)','123456789')  print(m.group(0))              # 123456789  print(m.group(1))              # 12  print(m.group(2))              # 67  print(m.group(3))              # 89

group(0)与group(1)区别比较

import re  m = re.match('www.(.*)..*','www.baidu.com')  print(m.group(1))           # baidu  print(m.start(1))             # 4  print(m.end(1))             # 9  print(m.span(1))            # (4, 9)

group()匹配之返回匹配索引

import re  test = 'dsfdf 22 g2323  GigabitEthernet0/3        10.1.8.1        YES NVRAM up eee'  # print(re.match('(w.*d)s+(d{1,3}.d{1,3}.d{1,3}.d{1,3})s+YESs+NVRAMs+(w+)s+(w+)s*', test).groups())    ret = re.search(  r'(w*/d+).*s(d{1,3}.d{1,3}.d{1,3}.d{1,3}).*(s+ups+)',test ).groups()  print(ret)          # 运行结果: ('GigabitEthernet0/3', '10.1.8.1', ' up ')    #1. (w*d+/d+)      匹配结果为:GigabitEthernet0/3  #1.1   w*: 匹配所有字母数字       #1.2   /d+:匹配所有斜杠开头后根数字 (比如:/3 )    #2. (d{1,3}.d{1,3}.d{1,3}.d{1,3})  匹配结果为:10.1.8.1    #3. s+ups+   匹配结果为: up 这个单词,前后都为空格

group()匹配ip,状态以元组返回

re模块其他知识点

import re  #匹配时忽略大小写  print(re.search("[a-z]+","abcdA").group())                #abcd  print(re.search("[a-z]+","abcdA",flags=re.I).group())            #abcdA  #连同换行符一起匹配:  #'.'默认匹配除n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行  print(re.search(r".+","naaanbbbnccc").group())            #aaa  print(re.search(r".+","naaanbbbnccc",flags=re.S))          #<_sre.SRE_Match object; span=(0, 12), match='naaanbbbnccc'>  print(re.search(r".+","naaanbbbnccc",flags=re.S).group())                                                                                     aaa                                                                                     bbb                                                                                     ccc

re匹配忽略大小写,匹配换行

1)init_l=[i for i in re.split('(-d+.*d*)',expression) if i]          a. 按照类似负数的字符串分割成列表          b. -d+.*d*是为了可以匹配浮点数(比如:3.14)          c. (if i)是为了去除列表中的空元素          d. 分割结果:['-1', '-2', '*((', '-60', '+30+(',  2)re.search('[+-*/(]$',expression_l[-1])          a. 匹配expression_l列表最后一个元素是 +,-,*,/,( 这五个符号就是负数  3)new_l=[i for i in re.split('([+-*/()])',exp) if i]          a. 将字符串按照+,-,*,/,(,)切分成列表(不是正真的负数就切分)  4)print(re.split('([+-])','-1+2-3*(2*2+3)'))            #按照加号或者减号分割成列表  运行结果: ['', '-', '1', '+', '2', '-', '3*(2*2', '+', '3)']

计算器用到的几个知识点

 

paramiko模块(二)

在windows中安装paramiko:   pip3 install paramiko

linux中scp命令的使用

ssh [email protected]     #ssh远程登录  scp -rp aa.txt  [email protected]:/tmp/    #将本地aa.txt文件复制到10.1.0.50的/tmp文件夹中

Paramiko模块作用

  • 如果需要使用SSH从一个平台连接到另外一个平台,进行一系列的操作时,
  • 比如:批量执行命令,批量上传文件等操作,paramiko是最佳工具之一。
  • paramiko是用python语言写的一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接
  • 由于使用的是python这样的能够跨平台运行的语言,所以所有python支持的平台,如Linux, Solaris, BSD,MacOS X, Windows等,paramiko都可以支持
  • 如果需要使用SSH从一个平台连接到另外一个平台,进行一系列的操作时,paramiko是最佳工具之一
  • 现在如果需要从windows服务器上下载Linux服务器文件:
    • a. 使用paramiko可以很好的解决以上问题,它仅需要在本地上安装相应的软件(python以及PyCrypto)
    • b. 对远程服务器没有配置要求,对于连接多台服务器,进行复杂的连接操作特别有帮助。

paramiko基于用户名密码连接

import paramiko    # 1 创建SSH对象  ssh = paramiko.SSHClient()  # 2 允许连接不在know_hosts文件中的主机  ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())  # 3 连接服务器  ssh.connect(hostname='1.1.1.3', port=22, username='root', password='chnsys@2016')    # 4 执行命令                                         #stdin标准输入: 自己输入的命令  stdin, stdout, stderr = ssh.exec_command('pwd')      # stdout标准输出:  命令执行结果  # 5 获取命令结果                                     #stderr标准错误:  命令执行报错的结果  res, err = stdout.read(), stderr.read()  result = res if res else err  print(result.decode())                              #运行结果: /root    # 6 关闭连接  ssh.close()

远程执行命令

import paramiko    #1 连接客户端  transport = paramiko.Transport(('10.1.0.50',22))  transport.connect(username='root',password='chnsys@2016')    #2 定义与客户端交互    将刚刚定义的transport当参数传递给他  sftp = paramiko.SFTPClient.from_transport(transport)  #3 将location.py 上传至服务器 /tmp/test.py  sftp.put(r'C:bbbfile.txt', '/tmp/file.txt')    #4 将remove_path 下载到本地 local_path  sftp.get('/tmp/file.txt',r'C:bbbfile.txt')    #5 关闭连接  transport.close()

SFTPClient实现对Linux服务器上传和下载

在两台Linux中演示无密码ssh登陆对方

  •  使用ssh-copy-id命令将公钥copy到被管理服务器中(法1:简单)
    • 操作目的:在10.1.0.50的tom用户下生成秘钥对,将生成的私钥用ssh-copy-id拷贝给10.1.0.51的root用户,那么root用户就可以使用ssh[email protected]远程登陆了

[tom@localhost .ssh]$ ssh-keygen  Generating public/private rsa key pair.  Enter file in which to save the key (/home/tom/.ssh/id_rsa):  Enter passphrase (empty for no passphrase):  Enter same passphrase again:  Your identification has been saved in /home/tom/.ssh/id_rsa.  Your public key has been saved in /home/tom/.ssh/id_rsa.pub.

ssh-keygen生成秘钥

  • 执行完上面命令后再 /home/tom/.ssh 文件夹下生成了公钥私钥连个文件:  id_rsa  id_rsa.pub
  • 将在10.1.0.50中生成的公钥复制到10.1.0.51的root加目录下:
  • 执行完成后就会在10.1.0.51中生成 /home/zhangsan/.ssh/ authorized_keys 文件
  • 此时输入: ssh [email protected]       就可以直接不用密码登陆了

手动创建秘钥并手动copy到被管理服务器(法2:较复杂)

  • 使用10.1.0.51在不输入密码的情况下ssh链接到10.1.0.50,使用10.1.0.50的tom用户身份进行登录
  • 在 10.1.0.51上创建用于认证的秘钥对

[root@localhost /]# ssh-keygen   Generating public/private rsa key pair.  Enter file in which to save the key (/root/.ssh/id_rsa):  Enter passphrase (empty for no passphrase):  Enter same passphrase again:  Your identification has been saved in /root/.ssh/id_rsa.        #存放私钥 的路径  Your public key has been saved in /root/.ssh/id_rsa.pub.        #存放公约 的路径  注:将10.1.0.51上生成的密钥对中的公钥放到10.1.0.50的服务器tom用户家目录/home/tom/.ssh/authorized_keys 中,就可以在10.1.0.51中无密码登陆10.1.0.50了

ssh-keygen生成秘钥对

  • 在10.1.0.51中生成的私钥路径:cat  ~/.ssh/id_rsa.pub
  • 在被登录服务器中创建用户tom,将刚刚在10.1.0.51上生成的私钥内容放到10.1.0.50的/home/tom/.ssh/authorized_keys中,
  • 新创建用户没用ssh登录过时没有,可以手动创建

1、mkdir /home/tom/.ssh                                               #创建/home/tom/.ssh目录  2、chmod 700 /home/tom/.ssh/                                     #将目录权限改为 700  3、touch /home/tom/.ssh/authorized_keys                              #创建/home/tom/.ssh/authorized_keys文件  4、chmod 600 /home/tom/.ssh/authorized_keys                     #将文件权限改为600  5、将10.1.0.51的公钥文件粘贴到10.1.0.50的/home/tom/.ssh/authorized_keys中

手动创建.ssh中的文件

  • 完成上面几步后就可以在10.1.0.51上无密码登陆10.1.0.50了
  • 登陆命令:  ssh [email protected]

paramiko基于公钥密钥连接:(ssh_rsa)

  • 操作目的:在10.1.0.50中生成公钥和私钥,然后将生成的公钥放到10.1.0.51的winuser的/home/zhangsan/.ssh/ authorized_keys 目录下
  • 第一步:在10.1.0.50中创建anyuser,在10.1.0.51中创建winuser
  • 在10.1.0.50中使用anyuser登陆,然后生成公钥和私钥

[anyuser@localhost ~]$ ssh-keygen  Generating public/private rsa key pair.    Enter file in which to save the key (/home/anyuser/.ssh/id_rsa): Created directory '/home/anyuser/.ssh'.  Enter passphrase (empty for no passphrase):  Enter same passphrase again:  Your identification has been saved in /home/anyuser/.ssh/id_rsa.  Your public key has been saved in /home/anyuser/.ssh/id_rsa.pub.

anyuser生成秘钥对

  • 将在10.1.0.50中生成的公钥复制到10.1.0.51的winuser目录下:    ssh-copy-id [email protected]
  • 执行完ssh-copy-id命令后就可以看到在10.1.0.51中新加了这样的目录和文件/home/zhangsan/.ssh/ authorized_keys   # authorized_keys就是刚刚在10.1.0.50中生成的私钥
  • 将10.1.0.50中生成秘钥文件内容放到windows的PyCharm运行文件同目录,随便命名为:id_rsa50.txt

import paramiko    # 1 指定公钥所在本地的路径  private_key = paramiko.RSAKey.from_private_key_file('id_rsa50.txt')    # 2 创建SSH对象  ssh = paramiko.SSHClient()  # 3 允许连接不在know_hosts文件中的主机  ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())  # 4 连接服务器  ssh.connect(hostname='10.1.0.51', port=22, username='winuser', pkey=private_key)    # 5 执行命令  stdin, stdout, stderr = ssh.exec_command('pwd')  # stdout标准输出:  命令执行结果  # 6 获取命令结果                                          #stderr标准错误:  命令执行报错的结果  res, err = stdout.read(), stderr.read()  result = res if res else err  print(result.decode())  # 7 关闭连接  ssh.close()

用PyCharm基于公钥密钥执行命令

复制代码  import paramiko  #1 指定公钥所在本地的路径  private_key = paramiko.RSAKey.from_private_key_file('id_rsa50.txt')    #2 连接客户端  transport = paramiko.Transport(('10.1.0.51', 22))  transport.connect(username='winuser', pkey=private_key )  #3 定义与客户端交互  sftp = paramiko.SFTPClient.from_transport(transport)    #4上传 将本地test1.py 上传至服务器 /tmp/test1.py  sftp.put('test1.py', '/tmp/test1.py')    #5下载 将服务器/tmp/test1.py文件 下载到本地C:bbbtest1.txt  sftp.get('/tmp/test1.py', r'C:bbbtest1.txt')    transport.close()

用PyCharm基于公钥密钥上传下载

 

subprocess模块(三)

subprocess原理以及常用的封装函数

  • 运行python的时候,我们都是在创建并运行一个进程。像Linux进程那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序
  • 在Python中,我们通过标准库中的subprocess包来fork一个子进程,并运行一个外部的程序。
  • subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用
  • 另外subprocess还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。

#1、返回执行状态:0 执行成功  retcode = subprocess.call(['ping', 'www.baidu.com', '-c5'])    #2、返回执行状态:0 执行成功,否则抛异常  subprocess.check_call(["ls", "-l"])    #3、执行结果为元组:第1个元素是执行状态,第2个是命令结果  >>> ret = subprocess.getstatusoutput('pwd')  >>> ret  (0, '/test01')    #4、返回结果为 字符串 类型  >>> ret = subprocess.getoutput('ls -a')  >>> ret  '.n..ntest.py'      #5、返回结果为'bytes'类型  >>> res=subprocess.check_output(['ls','-l'])  >>> res.decode('utf8')  '总用量 4n-rwxrwxrwx. 1 root root 334 11月 21 09:02 test.pyn'

subprocess常用函数

subprocess.check_output(['chmod', '+x', filepath])  subprocess.check_output(['dos2unix', filepath])

将dos格式文件转换成unix格式

subprocess.Popen()

  • 实际上,上面的几个函数都是基于Popen()的封装(wrapper),这些封装的目的在于让我们容易使用子进程
  • 当我们想要更个性化我们的需求的时候,就要转向Popen类,该类生成的对象用来代表子进程
  • 与上面的封装不同,Popen对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block)
  • 从运行结果中看到,父进程在开启子进程之后并没有等待child的完成,而是直接运行print。

#1、先打印'parent process'不等待child的完成  import subprocess  child = subprocess.Popen(['ping','-c','4','www.baidu.com'])  print('parent process')    #2、后打印'parent process'等待child的完成  import subprocess  child = subprocess.Popen('ping -c4 www.baidu.com',shell=True)  child.wait()  print('parent process')

child.wait()等待子进程执行

child.poll()                                 # 检查子进程状态  child.kill()                                  # 终止子进程  child.send_signal()                   # 向子进程发送信号  child.terminate()                       # 终止子进程

subprocess.PIPE 将多个子进程的输入和输出连接在一起

  • subprocess.PIPE实际上为文本流提供一个缓存区。child1的stdout将文本输出到缓存区,随后child2的stdin从该PIPE中将文本读取走
  • child2的输出文本也被存放在PIPE中,直到communicate()方法从PIPE中读取出PIPE中的文本。
  • 注意:communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成

import subprocess  #下面执行命令等价于: cat /etc/passwd | grep root  child1 = subprocess.Popen(["cat","/etc/passwd"], stdout=subprocess.PIPE)  child2 = subprocess.Popen(["grep","root"],stdin=child1.stdout, stdout=subprocess.PIPE)  out = child2.communicate()               #返回执行结果是元组  print(out)  #执行结果: (b'root:x:0:0:root:/root:/bin/bashnoperator:x:11:0:operator:/root:/sbin/nologinn', None)

分步执行cat /etc/passwd | grep root命

import subprocess    list_tmp = []  def main():      p = subprocess.Popen(['ping', 'www.baidu.com', '-c5'], stdin = subprocess.PIPE, stdout = subprocess.PIPE)      while subprocess.Popen.poll(p) == None:          r = p.stdout.readline().strip().decode('utf-8')          if r:              # print(r)              v = p.stdout.read().strip().decode('utf-8')              list_tmp.append(v)  main()  print(list_tmp[0])

获取ping命令执行结果