七、名称空间和作用域
可以简单理解为存放变量名和变量值之间绑定关系的地方。
1、名称空间
在 Python 中有各种各样的名称空间:
-
全局名称空间:每个程序的主要部分定义了全局的变量名和变量值的对应关系,这样就叫做全局名称空间
-
局部名称空间:在函数的运行中定义的临时的空间叫做局部名称空间,只可以被这个函数所有。函数运行结束后,这个局部的名称空间就会销毁。
-
内置名称空间:内置名称空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple...它们都是我们熟悉的,拿过来就可以用的方法。
1、三种名称空间之间的加载顺序
python3 test.py # 1、python解释器先启动,因而首先加载的是:内置名称空间 # 2、执行test.py文件,然后以文件为基础,加载全局名称空间 # 3、在执行文件的过程中如果调用函数,则临时产生局部名称空间
2、名字的查找顺序
当我们要使用一个变量时,先从哪里找?
局部使用:局部名称空间——>全局名称空间——>内置名称空间
x = 1 def f1():x = 10print(x) f1() # 输出 10
全局使用:全局——>内置
#需要注意的是:在全局无法查看局部的,在局部可以查看全局的,如下示例 x = 1 def f1():x = 10print(x) print(x) # 输出 1
2、作用域
作用域就是变量名的生效范围
分为: 全局作用域 和 局部作用域
1、全局作用域
内置的名称空间中的名字和全局名称空间的名字属于这个全局作用域。
-
全局作用域中的名字,在整个文件的任意位置都能被引用,全局存活,全局内都有效。
2、局部作用域
局部名称空间的名字属于局部作用域。
-
局部作用域中的名字只能在其本地局部生效和使用。
-
python
中局部作用域就是函数内定义或生成的名字,临时存活,随着函数调用的结束而消失。
八、模块的导入和使用
1、模块介绍
-
Python 有时候称为胶水语言,就是因为它有强大的可扩展性,这个扩展性就是用模块实现的。
-
模块其实就是一个以
.py
结尾的 Python 文件,这个文件中可以包含变量、函数、类等。 -
模块可以包含实现了一个或者多个功能的代码。
-
模块可以在其他 Python 文件中使用,可以通过网络进行传播。
这样的话,如果想在你的程序中实现某些功能,其实网络的其他程序猿已经给你写好了,下载下来,安装到自己的环境下,就可以使用了。
2、为什么要模块化
模块化编程是指将大型,笨拙的编程任务分解为单独的,更小的,更易于管理的子任务或模块的过程。然后可以像构建块一样拼凑单个模块以创建更大的应用程序。
在大型应用程序中模块化代码有几个优点:
-
简单性:模块通常只关注问题的一小部分,而不是关注当前的整个问题。如果您正在处理单个模块,那么您的头脑中要思考的将有一个较小的问题范围。这使得开发更容易,更不容易出错。
-
可维护性:模块通常设计为能够在不同的问题域之间实施逻辑边界。如果以最小化相互依赖性的方式编写模块,则对单个模块的修改将对程序的其他部分产生影响的可能性降低。(您甚至可以在不了解该模块之外的应用程序的情况下对模块进行更改。)这使得许多程序员团队在大型应用程序上协同工作更加可行。
-
可重用性:单个模块中定义的功能可以通过应用程序的其他部分轻松地重用。这消除了重新创建重复代码的需要。
-
范围:模块通常定义一个单独的命名空间,这有助于避免程序的不同区域中的变量名之间的冲突。
函数,模块和包都是Python中用于促进代码模块化的构造。
3、模块的分类
1、实现方式分类
实际上有两种不同的方法可以在Python中定义模块:
-
模块可以用Python本身编写。
-
模块可以用C编写并在运行时动态加载,就像
re
(正则表达式)模块一样。
以上两种情况下,模块的内容都以相同的方式访问:使用import
语句
2、模块的归属分类
-
-
包含在解释中的一个内置的模块本,像
[itertools](https://realpython.com/python-itertools/)
模块。
-
-
-
其他机构或个人开发者编写的模块,成为第三方模块
-
-
-
自己写的
.py
文件,就是自定义的模块
-
3、第三方模块
从网络上下载的模块称为 第三方模块。
安装方法
1、 pip3
工具安装
例如下面的示例是安装用于执行远程主机命令的模块 paramiko
注意: pip3 是 bash 环境下的命令 pip3 install paramiko
python2.x 使用
pip
python3.x 使用pip3
当然这也不是固定的,比如你给pip3
定义了一个别名pip
2、源码安装
源码安装就是,从网络上下载没有封装的 python 文件的源码,之后在本地执行其源码中的 setup.py
文件进行安装。
模块的源码一般都有一个主目录,主目录中包含了一个到多个子目录和文件。 但是主目录下一定有一个 setup.py
的文件,这个是源码安装的入口文件,就是需要执行这个文件并且传入一个 install
参数进行源码安装。
示例:
a. 下载源码包
wget https://files.pythonhosted.org/packages/4a/1b/9b40393630954b54a4182ca65a9cf80b41803108fcae435ffd6af57af5ae/redis-3.0.1.tar.gz
b. 解压源码包
tar -xf redis-3.0.1.tar.gz
-
进入模块源码的主目录,并安装源码包
上面表示安装成功
4、自定义模块
有的情况下,是需要自己编写一些模块的,这种就是自定义模块了。
示例:
some_mod.py x = 10 li = ['shark', 18] def foo():return 30 class Person():def __init__(self, name, age):self.name = nameself.age = age
5、内置模块
模块除了 第三方模块, 自定义模块,还有 内置模块。
4、模块的使用
-
使用模块需要先导入模块名。
-
模块名就是把
.py
去掉后的文件名。比如some_mod.py
的模块名就是some_mod
1、导入模块
import some_mod
2、使用模块中的对象
要想使用模块中的变量名或者函数名等,只需要使用 模块名.变量名
的方式即可。
例如,下面是使用的了 some_mod
模块中的 foo
函数。
import some_mod some_mod.foo()
3、更多模块导入的方式
a. 从模块中导入其中的一个对象
from some_mod import x
b. 从模块中导入多个对象
from some_mod import x, foo
c. 从模块中导入全部的对象, 不建议这么做
from some_mod import *
4、导入模块时模块的代码会自动被执行一次
st = """www.qfecu.com 千峰欢迎您! www.qfecu.com 千峰欢迎您! """ print(st)
5、模块名的搜索路径
当你导入模块或者包的时候, 查找模块或包的顺序:
-
系统会先从当前目录下查找
-
之后再从
sys.path
输出的值中的路径里查找模块名或者包名。
import sys print(sys.path)
sys.path
输出的值是一个 Python 的列表,这个列表我们可以对其修改的。
比如我们可以把某个文件的路径添加到此列表中,通常会这么做。
run.py import os import sys BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # python2 sys.path.insert(0, BASE_DIR) sys.path.insert(0,'D:\gitlab') # 将D:\gitlab 添加到python解释器的查询列表中 print(sys.path)
九、常用模块
1、sys 模块
提供了一系列有关Python运行环境的变量和函数
#python3 sys模块 #sys模块负责程序与python解释器的交互,提供了一系列的函数和变量, #用于操控python运行时的环境。 !# sys.argv 接收命令行参数,生成一个List,第一个元素是程序本身路径 # sys.modules.keys() 返回所有已经导入的模块列表 # sys.exc_info() 获取当前正在处理的异常类,exc_type、exc_value、exc_traceback当前处理的异常详细信息 !# sys.exit(n) 退出程序,正常退出时exit(0) # sys.hexversion 获取Python解释程序的版本值,16进制格式如:0x020403F0 # sys.version 获取Python解释程序的版本信息 # sys.maxsize 获取内存中最大的Int值 python2中是maxint # sys.maxunicode 获取内存从中最大的Unicode值 # sys.modules 返回系统导入的模块字典,key是模块名,value是模块 !# sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值 !# sys.platform 返回操作系统平台名称 # sys.stdout 标准输出 # sys.stdin 标准输入 # sys.stderr 错误输出 !# sys.exec_prefix 返回平台独立的python文件安装的位置 # sys.byteorder 本地字节规则的指示器,big-endian平台的值是'big',little-endian平台的值是'little' # sys.copyright 记录python版权相关的东西 # sys.api_version 解释器的C的API版本 import sys sys.argv # 命令行参数列表,第一个元素是程序本身路径;用于接收执行# Python 脚本时传的参数 # 示例: python3 echo.py a b c # echo.py 文件内容 import sys print(sys.argv[1:]) # 输出结果 ['a', 'b', 'c'] print('脚本名称:{}'.format(sys.argv[0])) for i in sys.argv:if i == sys.argv[0]:continueprint('参数为:',i)print('总参数个数:{}'.format(len(sys.argv)-1)[root@python python]# ./sysargv.py s1 s2 s3 s4 s5 脚本名称:./sysargv.py 参数为: s1 参数为: s2 参数为: s3 参数为: s4 参数为: s5 总参数个数:5
2、os 模块
os模块是与操作系统交互的一个接口,包含普遍的操作系统功能,如果你希望你的程序能够与平台有关的话,这个模块是尤为重要的。
import os # os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 # pwd # os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd # cd # os.curdir 返回当前目录: ('.') # os.pardir 获取当前目录的父目录字符串名:('..') # os.makedirs('dirname1/dirname2') 可生成多层递归目录 # mkdir -p # os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 rmdir -p # os.mkdir('dirname') 生成单级目录;相当于shell中mkdir dirname # mkdir # os.rmdir('dirname') 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname # rmdir # os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 # ls -A # os.remove() 删除一个文件 # rm -f # os.rename("oldname","newname") 重命名文件/目录 rename mv # os.stat('path/filename') 获取文件/目录信息 # os.sep 输出操作系统特定的路径分隔符,win下为"\",Linux下为"/" # os.linesep 输出当前平台使用的行终止符,win下为"\r\n",Linux下为"\n" # os.pathsep 输出用于分割文件路径的字符串 win下为;,Linux下为: # os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix' # os.system("bash command") 运行shell命令,直接显示 #一般用于脚本中去打印 # 这种方法实用性不如 os.popen subprocess.getoutput # os.environ 获取系统环境变量 # os.path.abspath(path) 返回path规范化的绝对路径 # os.path.split(path) 将path分割成目录和文件名二元组返回 # 不去检测文件系统 # os.path.dirname(path) 返回path的上一层目录。其实就是os.path.split(path)的第一个元素 # os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素 # os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False # if [ -e ] # os.path.isabs(path) 如果path是绝对路径,返回True # os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False # if [ -f ] # os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False # if [ -d ] # os.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,最后一个绝对路径之前的参数将被忽略 # os.path.getatime(path) 返回path所指向的文件或者目录的最后存取时间 # os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间 开始练习 import os # 获取当前工作目录,即当前python脚本工作的目录路径 os.getcwd() # 切换当前脚本工作目录;相当于shell下cd os.chdir("./dirname") # 创建单级目录,相当于 shell 中的 mkdir dirname os.mkdir('dirname') # 递归创建目录 os.makedirs('dir1/dir2') # 删除单级空目录,若目录非空则无法删除,并报错。 os.rmdir('dirname') # 递归删除 空 目录 os.removedirs('dir1/dir2') # 列出指定目录下的所有文件和目录,包括隐藏文件,并以列表方式打印 os.listdir('dirname') # 递归查找目录下的文件和目录,返回一个生成器对象。 # 生成器对象是个可迭代对象,每一层包含了三个值: # 1. 当前目录路径,2. 其下面的所有子目录, 3. 其下面的所有文件 os.walk('dirname') ### 练习需求 ### 1. 在 /tmp 目录下,创建目录 a/b/c 2. 进入到 /tmp/a/b 目录下,创建一个目录 f 3. 把当前的工作目录写到 f 目录下的 work.txt 文件内。 4. 删除目录 c 5. 把 /tmp 目录下及其子目录下的所有文件和目录打印到屏幕上 # 如果path存在,返回True;如果path不存在,返回False 在这里值得注意的是, # 在Linux shell 中,Python会认为: / 左边一定是一个目录,而不是文件 os.path.exists(path) # 如果path是一个存在的目录,则返回True。否则返回False os.path.isdir(path) # 如果path是一个存在的文件,返回True。否则返回False os.path.isfile(path) # 删除一个文件 os.remove('file') # 重命名文件/目录 os.rename("oldname", "new") # 如果path是绝对路径,返回 True os.path.isabs(path) # 将 path 分割成目录和文件名二元组返回 os.path.split(path) # 返回 文件 或 path 规范化的绝对路径 os.path.abspath(path) # 返回path的目录。 其实返回的就是 os.path.split(path) 的第一个元素 os.path.dirname(path)# 将多个路径组合后返回,每个路径之间不需要加路径分隔符(\或者/) os.path.join(path1[, path2[, ...]])
3、time 时间模块
1、time 模块
在Python中,通常有这几种方式来表示时间:
-
时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始到目前的秒数。我们运行
type(time.time())
,返回的是float类型。 -
结构化的时间(struct_time):struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,周几,一年中第几天,夏令时)
属性 值 tm_year(年) 比如2011 tm_mon(月) 1 - 12 tm_mday(日) 1 - 31 tm_hour(时) 0 - 23 tm_min(分) 0 - 59 tm_sec(秒) 0 - 61(60和61为闰秒) tm_wday(weekday) 0 - 6(0表示周一) tm_yday(一年中的第几天) 1 - 366 tm_isdst(是否是夏令时) 默认为 0
-
格式化的时间字符串(Format String)
# 快速认识它们 In [139]: import time In [140]: time.time() Out[140]: 1522057206.0065496 In [141]: time.localtime() # 本地时间, 结构化时间 Out[141]: time.struct_time(tm_year=2018, tm_mon=3, tm_mday=26, tm_hour=17, tm_min=40, tm_sec=53, tm_wday=0, tm_yday=85, tm_isdst=0) In [142]: time.gmtime() # 格林威治时间(UTC) Out[142]: time.struct_time(tm_year=2018, tm_mon=3, tm_mday=26, tm_hour=9, tm_min=43, tm_sec=31, tm_wday=0, tm_yday=85, tm_isdst=0) In [143]: time.strftime("%Y-%m-%d %X") Out[143]: '2018-03-26 17:40:40' #time模块没有time.py文件,是内置到解释器中的模块 #三种时间表示方式 ''' 1、时间戳(timestamp): 通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。 2、格式化的时间字符串:"2018-09-03 10:02:01" 3、元组(struct_time):struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天,夏令时) ''' #UTC:(Coordinated Universal Time,世界协调时),亦即格林威治天文时间,世界标准时间。在中国为UTC+8 #DST:(Daylight Saving Time),即夏令时。 大家开始练习 import time #时间戳 time() print(time.time()) 1535939025.4159343 #struct_time localtime([secs]) 将一个时间戳转换为当前时区的struct_time。secs参数未提供,则以当前时间为准。 #当地时间 print(time.localtime(time.time())) time.struct_time(tm_year=2018, tm_mon=9, tm_mday=3, tm_hour=9, tm_min=46, tm_sec=7, tm_wday=0, tm_yday=246, tm_isdst=0) print(time.localtime()) time.struct_time(tm_year=2018, tm_mon=9, tm_mday=3, tm_hour=9, tm_min=48, tm_sec=19, tm_wday=0, tm_yday=246, tm_isdst=0) t_local=time.localtime() print(t_local.tm_year) print(t_local.tm_mon) print(t_local.tm_mday) #gmtime([secs]) 将一个时间戳转换为UTC时区(0时区)的struct_time。 print(time.gmtime()) time.struct_time(tm_year=2018, tm_mon=9, tm_mday=3, tm_hour=1, tm_min=51, tm_sec=38, tm_wday=0, tm_yday=246, tm_isdst=0) mktime(t) : 将一个struct_time转化为时间戳。 print(time.mktime(time.localtime())) 1535939934.0 asctime([t]) : 把一个表示时间struct_time表示为这种形式:'Mon Sep 3 10:01:46 2018'。默认将time.localtime()作为参数传入。 print(time.asctime()) Mon Sep 3 10:01:46 2018 ctime([secs]) : 把一个时间戳转化为time.asctime()的形式,默认time.time()为参数。 print(time.ctime()) Mon Sep 3 10:05:40 2018 strftime(format[, t]) # 把一个代表时间的struct_time转化为格式化的时间字符串。 # 如果t未指定,将传入time.localtime()。 # 如果元组中任何一个元素越界,ValueError的错误将会被抛出。 print(time.strftime("%Y-%m-%d %X")) #%X 等同于 %H%M%S print(time.strftime("%Y-%m-%d %X",time.localtime())) print(time.strftime("%Y-%m-%d %H:%M:%S")) strptime(string[, format]) # 把一个格式化时间字符串转化为struct_time。实际上它是strftime()是逆操作。 print(time.strptime('2018-09-03 10:14:53', '%Y-%m-%d %X')) time.struct_time(tm_year=2018, tm_mon=9, tm_mday=3, tm_hour=10, tm_min=14, tm_sec=53, tm_wday=0, tm_yday=246, tm_isdst=-1) sleep(secs) time.sleep(10) #停止10秒,继续运行
格式化时间的变量:
格式 | 含义 |
---|---|
%a | 本地(locale)简化星期名称 |
%A | 本地完整星期名称 |
%b | 本地简化月份名称 |
%B | 本地完整月份名称 |
%c | 本地相应的日期和时间表示 |
%d | 一个月中的第几天(01 - 31) |
%H | 一天中的第几个小时(24小时制,00 - 23) |
%I | 第几个小时(12小时制,01 - 12) |
%j | 一年中的第几天(001 - 366) |
%m | 月份(01 - 12) |
%M | 分钟数(00 - 59) |
%p | 本地am或者pm的相应符 |
%S | 秒(00 - 61) |
%U | 一年中的星期数。(00 - 53星期天是一个星期的开始。)第一个星期天之前的所有天数都放在第0周。 |
%w | 一个星期中的第几天(0 - 6,0是星期天) |
%W | 和%U基本相同,不同的是%W以星期一为一个星期的开始。 |
%x | 本地相应日期 |
%X | 本地相应时间 |
%y | 去掉世纪的年份(00 - 99) |
%Y | 完整的年份 |
%Z | 时区的名字(如果不存在为空字符) |
%% | ‘%’字符 |
2、时间的互相转换
In [153]: time.time() Out[153]: 1522071229.4780636In [154]: time.localtime(1522071229.4780636)In [155]: time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(1522071229.4780636))In [156]: time.strptime('2018-03-26 21:41', "%Y-%m-%d %H:%M")In [157]: time.mktime(time.localtime()) In [178]: time.ctime(1093849081) Out[178]: 'Mon Aug 30 14:58:01 2004'In [179]: time.asctime(time.localtime()) Out[179]: 'Mon Mar 26 21:49:41 2018'In [183]: time.strptime('Mon mar 04 21:53:42 2018') In [182]: time.strptime('Mon mar 04 21:53:42 2018', "%a %b %d %H:%M:%S %Y") time.sleep(5) # 休眠 5 秒钟 print(time.clock()) # 精确到毫秒
3、datetime 模块
datetame 是 time 的升级版
import datetime In [192]: str(datetime.date.fromtimestamp(time.time())) Out[192]: '2018-03-26' In [193]: str(datetime.datetime.now()) Out[193]: '2018-03-26 22:09:44.424473' In [194]: datetime.datetime.now() + datetime.timedelta(3) Out[194]: datetime.datetime(2018, 3, 29, 22, 10, 42, 315584) In [196]: datetime.datetime.now() + datetime.timedelta(minutes=30) Out[196]: datetime.datetime(2018, 3, 26, 22, 41, 32, 44547) In [15]: datetime.date.strftime(datetime.datetime.now(),'%Y-%m') Out[15]: '2018-04'In [16]: datetime.datetime.strftime(datetime.datetime.now(),'%Y-%m') Out[16]: '2018-04' import time,datetime print('返回当前时间:',datetime.datetime.now()) 显示结果: 返回当前时间: 2017-12-19 18:13:01.974500 print('时间戳直接转成字符串格式: ',datetime.date.fromtimestamp(time.time())) 显示结果: 时间戳直接转成字符串格式: 2017-12-19 print('当前时间精确到微秒:',datetime.datetime.now()) 显示结果: 当前时间精确到微妙: 2017-12-19 18:13:01.974500 print('当前时间+3天: ',datetime.datetime.now() + datetime.timedelta(3)) 显示结果: 当前时间+3天: 2017-12-22 18:13:01.974500 print('当前时间-3天',datetime.datetime.now() + datetime.timedelta(-3)) 显示结果: 当前时间-3天 2017-12-16 18:13:01.974500print('当前时间+3小时',datetime.datetime.now() + datetime.timedelta(hours=3)) 显示结果: 当前时间+3小时 2017-12-19 21:13:01.974500 print('当前时间+30分: ',datetime.datetime.now() + datetime.timedelta(minutes=30)) 显示结果: 当前时间+30分: 2017-12-19 18:43:01.974500 print('当前时间+2年:',datetime.datetime.now()+datetime.timedelta(days=365*2)) 显示结果: 当前时间+2年: 2019-12-19 18:13:01.974500 c_time = datetime.datetime.now() print('时间替换:', c_time.replace(minute=3,hour=2)) #时间替换 显示结果: 时间替换: 2017-12-19 02:03:01.974500
4、shutil 压缩打包模块
shutil 是 Python3 中高级的文件 文件夹 压缩包 处理模块
1、拷贝文件
拷贝文件的内容到另一个文件中,参数是文件的相对路径或者绝对路径
import shutil shutil.copyfile('./src.file','./dst.file') #给目标文件命名
2、拷贝文件和权限
import shutil shutil.copy2('f1.log', 'f2.log')
3、递归拷贝
递归的去拷贝文件夹
shutil.copytree(src, dst, symlinks=False, ignore=None)
shutil.copytree('/home','/tmp/hbak',ignore=shutil.ignore_patterns('*.txt')) # 递归拷贝一个文件夹下的所有内容到另一个目录下,目标目录应该是原来系统中不存在的
shutil.ignore_patterns(*patterns) 忽略某些文件
ignore=shutil.ignore_patterns('排除的文件名', '排除的文件夹名') 支持通配符,假如有多个用逗号隔开
4、递归删除
递归删除一个文件夹下的所有内容
shutil.rmtree('/tmp/hb') shutil.rmtree('/tmp/hbad/') # 最后结尾的一定是明确的文件名,不可以类似下面这样 shutil.rmtree('/tmp/hbak/*')
5、递归移动
递归的去移动文件,它类似mv命令。
shutil.move('/home/src.file', './shark')
6、压缩
创建压缩包并返回文件路径,例如:zip、tar
# 将 /home/shark 目录下的所以文件打包压缩到当前目录下, # 名字 shark,格式 gztar。扩展名会自动根据格式自动生成。 shutil.make_archive('shark', # 压缩后文件名'gztar', # 指定的压缩格式'/home/shark/') # 被压缩的文件夹名字 # 将 /home/shark 目录下的所以文件打包压缩到 /tmp 目录下,名字shark,格式 tar shutil.make_archive( '/tmp/shark','tar','/home/shark/') # 查看当前 python 环境下支持的压缩格式 ret = shutil.get_archive_formats() print(ret)
7、解压缩
# 解压的文件名 解压到哪个路径下,压缩的格式 shutil.unpack_archive('./a/b.tar.gz', './a/c/','gztar')
5、subprocess 模块执行本机系统命令
os.system()
执行的命令只是把命令的结果输出导终端,程序中无法拿到执行命令的结果。
subprocess
是开启一个系统底层的单个进程,执行 shell 命令,可以得到命令的执行结果。
# 在 shell 中运行命令,并且获取到标准正确输出、标准错误输出 In [209]: subprocess.getoutput('ls |grep t') Out[209]: 'test.py' In [222]: ret = subprocess.getstatusoutput('date -u') In [223]: ret Out[223]: (0, '2018年 03月 26日 星期一 14:46:42 UTC')
6、Python3 正则模块(标准库)
1、常用特殊字符匹配内容
字符匹配:
正则特殊字符 | 匹配内容 |
---|---|
. | 匹配除换行符(\n)以外的单个任意字符 |
\w | 匹配单个字母、数字、汉字或下划线 |
\s | 匹配单个任意的空白符 |
\d | 匹配单个数字 |
\b | 匹配单词的开始或结束 |
^ | 匹配整个字符串的开头 |
$ | 匹配整个字符串的结尾 |
次数匹配:
正则特殊字符 | 匹配内容 |
---|---|
* | 重复前一个字符 0 - n 次 |
+ | 重复前一个字符 1 - n 次 |
? | 重复前一个字符 0 - 1 次 |
{n} | 重复前一个字符 n 次 a{2} |
匹配 aa | |
{n,} | 重复前一个字符 n 次或 n 次以上 a{2,} |
匹配 aa | |
或aaa | |
以上 | |
{n, m} | 重复前一个字符 n 到 m 次之间的任意一个都可以 |
2、Python 中使用正则的方法
match
只在整个字符串的起始位置进行匹配
示例字符串
string = "isinstance yangge enumerate www.qfedu.com 1997"
示例演示:
import re In [4]: r = re.match("is\w+", string) In [8]: r.group() # 获取匹配成功的结果 Out[8]: 'isinstance'
search
从整个字符串的开头找到最后,当第一个匹配成功后,就不再继续匹配。
In [9]: r = re.search("a\w+", string) In [10]: r.group() Out[10]: 'ance'
findall
搜索整个字符串,找到所有匹配成功的字符串, 把这些字符串放在一个列表中返回。
In [16]: r = re.findall("a\w+", string) In [17]: r Out[17]: ['ance', 'angge', 'ate']
sub
把匹配成功的字符串,进行替换。
re.sub的功能 re是regular expression的缩写,表示正则表达式;sub是substitude的缩写,表示替换 re.sub是正则表达式的函数,实现比普通字符串更强大的替换功能 # 语法: """ ("a\w+", "100", string, 2) 匹配规则,替换成的新内容, 被搜索的对象, 有相同的替换次数 """ sub(pattern,repl,string,count=0,flag=0) 1、pattern正则表达式的字符串 eg中r'\w+' 2、repl被替换的内容 eg中'10' 3、string正则表达式匹配的内容eg中"xy 15 rt 3e,gep" 4、count:由于正则表达式匹配的结果是多个,使用count来限定替换的个数从左向右,默认值是0,替换所有的匹配到的结果eg中2 5、flags是匹配模式,可以使用按位或者“|”表示同时生效,也可以在正则表达式字符串中指定 eg: In [24]: import re In [24]: re.sub(r'\w+','10',"xy 15 rt 3e,gep",2,flags=re.I ) '10 10 rt 3e,gep', 其中r'\w+'为正则表达式,匹配多个英文单词或者数字,'10'为被替换的内容,“xy 15 rt 3e,gep”是re匹配的字符串内容,count只替换前2个,flag表示忽略大小写 In [24]: r = re.sub("a\w+", "100", string, 2)In [25]: r Out[25]: 'isinst100 y100 enumera www.qfedu.com 1997' # 模式不匹配时,返回原来的值
split
以匹配到的字符进行分割,返回分割后的列表
re.split(pattern, string, maxsplit=0, flags=0) pattern compile 生成的正则表达式对象,或者自定义也可 string 要匹配的字符串 maxsplit 指定最大分割次数,不指定将全部分割 flags 标志位参数 (了解) re.I(re.IGNORECASE) 使匹配对大小写不敏感 re.L(re.LOCAL) 做本地化识别(locale-aware)匹配 re.M(re.MULTILINE) 多行匹配,影响 ^ 和 $ re.S(re.DOTALL) 使 . 匹配包括换行在内的所有字符 re.U(re.UNICODE) 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. re.X(re.VERBOSE) 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。
In [26]: string Out[26]: 'isinstance yangge enumerate www.qfedu.com 1997' In [27]: r = re.split("a", string, 1)
使用多个界定符分割字符串
>>> line = 'asdf fjdk; afed, fjek,asdf, foo' >>> import re >>> re.split(r'[;,\s]\s*', line) ['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']
3、正则分组
从已经成功匹配的内容中,再去把想要的取出来
# match In [64]: string Out[64]: 'isinstance yangge enumerate www.qfedu.com 1997' In [65]: r = re.match("is(\w+)", string) In [66]: r.group() Out[66]: 'isinstance' In [67]: r.groups() Out[67]: ('instance',)In [68]: r = re.match("is(in)(\w+)", string))In [69]: r.groups() Out[69]: ('in', 'stance') # search # 命名分组 In [87]: r = re.search("is\w+\s(?P<name>y\w+e)", string) In [88]: r.group() Out[88]: 'isinstance yangge' In [89]: r.groups() Out[89]: ('yangge',) 6 In [90]: r.groupdict() Out[90]: {'name': 'yangge'} 8 # findall In [98]: string Out[98]: 'isinstance all yangge any enumerate www.qfedu.com 1997' In [99]: r = re.findall("a(?P<name>\w+)", string) In [100]: r Out[100]: ['nce', 'll', 'ngge', 'ny', 'te'] In [101]: r = re.findall("a(\w+)", string) In [102]: r Out[102]: ['nce', 'll', 'ngge', 'ny', 'te'] # split In [113]: string Out[113]: 'isinstance all yangge any enumerate www.qfedu.com 1997' In [114]: r = re.split("(any)", string) In [115]: r Out[115]: ['isinstance all yangge ', 'any', ' enumerate www.qfedu.com 1997'] In [116]: r = re.split("(a(ny))", string) In [117]: r Out[117]: ['isinstance all yangge ','any','ny',' enumerate www.qfedu.com 1997'] In [118]: tag = 'value="1997/07/01"' In [119]: s = re.sub(r'(value="\d{4})/(\d{2})/(\d{2}")', r'\1-\2-\3', tag) In [120]: s Out [120]: value="1997-07-01"
编译正则
对于程序频繁使用的表达式,编译这些表达式会更有效。
compile 函数用于编译正则表达式,返回的是一个正则表达式( Pattern )对象,利用这个对象的相关方法再去匹配字符串。
好处:
re 模块会维护已编译表达式的一个缓存。
不过,这个缓存的大小是有限的,直接使用已编译的表达式可以避免缓存查找开销。
使用已编译表达式的另一个好处是,通过在加载模块时预编译所有表达式,可以把编译工作转到应用 一开始时,而不是当程序响应一个用户动作时才进行编译。
In [130]: regexes = re.compile("y\w+") In [131]: r = regexes.search(string) In [132]: r.group() Out[132]: 'yangge'
常用正则
# IP: r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b" # 手机号: r'\b1[3|4|5|6|7|8|9][0-9]\d{8}' # 邮箱: shark123@qq.com shark123@163.com r'[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+'
强调知识点
# 匹配所有包含小数在内的数字 print(re.findall('\d+\.?\d*',"asdfasdf123as1.13dfa12adsf1asdf3")) #['123', '1.13', '12', '1', '3'] #.*默认为贪婪匹配 print(re.findall('a.*b','a1b22222222b')) #['a1b22222222b'] #.*?为非贪婪匹配:推荐使用 print(re.findall('a.*?b','a1b22222222b')) #['a1b']
元字符
# \b 表示匹配单词边界,详见后面的速查表 In [17]: re.search('hello\b', 'hello world') In [18]: re.search('hello\\b', 'hello world') Out[18]: <_sre.SRE_Match object; span=(0, 5), match='hello'>
Python 自己也有转义
使用 r 禁止 Python 转义
In [19]: re.search(r'hello\b', 'hello world') Out[19]: <_sre.SRE_Match object; span=(0, 5), match='hello'>
扩展知识点
最后一个参数 flag
可选值有:
re.I # 忽略大小写
示例:
In [186]: s1 = string.capitalize() In [187]: s1 Out[187]: 'Isinstance all yangge any enumerate www.qfedu.com 1997' In [192]: r = re.search('is',s, re.I) Out[192]: <_sre.SRE_Match object; span=(0, 2), match='Is'>
4、正则模式速查表
模式 | 描述 |
---|---|
^ | 匹配字符串的开头 |
$ | 匹配字符串的末尾。 |
. | 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。 |
[...] | 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k' |
[^...] | 不在[]中的字符:abc 匹配除了a,b,c之外的字符。 |
re* | 匹配0个或多个的表达式。 |
re+ | 匹配1个或多个的表达式。 |
re? | 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式 |
re{n,m} | 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式 |
a| b | 匹配a或b |
(re) | 匹配括号内的表达式,也表示一个组 |
\w | 匹配字母数字及下划线 |
\W | 匹配非字母数字及下划线 |
\s | 匹配任意空白字符,等价于 [\t\n\r\f]. |
\S | 匹配任意非空字符 |
\d | 匹配任意数字,等价于 [0-9]. |
\D | 匹配任意非数字 |
\A | 匹配字符串开始 |
\Z | 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。 |
\z | 匹配字符串结束 |
\G | 匹配最后匹配完成的位置。 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
\B | 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
\n, \t, 等. | 匹配一个换行符。匹配一个制表符。等 |
\1...\9 | 匹配第n个分组的内容。 |
\10 | 匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式。 |
7、Python3 邮件模块
使用第三方库 **yagmail**
更新: 第三种方式的隐藏用户名和密码的方式,目前不再支持
1、简单介绍 yagmail
目标是尽可能简单,无痛地发送电子邮件。
最终的代码如下:
import yagmail yag = yagmail.SMTP() contents = ['This is the body, and here is just text http://somedomain/image.png','You can find an audio file attached.', '/local/path/song.mp3'] yag.send('to@someone.com', '邮件标题', contents)
或者在一行中实现:
yagmail.SMTP('mygmailusername').send('to@someone.com', 'subject', 'This is the body')
当然, 以上操作需要从你自己系统的密钥环中读取你的邮箱账户和对应的密码。关于密钥环稍后会提到如何实现。
2、安装模块
pip3 install yagmail # linux / Mac pip install yagmail # windows
这样会安装最新的版本,并且会支持所有最新功能,主要是支持从密钥环中获取到邮箱的账户和密码。
3、关于账户和密码
开通自己邮箱的 **SMTP**
** 功能,并获取到授权码**
这个账户是你要使用此邮箱发送邮件的账户,密码不是平时登录邮箱的密码,而是开通 POP3/SMTP
功能后设置的客户端授权密码。
这里以 126
邮箱为例:
4、方式一:不使用系统的密钥环
不使用系统的密钥环,可以直接暴露账户和密码在脚本里
import yagmail yag = yagmail.SMTP(user='自己的账号',password='账号的授权码',host='smtp.qq.com', # 邮局的 smtp 地址port='端口号', # 邮局的 smtp 端口smtp_ssl=False) yag.send(to='收件箱账号',subject='邮件主题',contents='邮件内容')
5、方式二: 使用系统的密钥环管理账户和授权码
模块支持从当前系统环境中的密钥环中获取账户和密码,要想实现这个功能,需要依赖模块 keyring
。之后把账户和密码注册到系统的密钥环中即可实现。
1. 安装依赖模块
pip3 install keyring # CentOS7.3 还需要安装下面的模块 pip3 install keyrings.alt
2. 开始向密钥环注册
import yagmail yagmail.register('你的账号', '你的授权密码')
注册账户和密码,只需要执行一次即可。
3. 发送邮件
import yagmail yag = yagmail.SMTP('自己的账号',host='smtp.qq.com', # 邮局的 smtp 地址port='端口号', # 邮局的 smtp 端口smtp_ssl=False # 不使用加密传输 ) yag.send(to='收件箱账号',subject='邮件主题',contents='邮件内容')
6、示例展示
下面是以我的 126 邮箱为例, 使用系统密钥环的方式,向我的 163邮箱发送了一封邮件。
import yagmail yag = yagmail.SMTP(user='shark@126.com',host='smtp.126.com',port=25,smtp_ssl=False) yag.send(to='docker@163.com',subject='from shark',contents='test')
这样就愉快的发送了一封测试邮件到 docker@163.com
的邮箱。
当然前提是:
-
126 邮箱开通了
SMTP
功能。 -
把 126 邮箱的账号和密码已经注册到自己系统的密钥环中。
7、发送附件
发送
发送附件只需要给 send
方法传递 attachments
关键字参数
比如我在系统的某一个目录下有一张图片,需要发送给 docker@163.com
import yagmail yag = yagmail.SMTP(user='shark@126.com',host='smtp.126.com',port=25,smtp_ssl=False) yag.send(to='docker@163.com',subject='from shark',contents='test',attachments='./松鼠.jpeg')
8、收到的邮件和附件
image.png
9、使用 ssl
发送加密邮件
要发送加密邮件,只需要把 smtp_ssl
关键字参数去掉即可,因为默认就是采用的加密方式 smtp_ssl=True
。
不传递 stmp_ssl
关键字参数的同时,需要设置端口为邮箱服务提供商的加密端口,这里还是以 126 邮箱为例,端口是 465
。
import yagmail yag = yagmail.SMTP(user='shark@126.com',host='smtp.126.com',port=465) yag.send(to='docker@163.com',subject='from sharkyunops',contents='test',attachments='./松鼠.jpeg')
10、发送 带 html 标记语言的邮件内容
在实际的生产环境中,经常会发送邮件沟通相关事宜,往往会有表格之类的内容,但是又不想以附件的形式发送,就可以利用 html 标记语言的方式组织数据。
import yagmail yag = yagmail.SMTP(user='shark@126.com',host='smtp.126.com',port=465) html="""<table border="1"><thead><tr><th>姓名</th><th>年龄</th></tr></thead><tbody><tr><td>shak</td><td>18</td></tr><tr><td>西瓜甜</td><td>28</td></tr></tbody> </table> """ yag.send(to='docker@163.com',subject='from sharkyunops',contents=['test',html])
11、更多
如果不指定to
参数,则发送给自己
如果to
参数是一个列表,则将该邮件发送给列表中的所有用户
attachments
参数的值可以是列表,表示发送多个附件
8、Python3 paramiko 模块
Paramiko 模块提供了基于ssh连接,进行远程登录服务器执行命令和上传下载文件的功能。这是一个第三方的软件包,使用之前需要安装。
1、安装
pip3 install paramiko
2、执行命令
1、基于用户名和密码的连接
封装 Transport
import paramiko #实例化一个transport(传输)对象 transport = paramiko.Transport(('172.16.32.129',2323)) #建立连接 transport.connect(username='root',password='123') #建立ssh对象 ssh = paramiko.SSHClient() #绑定transport到ssh对象 ssh._transport=transport #执行命令 stdin,stdout,stderr=ssh.exec_command('df') #打印输出 print(stdout.read().decode()) #关闭连接 transport.close() import paramiko transport = paramiko.Transport(('172.16.153.134', 22)) transport.connect(username='root', password='upsa') ssh = paramiko.SSHClient() ssh._transport = transport stdin, stdout, stderr = ssh.exec_command('df -P') print(stdout.read().decode()) transport.close()
3、文件上传下载
SFTPClient:
用于连接远程服务器并进行上传下载功能。
下面的命令可以快速创建密钥对,并用 -t 指定了算法类型为 ecdsa
ssh-keygen -t ecdsa -N "" ssh-keygen ssh-copy-id 192.168.95.154
1、基于用户名密码上传下载
import paramiko transport = paramiko.Transport(('10.18.46.104',22)) transport.connect(username='root',password='123.com') #paramiko.SSHClient() sftp = paramiko.SFTPClient.from_transport(transport) # 将ssh-key.py 上传至服务器 /tmp/ssh-key.py sftp.put('ssh-key.py', '/tmp/ssh-key.txt') # 后面指定文件名称 # 将远程主机的文件 /tmp/ssh-key.py 下载到本地并命名为 ssh-key.txt sftp.get('/tmp/ssh-key.py', 'ssh-key.txt') transport.close()