Fork me on GitHub

python自动化编程-第五天

python自动化编程-第五天 模块


一、模块

1、定义:

模块:用来从逻辑上组织python代码(变量,函数,类,逻辑;作用是实现某一个功能),本质上就是.py结尾的python文件(文件名:test.py,对应的模块名就是test)

包:用来从逻辑上组织模块,本质就是一个目录(但是必须带有一个__init__.py文件)

2、导入方法

import modue_name   #导入单个模块
import modue1_name,module2_name #导入多个模块


from module_name import [*|func_name|变量] 
#本质是将module_name的文件中的代码复制到脚本此处,并解释一遍

from module_name import func1_name,func2_name
#导入一个模块的多个函数


from module_name import logger as logger_test
#用as定义一个别名

3、import本质(路径搜索和搜索路径)

import modue_name
import的本质就是将module_name运行(解释)了一遍,然后将所有的代码,赋值给了module_name这个变量;
调用方法:module_name.func_name

from module_name import func_name
本质就是把module_name模块的func_name[变量|函数],在from module_name这个位置解释一遍;相当于将func_name的代码复制到import的位置。
调用方法:func_name

导入模块的本质就是把python文件解释一遍
导入包的本质就是执行该包下的__init__.py文件

import module_name ---->module_name.py---->module_name.py的路径--->sys.path

导入包与导入模块是不一样的!
导入包,要在__init__.py中导入模块,例如:form . import test1(使用相对导入),其中test1.py是一个模块,这样在导入包时,test1.py这个模块才能使用;

在其他目录导入包或模块时,需要用到from,例如
test
├──test1
│ └──test1.1
├──test2
│ └──test2.1
└──test3

要想在test2.1的脚本中调用test1.1,首先要将test目录加入到sys.path,然后使用from test1 import test1.1
这样就可以在test2.1中使用test1.1的代码了。

4、导入优化

from module_test import test
from主要是可以提高代码执行的效率,不需要每次都要找模块名
导入模块时,尽量使用from来导入;

5、模块的分类:

a.标准库
b.开源模块
c.自定义模块

二、time模块与datetime模块

https://docs.python.org/3.6/library/datetime.html#datetime.time

1、time模块

time模块中,时间有三种格式:
第一种是时间戳格式(以秒为单位,从1970年开始的秒数);
第二种是字符串格式;
第三种是时间元组格式,
三种格式可以相互转换;

>>> import time
>>> time.time()
1513156133.173734   #时间戳,单位是秒

>>> time.localtime()
time.struct_time(tm_year=2017, tm_mon=12, tm_mday=13, tm_hour=17, tm_min=11, tm_sec=26, tm_wday=2, tm_yday=347, tm_isdst=0) #时间元组格式,默认是当前的本地时间,也就是UTC+8的时间,因为中国是东八区,因此UTC+8;

>>> time.localtime(1513156133.173734)
time.struct_time(tm_year=2017, tm_mon=12, tm_mday=13, tm_hour=17, tm_min=8, tm_sec=53, tm_wday=2, tm_yday=347, tm_isdst=0) #time.localtime()的参数时时间戳;

sleep() #表示静默,单位是秒

>>> time.gmtime()
time.struct_time(tm_year=2017, tm_mon=12, tm_mday=13, tm_hour=9, tm_min=18, tm_sec=59, tm_wday=2, tm_yday=347, tm_isdst=0) #当前时间的元组格式,返回的是UTC时间

>>> time.gmtime(1513156133.173734)
time.struct_time(tm_year=2017, tm_mon=12, tm_mday=13, tm_hour=9, tm_min=8, tm_sec=53, tm_wday=2, tm_yday=347, tm_isdst=0) #指定时间戳的时间元组格式,返回的是UTC时间

# mktime():将元组的形式转换为时间戳,只接受一个参数,不能直接传递元组,可以将元组设置为一个变量传给给mktime();
>>> import time
>>> starttime = time.localtime()
>>> time.mktime(starttime)
1513227499.0
>>> starttime
time.struct_time(tm_year=2017, tm_mon=12, tm_mday=14, tm_hour=12, tm_min=58, tm_sec=19, tm_wday=3, tm_yday=348, tm_isdst=0)
>>>

# strftime(format,元组):还可以传递元组,就可以将元组的时间转换为字符串了;
>>> time.strftime("%Y-%m-%d %H:%M:%S") #元组不写,默认为当前时间
'2017-12-14 12:58:58'
>>> time.strftime("%Y-%m-%d %H:%M:%S",starttime)
'2017-12-14 12:58:19'
>>> starttime
time.struct_time(tm_year=2017, tm_mon=12, tm_mday=14, tm_hour=12, tm_min=58, tm_sec=19, tm_wday=3, tm_yday=348, tm_isdst=0)
>>>

# strptime(字符串,format):将字符串转换为元组:
>>> time.strptime('2017-12-14 12:28:58',"%Y-%m-%d %H:%M:%S")
time.struct_time(tm_year=2017, tm_mon=12, tm_mday=14, tm_hour=12, tm_min=28, tm_sec=58, tm_wday=6, tm_yday=337, tm_isdst=-1)

# asctime():将元组转换为字符串,该字符串有固定格式: '%c' 星期 月份 日 时间 年
>>> time.asctime(starttime)
'Thu Dec 14 12:58:19 2017'

# ctime():将时间戳转换为字符串,该字符串也有固定格式,与asctime()的格式一样;
>>> time.ctime(time.time())
'Thu Dec 14 13:23:54 2017'

取出时间元组的数据

>>> x = time.localtime()
>>> x.tm_year
2017 #取出年份

字符串格式的宏编码:

%a    本地简化星期名称    
%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    秒(01 - 61)    二    
%U    一年中的星期数。(00 - 53星期天是一个星期的开始。)第一个星期天之前的所有天数都放在第0周。    
%w    一个星期中的第几天(0 - 6,0是星期天)  
%W    和%U基本相同,不同的是%W以星期一为一个星期的开始。    
%x    本地相应日期    
%X    本地相应时间    
%y    去掉世纪的年份(00 - 99)    
%Y    完整的年份    
%Z    时区的名字(如果不存在为空字符)    
%%    ‘%’字符

2、datetime模块

datetime模块实际上就是time模块的高级封装;

datetime.date.now() #当前日期
datatime.time.now() #当前时间
datatime.datatime.now() #当前日期和时间
class datetime.timedelta(days=0、 seconds=0、 microseconds=0、 milliseconds=0、 minutes=0、 hours=0、 weeks=0)
# 所有参数都是可选的, 默认为0。参数可以是整数或浮点型, 也可以是正的或负的。

例如:

import datetime,time

print(datetime.datetime.now()) #返回 2016-08-19 12:47:03.941925
print(datetime.date.fromtimestamp(time.time()) )  # 时间戳直接转成日期格式 2016-08-19
print(datetime.datetime.now() )
print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天
print(datetime.datetime.now() + datetime.timedelta(-3)) #当前时间-3天
print(datetime.datetime.now() + datetime.timedelta(hours=3)) #当前时间+3小时
print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间+30分

# 输出:
2017-12-14 13:30:41.923147
2017-12-14
2017-12-14 13:30:41.923213
2017-12-17 13:30:41.923222
2017-12-11 13:30:41.923238
2017-12-14 16:30:41.923246
2017-12-14 14:00:41.923258

c_time  = datetime.datetime.now()
print(c_time.replace(minute=3,hour=2)) #时间替换

三、random 模块

随机数模块

import random
print (random.random())  #0.6445010863311293  
# random.random()用于生成一个0到1的随机符点数: 0 <= n < 1.0

print (random.randint(1,7)) #4
# random.randint()的函数原型为:random.randint(a, b),用于生成一个指定范围内的整数。
# 其中参数a是下限,参数b是上限,生成的随机数n: a <= n <= b

print (random.randrange(1,10)) #5
# random.randrange的函数原型为:random.randrange([start], stop[, step]),
# 从指定范围内,按指定基数递增的集合中 获取一个随机数。如:random.randrange(10, 100, 2),
# 结果相当于从[10, 12, 14, 16, ... 96, 98]序列中获取一个随机数。
# random.randrange(10, 100, 2)在结果上与 random.choice(range(10, 100, 2) 等效.
# stop -- 指定范围内的结束值,不包含在范围内。

print(random.choice('liukuni')) #i
# random.choice从序列中获取一个随机元素。
# 其函数原型为:random.choice(sequence)。参数sequence表示一个有序类型。
# 这里要说明一下:sequence在python不是一种特定的类型,而是泛指一系列的类型。
# list, tuple, 字符串都属于sequence。有关sequence可以查看python手册数据模型这一章。
# 下面是使用choice的一些例子:
print(random.choice("学习Python")) #学
print(random.choice(["JGood","is","a","handsome","boy"]))  #List
print(random.choice(("Tuple","List","Dict")))   #List
print(random.sample([1,2,3,4,5],3))    #[1, 2, 5]   随机取3个
#random.sample的函数原型为:random.sample(sequence, k),从指定序列中随机获取指定长度的片断。sample函数不会修改原有序列。

print(random.uniform(1, 10)) #9.887001463194844  取1到10之间的随机浮点数

#洗牌
items = [1,2,3,4,5,6,7]
print(items) #[1, 2, 3, 4, 5, 6, 7]
random.shuffle(items)
print(items) #[1, 4, 7, 2, 5, 3, 6]

应用场景:随机验证码

import random

checkcode = ''

for i in range(4):
    current = random.randrange(0,4)
    #字母   ascii中65-90是大写字母,97-122是小写字母
    if current == i:
        tmp = chr(random.randint(65,90))
    #数字
    else:
        tmp = random.randint(0,9)
    checkcode += str(tmp)


print(checkcode)

四、os 模块

https://docs.python.org/3.6/library/os.html?highlight=os#module-os
OS模块主要用来处理与操作系统相关的操作

os.getcwd() #获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname")  #改变当前脚本工作目录;相当于shell下cd
	\\ 转义 \,
	在字符串前加r
	在Python的string前面加上‘r’, 是为了告诉编译器这个string是个raw string,不要转义backslash '\' 。 例如,\n 在raw string中,是两个字符,\和n, 而不会转意为换行符。由于正则表达式和 \ 会有冲突,因此,当一个字符串使用了正则表达式后,最好在前面加上'r'。

os.curdir  #返回当前目录: ('.')
os.pardir  #获取当前目录的父目录字符串名:('..')
os.makedirs('dirname1/dirname2')    #可生成多层递归目录
os.removedirs('dirname1')    #若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname')    #生成单级目录;相当于shell中mkdir dirname
os.rmdir('dirname')    #删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir('dirname')    #列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove()  #删除一个文件
os.rename("oldname","newname")  #重命名文件/目录
os.stat('path/filename')  #获取文件/目录信息
os.sep    #输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep    #输出当前平台使用的行终止符,win下为"\r\n",Linux下为"\n"
os.pathsep    #输出用于分割文件路径的字符串
os.popen      #保存命令的执行结果输出,使用read()读出命令的结果,或者使用print打印到屏幕上,
os.name   #输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
os.system("bash command")  #运行shell命令,直接显示
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
os.path.isabs(path)  #如果path是绝对路径,返回True
os.path.isfile(path)  #如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path)  #如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]])  #将多个路径组合后返回,第一个绝对路径之前的参数将被忽略

os.path.getatime(path)  #返回path所指向的文件或者目录的最后存取时间
os.path.getmtime(path)  #返回path所指向的文件或者目录的最后修改时间
os.path.getsize(filename) #返回filename文件的大小,单位字节,filename必须是绝对路径

五、sys 模块

https://docs.python.org/3.6/library/sys.html#module-sys
sys模块主要用来处理与python解析器相关的操作;

sys.argv           命令行参数List,第一个元素是程序本身路径
sys.exit(n)        退出程序,正常退出时exit(0)
sys.version        获取Python解释程序的版本信息
sys.maxint         最大的Int值
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       返回操作系统平台名称
sys.stdout.write('please:')
val = sys.stdin.readline()[:-1]

六、shutil 模块

高级文件、文件夹、压缩包处理模块

shutil.copyfileobj(fsrc, fdst[, length])
将文件内容拷贝到另一个文件中,可以部分内容

shutil.copyfile(src, dst)
拷贝文件

shutil.copymode(src, dst)
仅拷贝权限。内容、组、用户均不变

shutil.copystat(src, dst)
拷贝状态的信息,包括:mode bits, atime, mtime, flags

shutil.copy(src, dst)
拷贝文件和权限

shutil.copy2(src, dst)
拷贝文件和状态信息

shutil.ignore_patterns(*patterns)
shutil.copytree(src, dst, symlinks=False, ignore=None)
递归的去拷贝文件

例如:copytree(source, destination, ignore=ignore_patterns('.pyc', 'tmp'))

shutil.rmtree(path[, ignore_errors[, onerror]])
递归的去删除文件

shutil.move(src, dst)
递归的去移动文件

shutil.make_archive(base_name, format,...)

创建压缩包并返回文件路径,例如:zip、tar

  • base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
  • 如:www =>保存至当前路径
  • 如:/Users/wupeiqi/www =>保存至/Users/wupeiqi/
  • format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
  • root_dir: 要压缩的文件夹路径(默认当前目录)
  • owner: 用户,默认当前用户
  • group: 组,默认当前组
  • logger: 用于记录日志,通常是logging.Logger对象
#将 /Users/wupeiqi/Downloads/test 下的文件打包放置当前程序目录
 
import shutil
ret = shutil.make_archive("wwwwwwwwww", 'gztar', root_dir='/Users/wupeiqi/Downloads/test')
 
 
#将 /Users/wupeiqi/Downloads/test 下的文件打包放置 /Users/wupeiqi/目录
import shutil
ret = shutil.make_archive("/Users/wupeiqi/wwwwwwwwww", 'gztar', root_dir='/Users/wupeiqi/Downloads/test')

shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的,详细:

import zipfile

# 压缩
z = zipfile.ZipFile('laxi.zip', 'w')
z.write('a.log')
z.write('data.data')
z.close()

# 解压
z = zipfile.ZipFile('laxi.zip', 'r')
z.extractall()
z.close()


import tarfile

# 压缩
tar = tarfile.open('your.tar','w')
tar.add('/Users/wupeiqi/PycharmProjects/bbs2.zip', arcname='bbs2.zip')
tar.add('/Users/wupeiqi/PycharmProjects/cmdb.zip', arcname='cmdb.zip')
tar.close()

# 解压
tar = tarfile.open('your.tar','r')
tar.extractall()  # 可设置解压地址
tar.close()

七、json & pickle 模块

详见第四天的序列化和反序列化,

八、shelve 模块

shelve模块是一个简单的k,v将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式,只支持pickle格式;

import shelve
 
d = shelve.open('shelve_test') #打开一个文件
 
t = [1,2,3,4,5] 
t2 = ‘hello world’
 
name = ["alex","rain","test"]
d["test"] = name #持久化列表
d["t1"] = t      #持久化类
d["t2"] = t2
 
d.close()

#读取文件内容
print(d.get('test'))
print(d.get('t1'))
print(d.get('t2'))

九、XML 模块

xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单;xml还有些地方在使用;
xml的格式如下,就是通过<>节点来区别数据结构的:

<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank updated="yes">2</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
    <country name="Singapore">
        <rank updated="yes">5</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/>
    </country>
    <country name="Panama">
        <rank updated="yes">69</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>
</data>

xml协议在各个语言里的都 是支持的,在python中可以用以下模块操作xml  

import xml.etree.ElementTree as ET
 
tree = ET.parse("xmltest.xml")
root = tree.getroot()
print(root.tag)
 
#遍历xml文档
for child in root:
    print(child.tag, child.attrib)
    for i in child:
        print(i.tag,i.text)
 
#只遍历year 节点
for node in root.iter('year'):
    print(node.tag,node.text)

修改和删除xml文档的内容

import xml.etree.ElementTree as ET
 
tree = ET.parse("xmltest.xml")
root = tree.getroot()
 
#修改
for node in root.iter('year'):
    new_year = int(node.text) + 1
    node.text = str(new_year)
    node.set("updated","yes")
 
tree.write("xmltest.xml")
 
 
#删除node
for country in root.findall('country'):
   rank = int(country.find('rank').text)
   if rank > 50:
     root.remove(country)
 
tree.write('output.xml')

自己创建xml文档

import xml.etree.ElementTree as ET
 
 
new_xml = ET.Element("namelist")
name = ET.SubElement(new_xml,"name",attrib={"enrolled":"yes"})
age = ET.SubElement(name,"age",attrib={"checked":"no"})
sex = ET.SubElement(name,"sex")
sex.text = '33'
name2 = ET.SubElement(new_xml,"name",attrib={"enrolled":"no"})
age = ET.SubElement(name2,"age")
age.text = '19'
 
et = ET.ElementTree(new_xml) #生成文档对象
et.write("test.xml", encoding="utf-8",xml_declaration=True)
 
ET.dump(new_xml) #打印生成的格式

十、PyYAML 模块

Python也可以很容易的处理ymal文档格式,只不过需要安装一个模块,参考资料:http://pyyaml.org/wiki/PyYAMLDocumentation

安装
pip3 install pyyaml

十一、ConfigParser 模块

用于生成和修改常见配置文档,当前模块的名称在 python 3.x 版本中变更为 configparser。

来看一个好多软件的常见文档格式如下

[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
 
[bitbucket.org]
User = hg
 
[topsecret.server.com]
Port = 50022
ForwardX11 = no

创建configparser文档

import configparser
 
config = configparser.ConfigParser()
config["DEFAULT"] = {'ServerAliveInterval': '45',
                      'Compression': 'yes',
                     'CompressionLevel': '9'}
 
config['bitbucket.org'] = {}
config['bitbucket.org']['User'] = 'hg'
config['topsecret.server.com'] = {}
topsecret = config['topsecret.server.com']
topsecret['Host Port'] = '50022'     # mutates the parser
topsecret['ForwardX11'] = 'no'  # same here
config['DEFAULT']['ForwardX11'] = 'yes'
with open('example.ini', 'w') as configfile:
   config.write(configfile)

读取configparser文档

>>> import configparser
>>> config = configparser.ConfigParser()
>>> config.sections()
[]
>>> config.read('example.ini')
['example.ini']
>>> config.sections()
['bitbucket.org', 'topsecret.server.com']
>>> 'bitbucket.org' in config
True
>>> 'bytebong.com' in config
False
>>> config['bitbucket.org']['User']
'hg'
>>> config['DEFAULT']['Compression']
'yes'
>>> topsecret = config['topsecret.server.com']
>>> topsecret['ForwardX11']
'no'
>>> topsecret['Port']
'50022'
>>> for key in config['bitbucket.org']: print(key)
...
user
compressionlevel
serveraliveinterval
compression
forwardx11
>>> config['bitbucket.org']['ForwardX11']
'yes'

configparser文档的方法

[section1]
k1 = v1
k2:v2
  
[section2]
k1 = v1
 
import ConfigParser
  
config = ConfigParser.ConfigParser()
config.read('i.cfg')
  
# ########## 读 ##########
#secs = config.sections()
#print secs
#options = config.options('group2')
#print options
  
#item_list = config.items('group2')
#print item_list
  
#val = config.get('group1','key')
#val = config.getint('group1','key')
  
# ########## 改写 ##########
#sec = config.remove_section('group1')
#config.write(open('i.cfg', "w"))
  
#sec = config.has_section('wupeiqi')
#sec = config.add_section('wupeiqi')
#config.write(open('i.cfg', "w"))
  
  
#config.set('group2','k1',11111)
#config.write(open('i.cfg', "w"))
  
#config.remove_option('group2','age')
#config.write(open('i.cfg', "w"))

十二、hashlib 模块

用于加密相关的操作,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法
使用场景:一般用于网站防篡改,ssl通用的是sha256

import hashlib
 
m = hashlib.md5()
m.update(b"Hello")
m.update(b"It's me")
print(m.digest())
m.update(b"It's been a long time since last time we ...")
 
print(m.digest()) #2进制格式hash
print(len(m.hexdigest())) #16进制格式hash
'''
def digest(self, *args, **kwargs): # real signature unknown
    """ Return the digest value as a string of binary data. """
    pass
 
def hexdigest(self, *args, **kwargs): # real signature unknown
    """ Return the digest value as a string of hexadecimal digits. """
    pass
 
'''
import hashlib
 
# ######## md5 ########
 
hash = hashlib.md5()
hash.update('admin')
print(hash.hexdigest())
 
# ######## sha1 ########
 
hash = hashlib.sha1()
hash.update('admin')
print(hash.hexdigest())
 
# ######## sha256 ########
 
hash = hashlib.sha256()
hash.update('admin')
print(hash.hexdigest())
 
 
# ######## sha384 ########
 
hash = hashlib.sha384()
hash.update('admin')
print(hash.hexdigest())
 
# ######## sha512 ########
 
hash = hashlib.sha512()
hash.update('admin')
print(hash.hexdigest())

十三、hmac 模块

散列消息鉴别码,简称HMAC,是一种基于消息鉴别码MAC(Message Authentication Code)的鉴别机制。使用HMAC时,消息通讯的双方,通过验证消息中加入的鉴别密钥K来鉴别消息的真伪;

一般用于网络通信中消息加密,前提是双方先要约定好key,就像接头暗号一样,然后消息发送把用key把消息加密,接收方用key + 消息明文再加密,拿加密后的值 跟 发送者的相对比是否相等,这样就能验证消息的真实性,及发送者的合法性了。

import hmac
h = hmac.new(b'天王盖地虎', b'宝塔镇河妖')
print h.hexdigest()

3个参数,key,加密的内容,加密算法
key和加密的内容都要是byte类型

hmac和hashlib的参数中如果有中文,则需要encode(编码)一下,

更多关于md5,sha1,sha256等介绍的文章看这里https://www.tbs-certificates.co.uk/FAQ/en/sha256.html

十四、subprocess 模块 --- 获取命令输入

允许您生成新的进程,连接到它们的输入/输出/错误管道,并获得它们的返回代码;主要用来替换以下模块:
os.system
os.spawn*

>>> subprocess.run(["ls", "-l"])
显示结果
>>> subprocess.run("ls -l",shell=True)

如果命令带有管道符,则需要把整个命令当做一个字符串来传递,并且需要shell=true这个参数,若没有管道符,则需要传递一个列表,而每个参数当做列表的一个元素来传递。

#执行命令,返回命令执行状态 , 0 or 非0
>>> retcode = subprocess.call(["ls", "-l"])

#执行命令,如果命令结果为0,就正常返回,否则抛异常
>>> subprocess.check_call(["ls", "-l"])
0

#接收字符串格式命令,返回元组形式,第1个元素是执行状态,第2个是命令结果 
>>> subprocess.getstatusoutput('ls /bin/ls')
(0, '/bin/ls')

#接收字符串格式命令,并返回结果
>>> subprocess.getoutput('ls /bin/ls')
'/bin/ls'

#执行命令,并返回结果,注意是返回结果,不是打印,下例结果返回给res
>>> res=subprocess.check_output(['ls','-l'])
>>> res
b'total 0\ndrwxr-xr-x 12 alex staff 408 Nov 2 11:05 OldBoyCRM\n'

#上面那些方法,底层都是封装的subprocess.Popen
poll()
Check if child process has terminated. Returns returncode
返回命令执行状态,None表示没有执行完成,0表示执行完成

wait()
Wait for child process to terminate. Returns returncode attribute.
等待执行结果,直到出现结果;

terminate() 杀掉所启动进程
communicate() 等待任务结束,一般交互时使用

stdin 标准输入

stdout 标准输出

stderr 标准错误

pid
The process ID of the child process.

#例子
>>> p = subprocess.Popen("df -h|grep disk",stdin=subprocess.PIPE,stdout=subprocess.PIPE,shell=True)
>>> p.stdout.read()
b'/dev/disk1 465Gi 64Gi 400Gi 14% 16901472 104938142 14% /\n'
# subprocess.Popen 默认输出不是输出到屏幕上,必须在参数中指定下,stdout=subprocess.PIPE,先将标准输出输出到管道上,然后在通过read()输出到屏幕上;PIPE相当于一个存储功能,屏幕不能保存结果,结果保存在PIPE中,PIPE在内存中;这种方法可以隔离错误信息;输入时也是一样,不同进程之间相互通信,要通过管道来进行;
>>> subprocess.run(["ls", "-l"])  # doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)
 
>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
 
>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')

调用subprocess.run(...)是推荐的常用方法,但是run()方法在python3.5才出现的方法,在大多数情况下能满足需求,但如果你可能需要进行一些复杂的与系统的交互的话,你还可以用subprocess.Popen(),语法如下:

p = subprocess.Popen("find / -size +1000000 -exec ls -shl {} \;",shell=True,stdout=subprocess.PIPE)
print(p.stdout.read())

可用参数:

  • args:shell命令,可以是字符串或者序列类型(如:list,元组)
  • bufsize:指定缓冲。0 无缓冲,1 行缓冲,其他 缓冲区大小,负值 系统缓冲
  • stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
  • preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
  • close_sfs:在windows平台下,如果close_fds被设置为True,则新创建的子进程将不会继承父进程的输入、输出、错误管道。所以不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误(stdin, stdout, stderr)。
  • shell:不要python解析命令,直接传递到系统上让系统来解析;
  • cwd:用于设置子进程的当前目录,
  • env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
  • universal_newlines:不同系统的换行符不同,True -> 同意使用 \n,默认自动兼容windows和linux
  • startupinfo与createionflags只在windows下有效,将被传递给底层的CreateProcess()函数,用于设置子进程的一些属性,如:主窗口的外观,进程的优先级等等

终端输入的命令分为两种:

  • 输入即可得到输出,如:ifconfig
  • 输入进行某环境,依赖再输入,如:python

需要交互的命令示例

import subprocess
 
obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
obj.stdin.write('print 1 \n ')
obj.stdin.write('print 2 \n ')
obj.stdin.write('print 3 \n ')
obj.stdin.write('print 4 \n ')
 
out_error_list = obj.communicate(timeout=10)
print out_error_list

subprocess实现sudo 自动输入密码(可以不用这个函数的方法,直接echo即可)

import subprocess
 
def mypass():
    mypass = '123' #or get the password from anywhere
    return mypass
 
echo = subprocess.Popen(['echo',mypass()],
                        stdout=subprocess.PIPE,
                        )
 
sudo = subprocess.Popen(['sudo','-S','iptables','-L'],
                        stdin=echo.stdout,
                        stdout=subprocess.PIPE,
                        )
 
end_of_pipe = sudo.stdout
 
print "Password ok \n Iptables Chains %s" % end_of_pipe.read()


使用linux的命令:
echo 'password' | sudo -S iptables -L

另外Python2.7中,Commands 模块(windows上不好用),commands.getstatusoutput()

>>> commands.getstatusoutput('pwd')
(0, '/Users/chenzhuanglan')

此命令在Python3中可以通过subprocess.getstatusoutput()方法代替

>>> subprocess.getstatusoutput('ls')
(0, 'Applications\nApplications (Parallels)\nDesktop\nDocuments\nDownloads\nDropbox\nLibrary\nMovies\nMusic\nMusic-local\nOneDrive\nParallels\nPictures\nPublic\nlearning\nprivate\nworking\n铃声\n虚拟机')
>>>

十五、logging 模块

logging的日志可以分为 debug(), info(), warning(), error() and critical() 5个级别

把日志写到文件中

import logging
 
logging.basicConfig(filename='example.log',level=logging.INFO)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')

其中下面这句中的level=loggin.INFO意思是,把日志纪录级别设置为INFO,也就是说,只有当日志是INFO或比INFO级别更高的日志才会被纪录到文件里,在这个例子, 第一条日志是不会被纪录的,如果希望纪录debug的日志,那把日志级别改成DEBUG就行了。

logging.basicConfig(filename='example.log',level=logging.INFO)

感觉上面的日志格式忘记加上时间啦,日志不知道时间怎么行呢,下面就来加上!

import logging
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('is when this event was logged.')
 
#输出
12/12/2010 11:46:36 AM is when this event was logged.

Python 使用logging模块记录日志涉及四个主要类,使用官方文档中的概括最为合适:

logger提供了应用程序可以直接使用的接口;

handler将(logger创建的)日志记录发送到合适的目的输出;

filter提供了细度设备来决定输出哪条日志记录;

formatter决定日志记录的最终输出格式。

logger
每个程序在输出信息之前都要获得一个Logger。Logger通常对应了程序的模块名,比如聊天工具的图形界面模块可以这样获得它的Logger:
LOG=logging.getLogger(”chat.gui”)
而核心模块可以这样:
LOG=logging.getLogger(”chat.kernel”)

Logger.setLevel(lel):指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加或删除指定的filter
Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或删除指定的handler
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():可以设置的日志级别

handler

handler对象负责发送相关的信息到指定目的地。Python的日志系统有多种Handler可以使用。有些Handler可以把信息输出到控制台,有些Logger可以把信息输出到文件,还有些 Handler可以把信息发送到网络上。如果觉得不够用,还可以编写自己的Handler。可以通过addHandler()方法添加多个多handler
Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
Handler.setFormatter():给这个handler选择一个格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象

每个Logger可以附加多个Handler。接下来我们就来介绍一些常用的Handler:

  1. logging.StreamHandler(屏幕输出)
    使用这个Handler可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。它的构造函数是:
    StreamHandler([strm])
    其中strm参数是一个文件对象。默认是sys.stderr

  2. logging.FileHandler(文件输出)
    和StreamHandler类似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件。它的构造函数是:
    FileHandler(filename[,mode])
    filename是文件名,必须指定一个文件名。
    mode是文件的打开方式。参见Python内置函数open()的用法。默认是’a',即添加到文件末尾。

  3. logging.handlers.RotatingFileHandler(日志文件大小滚动方式)
    这个Handler类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建 一个新的同名日志文件继续输出。比如日志文件是chat.log。当chat.log达到指定的大小之后,RotatingFileHandler自动把 文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建 chat.log,继续输出日志信息。它的构造函数是:
    RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
    其中filename和mode两个参数和FileHandler一样。
    maxBytes用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。
    backupCount用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。

  4. logging.handlers.TimedRotatingFileHandler (日志文件时间滚动方式)
    这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就 自动创建新的日志文件。重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间。它的构造函数是:
    TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
    其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义。
    interval是时间间隔。
    when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:
    S 秒
    M 分
    H 小时
    D 天
    W 每星期(interval==0时代表星期一)
    midnight 每天凌晨

import logging

# create logger
logger = logging.getLogger('TEST-LOG')   #自定义名字
logger.setLevel(logging.DEBUG)       #最低等级是DEBUG

# create console handler and set level to debug
ch = logging.StreamHandler()          #屏幕的handler
ch.setLevel(logging.WARNING)          #设置hanlder的日志级别

# create file handler and set level to warning
fh = logging.FileHandler("access.log",encoding='utf-8')
fh.setLevel(logging.ERROR)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

#设置不同的handler的输出格式
# fh_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# fh.setFormatter(fh_formatter)

# add formatter to ch and fh
ch.setFormatter(formatter)
fh.setFormatter(formatter)

# add ch and fh to logger
logger.addHandler(ch)
logger.addHandler(fh)

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

十六、re 模块

常用正则表达式符号

'.'     默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行
'^'     匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","\nabc\neee",flags=re.MULTILINE)
'$'     匹配字符结尾,或e.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()也可以
'*'     匹配*号前的字符0次或多次,re.findall("ab*","cabb3abcbbac")  结果为['abb', 'ab', 'a']
'+'     匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb']
'?'     匹配前一个字符1次或0次
'{m}'   匹配前一个字符m次
'{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb']
'|'     匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC'
'(...)' 分组匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 结果 abcabca456c
 
 '[]'   匹配括号内的任意字符,但是需要需要括号内的‘^’表示非,而其他正则字符就表示字符,例如[(+*)] 表示匹配 '(','+','*',')'.
 
 
'\A'    只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的
'\Z'    匹配字符结尾,同$
'\d'    匹配数字0-9
'\D'    匹配非数字
'\w'    匹配[A-Za-z0-9]
'\W'    匹配非[A-Za-z0-9]
's'     匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t'
 
'(?P<name>...)' 分组匹配 re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict("city") 结果{'province': '3714', 'city': '81', 'birthday': '1993'}

最常用的匹配语法

re.match 从头开始匹配
re.search 匹配包含
re.findall 把所有匹配到的字符放到以列表中的元素返回
re.splitall 以匹配到的字符当做列表分隔符
re.sub      匹配字符并替换

反斜杠的困扰
与大多数编程语言相同,正则表达式里使用""作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"",那么使用编程语言表示的正则表达式里将需要4个反斜杠"\\":前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r"\"表示。同样,匹配一个数字的"\d"可以写成r"\d"。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。

仅需轻轻知道的几个匹配模式

re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同)
M(MULTILINE): 多行模式,改变'^'和'$'的行为(参见上图)
S(DOTALL): 点任意匹配模式,改变'.'的行为

例如:

#!/usr/bin/python
import re
 
line = "Cats are smarter than dogs";
 
matchObj = re.match( r'dogs', line, re.M|re.I)
if matchObj:
   print "match --> matchObj.group() : ", matchObj.group()
else:
   print "No match!!"
 
matchObj = re.search( r'dogs', line, re.M|re.I)
if matchObj:
   print "search --> matchObj.group() : ", matchObj.group()
else:
   print "No match!!"
   
执行结果:
No match!!
search --> matchObj.group() :  dogs

十七、uuid模块

uuid.uuid1() 设备唯一标识符,基于设备MAC、时间等参数

十八、动态导入模块

lib = __import__('lib.aa') 
# 看上去导入的是lib.aa,实际上此时导入的是lib,但是如果不写'lib.aa'而只写'lib'的话,则aa这个模块不能导入,使用递归的方法是找不到的;

print(lib)

obj = lib.aa.C()  #要调用C这个类,只能这样调用;若在import时,写的是'lib',那么此时就会报错。

上面的方法是解释器自己用的,不建议使用
官方建议使用的方法:

import importlib

aa = importlib.import_module('lib.aa')  #此处是直接导入了aa模块,在调用时直接使用aa即可;

print(aa)
obj = aa.C()    #不用写'lib.aa.C()'
posted @ 2018-03-05 17:26  耳_东  阅读(152)  评论(0)    收藏  举报