1.模块介绍
定义:
模块:是用来从逻辑上组织python 代码(变量,函数,类,逻辑:实现一个功能),本质就是.py 结尾的python 文件(文件名:test,py,对应的 模块名:test)
包:用来从逻辑上组织模块的,本质上就是一个目录,(必须带一个__init___.py文件)
导入方法:
import module_name
import module1_name ,module2_name
from module import *
from module import m1,m2,m3
from module import pandas as pd #导入模块优化
import sys
print(sys.path)
['C:\\Users\\Administrator\\PycharmProjects\\MyProject1\\day5-2018-01-24', 'C:\\Users\\Administrator\\PycharmProjects\\MyProject1', 'C:\\Python36-32\\python36.zip', 'C:\\Python36-32\\DLLs', 'C:\\Python36-32\\lib', 'C:\\Python36-32', 'C:\\Python36-32\\lib\\site-packages', 'C:\\Python36-32\\lib\\site-packages\\pip-9.0.1-py3.6.egg', 'C:\\Python36-32\\lib\\site-packages\\sqlalchemy-1.1.11-py3.6-win32.egg']s
import 本质:
import的本质就是,把导入的模块运行一遍(把python文件解释执行一遍 import test test='test.py all code' )。
import module_name ---> module_name.py ---> module_name.py 的路径 ---> sys.path
一般这个模块下都是定义的变量和函数,所以并不会直接运行这些函数,但是如果模块中有可运行的代码,也是会在import的时候被运行的
import包的本质就是,运行包下的__init__.py这个文件
__init__.py文件可以为空,一般为空。其实为空的话,也可以干脆不要这个文件。不过有可以文件做标记可以把这个文件夹和普通的文件夹区分开来。有这个文件,它就是一包。如果里面有内容,那么会在import 或者 from 这个包的时候,执行里面的内容。这种情况:import PackageA.SubPackageA.ModuleA 会执行每一层包里的每一个__init__.py文件。
但是并不是每次import都会执行一编模块或者__init__,只有在第一次import的时候才会执行。
__name__ 变量
要让你的文件既可以被作为包调用,又可以直接运行,但是不要在被调用的时候运行起来,需要用到__name__这个变量。
如果一个文件被作为程序运行的时候,__name__ == '__main__'
如果是作为模块被调用的时候,__name__ == '模块名' or '包名.模块名',取决于模块的位置
一般我们只需要判断是不是'__main__'。加上if判断,保证你文件中的这段代码在文件被作为模块import的时候,不会被执行。
经常使用if __name__ == '__main__',保证你写的文件既可以被import又可以独立运行,用于test。
示例1
编写一个简单的module.py
string = "This is module,py"
def sayhi():
print("hello ljohn!!")
def test():
return "test in module.py"
编写 导入module模块
import module
print(module.string)
module.sayhi()
示例2:
编写一个module1.py 文件,文件放置到最外层“MyProject1”,编写程序并导入该模块
string = "This is module1,py"
def sayhi():
print("hello ljohn!!")
def test():
return "test in module1.py"
编写程序并导入模块module1
import os,sys
print(sys.path)
# 找到项目最外层文件夹"MyProject1"
p = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(p) #添加至python系统环境变量
# 导入"MyProject1"下的module1
import module1
print(module1.string) #调用模块中的变量
module1.sayhi() # 调用模块中的函数
自定义模块 和开源模块的使用参考
2.time &datetime模块
时间相关的操作,时间有三种表示方式:
- 时间戳 1970年1月1日之后的秒,即:time.time()
- 格式化的字符串 2014-11-11 11:11, 即:time.strftime('%Y-%m-%d')
- 结构化时间 元组包含了:年、日、星期等... time.struct_time 即:time.localtime()
time模块
time.time() # 返回时间戳
time.sleep(n) # 停止n秒
time.gmtime(seconds) #返回一个元祖,UTC时间。参数是时间戳,默认当前时间即time.time()。一般都需要本地时间,
时间戳和时间元祖的互相转换:
time.localtime(seconds) # 时间戳转为元祖
time.mktime(tuple) # 元祖转为时间戳
时间元祖和时间字符串的互相转换:
time.strftime(format,tuple) # format是字符串的格式,后面的元祖可以省略,省略就是当前时间
time.strptime(string,format) # string是表示时间的字符串,format是这个字符串的格式
下面2个函数是分别将元祖和时间戳转为字符串,字符串格式不能自定义,只能是这种格式:'Sat Jun 06 16:26:11 1998'
time.asctime(tuple) # 缺省元祖就是当前时间
time.ctime(seconds) # 缺省时间戳就是当前时间,所以在缺省参数的情况下,和上面的结果一样
代码:
# -*- coding:utf-8 -*-
# Author:Ljohn
# Description:time && datetime
#time模块
import time
print(time.time()) #返回时间戳
#time.sleep(3) #停止3秒
print(time.clock()) #返回处理器时间
print(time.altzone) #返回与utc时间的时间差,以秒计算
print(time.asctime()) #返回时间格式Thu Jan 25 16:46:28 2018
print(time.localtime()) #返回本地时间 的struct time对象格式
print(time.gmtime(time.time()-800000)) #返回utc时间的struc时间对象格式
print(time.asctime(time.localtime())) #返回时间格式"Thu Jan 25 16:46:28 2018"
print(time.ctime()) #同上
#日期字符 转换 时间戳
string_2_string = time.strptime("2018/01/25","%Y/%m/%d") #将 日期字符串 转成 struct时间对象格式
print(string_2_string)
string_2_string = time.mktime(string_2_string) #将struct时间对象转成时间戳
print(string_2_string)
#将时间戳 转为 字符串格式
print(time.gmtime(time.time()-86400)) #将utc时间戳转换成struct_time格式
print(time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime())) #将utc struct_time格式转成指定的字符串格式
#datetime模块
# 时间加减
import datetime
print(datetime.datetime.now()) #返回 "2018-01-25 17:08:57.782458"
print(datetime.date.fromtimestamp(time.time())) # 时间戳直接转成日期格式 "2018-01-25"
print(datetime.datetime.now()) #"2018-01-25 17:08:57.782458"
print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天 "2018-01-28 17:12:52.765267"
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分钟
c_time= datetime.datetime.now() #"2018-01-25 17:47:46.092925"
print(c_time.replace(minute=3,hour=2)) #时间替换 "2018-01-25 02:03:46.092925"
参考:
3. ramdom
import random
print(random.random()) # 生成一个[0,1)范围的随机浮点数
print(random.uniform(1,2)) # 基本和上面一样,但是有参数可以指定区间
print(random.randint(1,3)) # 生成一个随机整数。如果a是参数1,b是参数2,结果是n,则a<=n<=b
print(random.randrange(1,7,2)) # 参数和range()一样,分别是开始、结束、步长,同样也不包括结束的值
print(random.choice([1,2,3,4,5])) # 参数可以是字符串、列表、元祖这样的序列
print(random.sample([1,2,3,4,5],3)) # 参数1和上面一样,参数2表示取多少位。如果参数2等于长度,那么结果也是随机排序的
生成随机验证码:
import random
checkcode = ''
for i in range(4):
current = random.randrange(0,4)
if current != i:
temp = chr(random.randint(65,90)) #对应Ascii字母(A-Z)
else:
temp = random.randint(0,9)
checkcode += str(temp)
print (checkcode)
View Code
4.os模块
提供对操作系统进行调用的接口
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("/server/python") 改变当前脚本工作目录"/server/python";相当于shell下cd
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下为"\t\n",Linux下为"\n"
os.pathsep 输出用于分割文件路径的字符串
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所指向的文件或者目录的最后修改时间
5.sys模块
sys.argv 命令行参数List,第一个元素是程序本身路径
sys.exit(n) 退出程序,正常退出时exit(0)
sys.version 获取Python解释程序的版本信息
sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform 返回操作系统平台名称
示例:
sys.stdout:平时用的print实际就是通过sys.stdout来输出的:
import sys
sys.stdout.write("Hello")
sys.stdout.write("Hello\n") # 这句和下面的print是一样的
print("Hello") # print实际也是在字符串后面加上换行后,再调用stdout.write
sys.stdin:平时用的input实际上就是先print一段内容,然后再捕获屏幕上的输入:
import sys
val = input('Hello')
# 上面的和下面的是一样的
print('Hello') # 这里的效果有的差别,print之后有个换行
val = sys.stdin.readline()[:-1] # 这里切掉了你最后敲的那个回车符
sys.stdout主要是可以用来重定向输出。可以从控制台重定向到文件,或者同时定向到控制台和文件,实现日志记录。
6.shutil模块
高级的文件、文件夹、压缩包处理模块
shutil.copyfileobj(fsrc, fdst[, length])
将文件内容拷贝到另一个文件中,可以部分内容
import shutil
with open('test.txt',encoding='utf-8') as f1,\
open('test2.txt','w',encoding='utf-8') as f2:
shutil.copyfileobj(f1,f2)
shutil.copyfile(src, dst)
拷贝文件,这个比较简单,直接输入原文件名和目标文件名就可以
import shutil
shutil.copyfile('test.txt','test3.txt')
其他方法:
shutil.copymode(src, dst)
仅拷贝权限。内容、组、用户均不变。就是linux里的chmod的权限(ugo权限)
shutil.copystat(src, dst)
拷贝状态的信息,包括:mode bits, atime, mtime, flags
shutil.copy(src, dst)
拷贝文件和权限。就是copyfile,然后copymod
shutil.copy2(src, dst)
拷贝文件和状态信息。就是copyfile,然后copystat
shutil.copytree(src, dst, symlinks=False, ignore=None)
递归的去拷贝文件
src:原目录
dst:目标目录
symlinks:这个默认就好,是处理链接的情况,目标目录里仍然是链接。如果改成True应该会把链接的文件拷贝过去
ignore:忽略哪些文件,让我想到了自动备份,有些扩展名或者目录我是不需要拷贝的。
shutil.copytree(src, dst, symlinks=False, ignore=None)
递归的去拷贝文件
shutil.rmtree(path[, ignore_errors[, onerror]])
递归的去删除文件
shutil.move(src, dst)
递归的移动文件
shutil.make_archive(base_name, format,...)
创建压缩包并返回文件路径,例如: zip、tar
- base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
- 如:log =>保存至当前路径
- 如:/tmp/log =>保存至/tmp
- format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
- root_dir: 要压缩的文件夹路径(默认当前目录)
- owner: 用户,默认当前用户
- group: 组,默认当前组
- logger: 用于记录日志,通常是logging.Logger对象
import shutil
res1 = shutil.make_archive("log", 'gztar', root_dir='/var/log') #将/var/log 压缩至当前目录,log.tar.gz
res2 = shutil.make_archive('/tmp/log','gztar',root_dir='/var/log') #将/var/log 压缩至/tmp目录下,log.tar.gz
7. shelve
shelve模块是一个简单的k,v将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式。
抄一个栗子:
import shelve
d = shelve.open('shelve_test') #打开一个文件
class Test(object):
def __init__(self,n):
self.n = n
t = Test(123)
t2 = Test(1234567)
name = ["Ljohn","Jack","Rose"]
d["test"] = name #持久化列表
d["t1"] = t #持久化类
d["t2"] = t2
d["str1"] = "abc"
d["int1"] = 123
d.close()
接着上面的生成的数据,通过下段代码将数据取回来。(用d.items()取回全部)
import shelve
d = shelve.open('shelve_test') #打开一个文件
print(d.get("test"))
print(d.get("str1"))
print(d.get("int1"))
d.close()
8. xml处理
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单。
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>
View Code
xml协议在各个语言里的都 是支持的,在python中可以用以下模块操作xml。
import xml.etree.cElementTree 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.cElementTree 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文档
# Description:创建一个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 = 'male'
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) # 打印生成的格式
9. yaml处理
Python也可以很容易的处理ymal文档格式,只不过需要安装一个模块,参考文档:http://pyyaml.org/wiki/PyYAMLDocumentation
10.ConfigParser模块
用于生成和修改常见配置文档,一般是ini扩展名。有时候扩展名也可能是.cfg、.conf、.txt,文本格式就是个纯文本文件,只是文本内容要遵循一定的格式当前模块的名称在 python 3.x 版本中变更为 configparser。
[Default]
serveraliveinterval = 45
compression = yes
compressionlevel = 9
login = yes
[ljohn.com]
user = liujian
[mail.ljohn.com]
smtp = smtp.163.com
port = 25
login = no
使用下面代码来生成一个这样的配置文件:
import configparser
config = configparser.ConfigParser()
config['Default'] = {
'ServerAliveInterval':'45',
'Compression':'yes',
'CompressionLevel':9 #字典里的数字可以不加引号,不过到文件里肯定还是一样的
}
config['ljohn.com'] ={} # 这里也可以先建个空的,然后再定义里面的值
config['ljohn.com']['User'] = 'liujian'
config['mail.ljohn.com'] = {}
mail = config['mail.ljohn.com'] # 每次的要打一串也很烦,所以这么弄
mail['smtp'] = 'smtp.163.com'
mail['port'] = '25'
mail['login'] = 'no'
config['Default']['login'] = 'yes'
with open('test.ini','w') as inifile:
config.write(inifile) #当前目录下新建test.ini并写入上面配置
View Code
读取配置文件
import configparser
config = configparser.ConfigParser() # 现在还是空的
print(config.sections()) # 读取配置文件,保存在config里
config.read('test.ini')
print(config.defaults())
print(config.sections()) # 打印节点,读到了2个,没有DEFAULT,也没有下面的详细内容
print(config['ljohn.com']['user']) # 读取具体的某一条配置信息
mail = config['mail.ljohn.com']
print(mail['login'])
for key in config['ljohn.com']:
print(key) # ljohn.com下面值定义了一个属性,但是这里继承了DEFAULT的所有属性
print(config['ljohn.com']['login']) # 这里继承了DEFAULT的属性,所以有值,是yes
View Code
11.hashlib
用于加密相关的操作,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法
import hashlib
m = hashlib.md5() # 别的格式也一样,只要替换这里的名字就好。比如:hashlib.sha1()
m.update(b'Hello')
print(m.digest()) # 2进制格式hash,也有16进制的方法
m.update(b"It's me") # update()是继续往里追加
print(m.digest())
m2 = hashlib.md5()
m2.update(b"HelloIt's me")
print(m2.digest()) # 这里的输出应该和上面一样
print(m2.hexdigest()) # 16进制格式hash,貌似16进制用的多
如果要进行运算的不是ascii码,就需要用encode转成bytes数据类型
import hashlib
# ######## md5 ########
hash = hashlib.md5()
hash.update('ljohn'.encode("utf-8")) # 数据类型必须是bytes
print(hash.hexdigest())
# ######## sha1 ########
hash = hashlib.sha1()
hash.update('admin'.encode("utf-8"))
print(hash.hexdigest())
# ######## sha256 ########
hash = hashlib.sha256()
hash.update('admin'.encode("utf-8"))
print(hash.hexdigest())
# ######## sha384 ########
hash = hashlib.sha384()
hash.update('admin'.encode("utf-8"))
print(hash.hexdigest())
######## sha512 ########
hash = hashlib.sha512()
hash.update('admin'.encode("utf-8"))
print(hash.hexdigest())
View Code
12. subprocess
很常用的模块,用到再解释。
13. logging模块
用来记录日志的,这个很有用,也很重要。
日志分为5个级别,重要等级一次降低是:critical、error、warning、info、debug
一个简单写日志的例子:
import logging
logging.basicConfig(filename='test.log',level=logging.INFO)
logging.warning('test warning')
logging.info('test info')
logging.debug('test debug')
注意:其中下面这句中的level=logging.INFO意思是,把日志纪录级别设置为INFO,也就是说,只有比日志是INFO或比INFO级别更高的日志才会被纪录到文件里,在这个例子, 第三条日志是不会被纪录的,如果希望纪录debug的日志,那把日志级别改成DEBUG就行了。
日志格式:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s 用户输出的消息
介绍一些常用的日志格式,下面我们给面的例子加上时间格式:
import logging
logging.basicConfig(filename='test1.log',
level=logging.INFO,
format='%(asctime)s %(module)s-%(lineno)d [%(levelname)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
logging.warning('test warnning')
logging.info('test info')
logging.debug('test debug')
加上时间后,只能输出到文件中,如果日志需要打印到前台使用如下类:
1. logger提供了应用程序可以直接使用的接口,同时把log同时打印在屏幕上和文件里;
2. handler将(logger创建的)日志记录发送到合适的目的输出;
3. filter提供了细度设备来决定输出哪条日志记录;
4. formatter决定日志记录的最终输出格式。
下面我们来给个例子:
import logging
#创建一个logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
#创建handler和输出级别
ch = logging.StreamHandler() # 输出到屏幕的handler
ch.setLevel(logging.INFO) # 输出级别和上面的忽略级别都不一样,可以看一下效果
fh = logging.FileHandler('access.log',encoding='utf-8') # 输出到文件的handler,定义一下字符编码
fh.setLevel(logging.WARNING)
# 创建日志格式,可以为每个handler创建不同的格式
ch_formatter = logging.Formatter('%(name)s %(asctime)s {%(levelname)s}:%(message)s',datefmt='%Y-%m-%d %H:%M:%S') # 关键参数datefmt自定义日期格式
fh_formatter = logging.Formatter('%(asctime)s %(module)s-%(lineno)d [%(levelname)s]:%(message)s',datefmt='%Y/%m/%d %H:%M:%S')
# 把上面的日志格式和handler关联起来
ch.setFormatter(ch_formatter)
fh.setFormatter(fh_formatter)
#将handler加入logger
logger.addHandler(ch)
logger.addHandler(fh)
# 输出日志
logger.debug('logger test debug')
logger.info('logger test info')
logger.warning('logger test warning')
logger.error('logger test error')
logger.critical('logger test critical')
上面的实例,可以满足基本日志功能。还有一个日志文件轮训的功能,只需要用另外一个模块重新定义一个fh就好了,就改1句
import logging
from logging import handlers # 需要额外导入这个模块,
# 还是要创建logger,这里不是必须的设置都省略了
logger = logging.getLogger(__name__)
#fh = logging.FileHandler('access.log',encoding='utf-8') # 原来的代码,替换为下面2种,一个是看时间,一个是看大小
#fh = handlers.TimedRotatingFileHandler(filename='access.log',when="S",interval=5,backupCount=3)
fh = handlers.RotatingFileHandler(filename='access.log',encoding='utf-8',maxBytes=100,backupCount=3)
fh_formatter = logging.Formatter('%(asctime)s %(module)s-%(lineno)d [%(levelname)s]:%(message)s',datefmt='%Y/%m/%d %H:%M:%S')
fh.setFormatter(fh_formatter)
logger.addHandler(fh)
# 以上就完成了,多输出几次
for i in range(10):
logger.critical('logger test critical%d'%i)
参数说明:
- interval:是时间间隔。
- when :参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:
- S: 秒
- M: 分
- H: 小时
- D: 天
- W :每星期(interval==0时代表星期一)
- midnight :每天凌晨,就是每天一个日志文件,很方便。
- maxBytes:用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生
- backupCount:用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。
14. re正则表达式
正则表达式,是非常重要的模块。不管什么语言会涉及到正则表达式。
常用的正则表达式符号
字符 描述
'.' 匹配除 "\n" 之外的任何单个字符。若指定flag DOTALL,则匹配任意字符,包括换行。
'^' 匹配输入字符串的开始位置。若指定flags MULTILINE,^ 也匹配 '\n' 或 '\r' 之后的位置。如:("^a","\nabc\neee",flags=re.MULTILINE)
'$' 匹配输入字符串的结束位置。若指定flags MULTILINE,$ 也匹配 '\n' 或 '\r' 之前的位置。
'*' 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。
'+' 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
'?' 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 。? 等价于 {0,1}。
'{n}' n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
'{n,}' n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
'{n,m}' m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。
'?' 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。
'x|y' 匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。
'[xyz]' 字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。
'[^xyz]' 负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'、'l'、'i'、'n'。
'[a-z]' 字符范围。匹配指定范围内的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。
'[^a-z]' 负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。
'\d' 匹配一个数字字符。等价于 [0-9]。
'\D' 匹配一个非数字字符。等价于 [^0-9]。
'\w' 匹配字母、数字、下划线。等价于'[A-Za-z0-9_]'。
'\W' 匹配非字母、数字、下划线。等价于 '[^A-Za-z0-9_]'。
'\A' 匹配字符开头,类似^,必须是字符串的开头,无法匹配'\n'之后的位置,忽略flags MULTILINE
'\Z' 匹配字符结尾,类似$,必须是字符串的结尾,无法匹配'\n'之前的位置,忽略flags MULTILINE
'\s' 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
'\S' 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
'\t' 匹配一个制表符。等价于 \x09 和 \cI。
'\v' 匹配一个垂直制表符。等价于 \x0b 和 \cK。
'\f' 匹配一个换页符。等价于 \x0c 和 \cL。
'\n' 匹配一个换行符。等价于 \x0a 和 \cJ。
'\r' 匹配一个回车符。等价于 \x0d 和 \cM。
分组匹配:
● '(...)' : 需要用group(),返回元祖
● '(?P<name>...)' :需要用groupdict(),返回字典。这里的'name'替换成你要定义的字典的Key
几个匹配模式:
● re.I (IGNORECASE):忽略大小写
● re.M(MULTLINE) :多行模式,主要影响^和$的匹配
● re.S(DOTALL) :点任意匹配模式,影响(.)点的匹配
上面的括号内是全拼,可以re.I这么写,也可以re.IGNORECASE这么写
常用的匹配语法:
- re.match:从头开始匹配
- re.search:匹配包含,一般都是用这个
- re.findall:把所有匹配到的字符放到列表中,以列表中的元素返回
- re.split:以匹配到的字符当做分隔符,返回列表
- re.sub:匹配字符并替换
示例:
1:match 和search 用法:
import re
test_match = re.match('abc','aabcde') #从头开始匹配
test_search = re.search('abc','aabcde') #匹配包含
print(test_match) #匹配不到,是因为不是abc开头
#print(test_match.group()) # 这句会报错,匹配不到就是None.没有group属性
print(test_search) # 匹配到了,包含abc字符串
print(test_search.group()) # 要返回匹配到的字符串使用group()
2:^ 、$ 和 \A、\Z 以及多行模式
在没有换行的情况下,两个的作用是一样的,就看一下有换行的情况。
import re
string1 = 'abc\ndef'
a1 = re.search('^def',string1)
print(a1) # 不开启多行模式,匹配不到
b1 = re.search('^def',string1,re.M)
print(b1) # 开启多行模式才能匹配到
c1 = re.search('\Adef',string1)
print(c1)
d1 = re.search('\Adef',string1,re.M)
print(d1) # 用\A会忽略多行模式,都是匹配不到的
string2 = 'abc\ndef\n'
a2 = re.search('def$',string2)
print(a2) # 这种有个换行符结尾的情况,$可以匹配到
b2 = re.search('def\Z',string2)
print(b2) # 这种有个换行符结尾的情况,\Z就匹配不到
c2 = re.search('abc$',string2)
print(c2)
d2 = re.search('abc$',string2,re.M)
print(d2) # 不过不是最后一个换行符,需要开启多行模式才能匹配
3:分组匹配
举一个身份证号的例子:
import re
id = '姓名:XXX ×××号码:320324199804242339 籍贯:XXX 职业:XXX'
a = re.search('(\d{6})(\d{4})(\d{2})(\d{2})\d{3}(\d|x)',id)
print(a)
print(a.group()) # 只是把×××号码提取出来
print(a.groups()) # 这里实现了分组,分别是地址码、年、月、日,中间3我没用小括号,性别。
import re
id = '姓名:XXX ×××号码:320324199804242339 籍贯:XXX 职业:XXX'
a = re.search('(?P<city>\d{6})(?P<Year>\d{4})(?P<Month>\d{2})(?P<Day>\d{2})\d{3}(?P<Sex>\d|x)',id)
print(a.groups()) # 用group输出还是一样
print(a.groupdict()) # 要用groupdict看字典
4:findall 和 split
import re
string = 'abc123abc123x0y9z8'
test_find = re.findall('\d+',string) # '\d+'是匹配连续的数字
test_split = re.split('\d+',string)
print(test_find) # 返回了所有的数字组合
print(test_split) # 把所有的数字作为分隔符,相当于返回了所有的字母组合。由于split的特性,最后是数字结尾的,最后会有一个空字符元素
test_find2 = re.findall('[^\d]+',string) # [^]是对中括号这的字符集合取反
print(test_find2) # 和上面一样,返回了所有的非数字组合
5:sub匹配并替换
用法:
- sub(pattern, repl, string, count=0, flags=0)
- pattern:要匹配的正则表达式
- repl:要替换的字符串
- string:带匹配和替换的字符串
- count:匹配和替换的次数,默认0全部匹配
- flags:3种匹配模式,默认不开启
import re
string = r"C:\Users\Public\Pictures\test.jpg"
string2 = re.sub(r'\\',r'/' ,string)
print(string2)
string3 = re.sub(r'\\',r'/',string,2)
print(string3)
text = "Alex is a goodboy , he is coll , clever , and so on..."
text2 = re.sub('\s+,\s+',',',text) # 把字符串中(,)逗号前后的空字符都去掉
print(text)
print(text2)
作业二:模拟计算器开发
实现加减乘除及拓号优先级解析
用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式后,必须自己解析里面的(),+,-,*,/符号和公式(不能调用eval等类似功能偷懒实现),运算后得出结果,结果必须与真实的计算器所得出的结果一致
补充说明:
- 练习正则的使用
- 先用正则找到()里的内容,把()计算出来替换掉
- 然后按级别,再解析乘除的运算
- 最后解析计算加减