设为首页 收藏本站
查看: 1100|回复: 0

[经验分享] Python自动化开发学习5

[复制链接]

尚未签到

发表于 2018-8-11 06:49:07 | 显示全部楼层 |阅读模式
  模块
  在模块中,我们可以定义变量、函数,还有类(这个还没学到)。总之应该就是所有的代码。先建一个文件,取名为module,py,内容如下:
# 一下是module.py的内容  
string = "This is module,py"
  
def say_hi():
  
    print("Hi")
  
def test():
  
    return "test in module.py"
  在上面的模块中我们定义了1个变量和2个函数,现在我们要在另外一个文件中导入这个模块的一部分或者全部
  导入模块
  import 导入模块
import module  
print(module.string)
  
module.say_hi()
  上面就可以导入并且调用模块中的方法。
  如果需要导入多个模块,可以一次import全部导入,只需要用逗号隔开
import module_name1,module_name2,module_name3  from import 导入模块
from module import string,test  
print(string)
  
print(test())
  向上面这样,可以指定导入一部分,而不用全部导入。但是调用的方法也不同了,这里需要注意。
from module import *  使用*可以一次全部导入,但是这个用法不推荐使用。或者在模块中定义一个__all__的列表(这部分上课没讲),那么*只能导入all列表中存在的对象。
  之所以不推荐使用,主要还是调用的方法导致的问题。如果在调用的文件中也有一个同名的变量,就会替换掉import来的变量,比如下面这样:
from module import string,test  
string = "This is main"
  
def test():
  
    return "test in main"
  
print(string)
  
print(test())
  所以使用*这种不可控的导入方式,不推荐。但是还是没解决调用文件和导入文件里变量同名的问题,如果2边都不能改的话。这里还需要用到下面的as
  form import as 导入模块
from module import test as module_test  
def test():
  
    return "test in main"
  
print(test())
  
print(module_test())
  上面使用as起了别名后,就没有同名的问题了。不过这里没法同时导入多个
  导入优化(我不信!)
  用import导入文件的话,每次调用的时候都会去环境变量中查找一次该文件。如果多次调用,那么会降低程序运行的效率。
  用from import后,直接导入了函数的代码,每次调用就不必再去环境变量中查找变量了,这样程序运行效率就会高一点
  包、导入包
  在创建许多模块后,我们需要将某些功能相近的文件组织在同一文件夹下,这就是一个包。
  python包就是,一个有层次的文件目录结构,一个包含__init__.py 文件的目录,该目录下一定得有这个__init__.py文件和其它模块或子包。
  从包中导入模块的方法和导入模块一样:
import PackageA.SubPackageA.ModuleA  # 使用时必须用全路径名  
from PackageA.SubPackageA import ModuleA  # 可以直接使用模块名而不用加上包前缀
  
from PackageA.SubPackageA.ModuleA import functionA  # 直接导入模块中的函数
  也可以用*,但是要这样用,必须在包下的__init__.py文件中,定义好__all__列表,包的情况这个变量不能省略,否则不能用*
from pacakge import *  另外直接import一个包,是不会导入包下的模块的。
import PackageA  除非你编辑一下PackageA下的__init__.py文件,文件里加一行
from . import ModuleA  上面的.表示__init__.py所在的当前目录,也可以用..表示上一级目录。import不能用*(定义了__all__也没用),必须指定模块名。调用的时候还是要Packagea.ModuleA.functionA(),因为上面那句是加在__init__里的。
  但是还是不要这么用了,上课也没完全讲清楚。
  import 的本质
  import的本质就是,把导入的模块运行一遍。
  一般这个模块下都是定义的变量和函数,所以并不会直接运行这些函数,但是如果模块中有可运行的代码,也是会在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。
  内置模块
  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)  #  缺省时间戳就是当前时间,所以在缺省参数的情况下,和上面的结果一样
  datetime模块
  这个主要就将了2个函数:
import datetime  
print(datetime.datetime.now())  # 获取当前时间
  
print(datetime.timedelta(3))  # 先看一下效果,不是这么用的
  
print(datetime.timedelta(2,3,4,5,6,7,1))  # 一共7个参数,天、微妙、毫秒、秒、分、小时、周
  timedelta一般是结合now来计算一个过去或者将来的时间的:
import datetime  
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=4))  # 4小时后的时间
  
print(datetime.datetime.now() + datetime.timedelta(minutes=5))  # 5分钟后的时间
  random模块
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  
list1 = [1,2,3,4,5]
  
print(list1)
  
random.shuffle(list1)  # 将参数里的变量随机排序了
  
print(list1)  # 看新的结果
  random可以用来生成随机验证码,下面的例子只是应用一下这个模块,验证码功能还不够好:
import random  
checkcode = ''
  
for i in range(4):
  
    current = random.randint(97,122)  # ASCII表中这个范围是小写字母
  
    checkcode = &quot;%s%s&quot;%(checkcode,chr(current))  # 用chr将数字根据ASCII转成字母
  
print(checkcode)
  os模块
  提供对操作系统进行调用的接口

  •   os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
  • os.chdir(&quot;dirname&quot;)  改变当前脚本工作目录;相当于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(&quot;oldname&quot;,&quot;newname&quot;)  重命名文件/目录
  • os.stat('path/filename')  获取文件/目录信息
  • os.sep    输出操作系统特定的路径分隔符,win下为&quot;\\&quot;,Linux下为&quot;/&quot;
  • os.linesep    输出当前平台使用的行终止符,win下为&quot;\t\n&quot;,Linux下为&quot;\n&quot;
  • os.pathsep    输出用于分割文件路径的字符串
  • os.name    输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
  • os.system(&quot;bash command&quot;)  运行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所指向的文件或者目录的最后修改时间
  sys模块

  • sys.argv           命令行参数List,第一个元素是程序本身路径
  • sys.exit(n)        退出程序,正常退出时exit(0)
  • sys.version        获取Python解释程序的版本信息
  • sys.maxint         最大的Int值
  • sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
  • sys.platform       返回操作系统平台名称
  sys.stdout:平时用的print实际就是通过sys.stdout来输出的
import sys  
sys.stdout.write(&quot;Hello&quot;)
  
sys.stdout.write(&quot;Hello\n&quot;)  # 这句和下面的print是一样的
  
print(&quot;Hello&quot;)  # print实际也是在字符串后面加上换行后,再调用stdout.write
  sys.stdin:平时用的input实际上就是先print一段内容,然后再捕获屏幕上的输入:
import sys  
val = input('Hello')
  
# 上面的和下面的是一样的
  
print('Hello')  # 这里的效果有的差别,print之后有个换行
  
val = sys.stdin.readline()[:-1]  # 这里切掉了你最后敲的那个回车符
  sys.stdout主要是可以用来重定向输出。可以从控制台重定向到文件,或者同时定向到控制台和文件(这个貌似有点复杂),实现日志记录。
  同样的sys.stdin和sys.stderr也应该可以重定向,
  shutil模块
  高级的文件、文件夹、压缩包处理模块
  shutil.copyfileobj(fsrc, fdst[, length])
  将文件内容拷贝到另一个文件中。这里只是拷贝文件的内容,所以要把2个文件先打开。参数1和参数2是这两个文件的句柄。
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.ignore_patterns(*patterns)
  这个略...
  shutil.copytree(src, dst, symlinks=False, ignore=None)
  递归的去拷贝文件
  src:原目录
  dst:目标目录
  symlinks:这个默认就好,是处理链接的情况,目标目录里仍然是链接。如果改成True应该会把链接的文件拷贝过去
  ignore:忽略哪些文件,让我想到了自动备份,有些扩展名或者目录我是不需要拷贝的。
  shutil.rmtree(path[, ignore_errors[, onerror]])
  递归的去删除文件
  shutil.move(src, dst)
  递归的移动文件
  shutil.make_archive(base_name, format,...)
  创建压缩包并返回文件路径

  •   base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
      如:www                        =>保存至当前路径
      如:/Users/wupeiqi/www =>保存至/Users/wupeiqi/
  •   format:压缩包种类,“zip”, “tar”, “bztar”,“gztar”
  •   root_dir:要压缩的文件夹路径(默认当前目录)
  •   owner:用户,默认当前用户
  •   group:组,默认当前组
  •   logger:用于记录日志,通常是logging.Logger对象
import shutil  
res = shutil.make_archive('test','gztar','TXT')
  
print(res)
  当前文件夹下有一个名为TXT的文件夹,将这个文件夹打包压缩,在当前目录下生成了一个test.tar.gz的压缩文件
  json 和 pickle 模块
  用户序列化的两个模块,之前的课已经将过了

  •   json,用于字符串 和 python数据类型间进行转换
  •   pickle,用于python特有的类型 和 python的数据类型间进行转换
  两个模块都提供了名字一样的四个功能,dumps、dump、loads、load,效果也差不多(适用范围不同)。
  shelve模块
  一个简单的key,value将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式
  直接抄个例子吧。
import shelve  
d = shelve.open('shelve_test') #打开一个文件
  
class Test(object):
  
    def __init__(self,n):
  
        self.n = n
  
t = Test(123)
  
t2 = Test(123334)
  
name = [&quot;alex&quot;,&quot;rain&quot;,&quot;test&quot;]
  
d[&quot;test&quot;] = name #持久化列表
  
d[&quot;t1&quot;] = t      #持久化类
  
d[&quot;t2&quot;] = t2
  
d[&quot;str1&quot;] = &quot;abc&quot;
  
d[&quot;int1&quot;] = 123
  
d.close()
  取回数据:
  接着上面的例子,把数据都取回。另外可以用d.items()取回全部。
import shelve  
d = shelve.open('shelve_test') #打开一个文件
  
print(d.get(&quot;test&quot;))
  
print(d.get(&quot;str1&quot;))
  
print(d.get(&quot;int1&quot;))
  
d.close()
  xml模块
  古时候用的,以前没有JSON,现在有了,遇到了再去查吧
  PyYAML
  这不是一个标准库,需要的时候还得去下载。处理yaml文档格式,这种格式主要是做配置文件用的
  参考文档:http://pyyaml.org/wiki/PyYAMLDocumentation
  configparser模块
  用于生产和修改配置文档,一般是ini扩展名。有时候扩展名也可能是.cfg、.conf、.txt,文本格式就是个纯文本文件,只是文本内容要遵循一定的格式。
  这也是一种常见的格式,来看一个好多软件的常见文档格式如下:
  [DEFAULT]
  ServerAliveInterval = 45
  Compression = yes
  CompressionLevel = 9
  ForwardX11 = yes
  [bitbucket.org]
  User = hg
  [topsecret.server.com]
  Port = 50022
  ForwardX11 = no
  下面用代码来生成一个这样的配置文件:
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'  # 这里必须是字符串,因为不支持数字格式
  
topsecret['ForwardX11'] = 'no'
  
config['DEFAULT']['ForwardX11'] = 'yes'
  
with open('test.ini','w') as inifile:
  
    config.write(inifile)
  下面用代码来取出配置:
import configparser  
config = configparser.ConfigParser()
  
print(config.sections())  # 现在还是空的
  
config.read('test.ini')  # 读取配置文件,保存在config里
  
print(config.defaults())
  
print(config.sections())  # 打印节点,读到了2个,没有DEFAULT,也没有下面的详细内容
  
print(config['bitbucket.org']['user'])  # 读取具体的某一条配置信息
  
topsecret = config['topsecret.server.com']  # 还是嫌名字太长,很麻烦,就这么弄
  
print(topsecret['forwardx11'])
  
for key in config['bitbucket.org']:  # 节点会继承DEFAULT的属性,没有就继承,有就替代
  
    print(key)  # bitbucket.org下面值定义了一个属性,但是这里继承了DEFAULT的所有属性
  
print(config['bitbucket.org']['forwardx11'])  # 这里继承了DEFAULT的属性,所以有值,是yes
  当然还可以对配置文件进行增删改查,真要改的时候再说吧,一般都是手动修改的。
  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&quot;It's me&quot;)  # update()是继续往里追加
  
print(m.digest())
  
m2 = hashlib.md5()
  
m2.update(b&quot;HelloIt's me&quot;)
  
print(m2.digest())  # 这里的输出应该和上面一样
  
print(m2.hexdigest())  # 16进制格式hash,貌似16进制用的多
  如果要进行运算的不是ascii码,就需要用encode转成bytes数据类型
import hashlib  
m = hashlib.sha512()  # 试个sha512的
  
m.update('你好'.encode(encoding='utf-8'))  # 数据类型必须是bytes
  
print(m.hexdigest())
  下面这个是可以进一步加密的模块,更高的安全性。
  hmac模块
  散列消息鉴别码,简称HMAC,是一种基于消息鉴别码MAC(Message Authentication Code)的鉴别机制。使用HMAC时,消息通讯的双方,通过验证消息中加入的鉴别密钥Key来鉴别消息的真伪。
  一般用于网络通信中消息加密,前提是双方先要约定好key,就像接头暗号一样,然后消息发送方用key把消息加密,接收方用key + 消息明文再加密,拿加密后的值跟发送者的相对比是否相等,这样就能验证消息的真实性,及发送者的合法性了。
  假设约定的key是“一二三四五”,我们要发一条消息“上山打老虎”:
import hmac  
# 可以直接把key和消息一起生成hash
  
h_send= hmac.new(&quot;一二三四五&quot;.encode(encoding='utf-8'),&quot;上山打老虎&quot;.encode(encoding='utf-8'))
  
print(h_send.hexdigest())
  
# 也可以先把key导入
  
h_receive = hmac.new(&quot;一二三四五&quot;.encode(encoding='utf-8'))
  
# 然后再用update()方法,把消息导入,生成hash
  
h_receive.update(&quot;上山打老虎&quot;.encode(encoding='utf-8'))
  
print(h_receive.hexdigest())  # 和上面的hash值是一样的
  
# 还有第三个参数,上面缺省了,默认是md5格式,如果要用别的格式,就补上参数
  
h_send= hmac.new(&quot;一二三四五&quot;.encode(encoding='utf-8'),&quot;上山打老虎&quot;.encode(encoding='utf-8'),'sha1')  # 使用sha1加密
  
print(h_send.hexdigest())
  
h_receive = hmac.new(&quot;一二三四五&quot;.encode(encoding='utf-8'),digestmod='sha1')  # 这里没有第二个参数,貌似只能用关键参数
  
h_receive.update(&quot;上山打老虎&quot;.encode(encoding='utf-8'))
  
print(h_receive.hexdigest())  # 和上面的hash值是一样的
  hmac的应用:
  hmac主要应用在身份验证是,它的使用方法是这样的:

  •   客户端发出登录请求(假设是浏览器的GET请求)
  •   服务器返回一个随机值,并在会话中记录这个随机值
  •   客户端将该随机值作为密钥,用户密码进行hmac运算,然后提交给服务器
  •   服务器读取用户数据库中的用户密码和步骤2中发送的随机值做与客户端一样的hmac运算,然后与用户发送的结果比较,如果结果一致则验证用户合法
  散列算法仅适用于登录验证,但是对于最初的密码设置和以后密码修改的过程不适用。但是散列算法要比对称和非对称加密算法效率高。
  加密的总结:上面的2个加密模块,应该都不是用来加密传数据的,因为加密后并不能解密,只是生成一个消息摘要,用来验证消息的完整性的。也可以理解为对消息生成一个数字签名,如果签名一致,则认为消息没有被修改过。
  subprocess模块
  运行linux的shelll命令,管理子进程。是对这些命令的替换 os.system 和 os.spawn* 。所以尽量用subprocess。没有展开讲
  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')
  去看一下文件的内容,应该只有2行。因为参数level设置了只接收info等级及以上的日志,所以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
  用户输出的消息
  下面的例子选了那些比较有用的格式,有日期、时间、模块名、代码行、日志级别和消息。
logging.basicConfig(filename='test.log',  
                    level=logging.DEBUG,
  
                    format='%(asctime)s %(module)s-%(lineno)d [%(levelname)s]:%(message)s',
  
                    datefmt='%Y-%m-%d %H:%M:%S')
  
logging.warning('test warning')
  
logging.info('test info')
  
logging.debug('test debug')
  默认的设置就很好用,但是只能输出到文件。如果想同时把log同时打印在屏幕上和文件里,需要自己创建一个logger。贴一点基础知识
  Python 使用的logging模块记录日志涉及四个主要类,使用官方文档中的概括最为合适:

  •   logger提供了应用程序可以直接使用的接口;
  •   handler将(logger创建的)日志记录发送到合适的目的输出;
  •   filter提供了细度设备来决定输出哪条日志记录;
  •   formatter决定日志记录的最终输出格式。
  每个类的介绍就不贴了,直接上例子:
import logging  
# 先创建一个logger
  
logger = logging.getLogger(__name__)  # 定义Logger的名字,之前直接用logging调用的名字是root,日志格式用%(name)s可以获得。这里的名字也可以自定义比如&quot;TEST&quot;
  
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')
  上面这个例子据说能满足90%的需求了。还需要一个日志文件轮训的功能。只需要用另外一个模块重新定义一个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=&quot;S&quot;,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并不会被更名,而是被删除。
  以上号称是能满足95%的需求了(我信了!)。剩下的是日志过滤,要用到四个类里的filter,号称很复杂,且用的不多,就没讲。
  re模块
  正则表达式,很重要的模块。
  常用的正则表达式符号
字符描述'.'  匹配除 &quot;\n&quot; 之外的任何单个字符。若指定flag DOTALL,则匹配任意字符,包括换行。
'^'  匹配输入字符串的开始位置。若指定flags MULTILINE,^ 也匹配 '\n' 或 '\r' 之后的位置。如:(&quot;^a&quot;,&quot;\nabc\neee&quot;,flags=re.MULTILINE)
'$'  匹配输入字符串的结束位置。若指定flags MULTILINE,$ 也匹配 '\n' 或 '\r' 之前的位置。
'*'  匹配前面的子表达式零次或多次。例如,zo* 能匹配 &quot;z&quot; 以及 &quot;zoo&quot;。* 等价于{0,}。
'+'  匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 &quot;zo&quot; 以及 &quot;zoo&quot;,但不能匹配 &quot;z&quot;。+ 等价于 {1,}。
'?'  匹配前面的子表达式零次或一次。例如,&quot;do(es)?&quot; 可以匹配 &quot;do&quot; 或 &quot;does&quot; 。? 等价于 {0,1}。
'{n}'  n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 &quot;Bob&quot; 中的 'o',但是能匹配 &quot;food&quot; 中的两个 o。
'{n,}'  n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 &quot;Bob&quot; 中的 'o',但能匹配 &quot;foooood&quot; 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
'{n,m}'  m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,&quot;o{1,3}&quot; 将匹配 &quot;fooooood&quot; 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。
'?'  当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 &quot;oooo&quot;,'o+?' 将匹配单个 &quot;o&quot;,而 'o+' 将匹配所有 'o'。
'x|y'  匹配 x 或 y。例如,'z|food' 能匹配 &quot;z&quot; 或 &quot;food&quot;。'(z|f)ood' 则匹配 &quot;zood&quot; 或 &quot;food&quot;。
'[xyz]'  字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 &quot;plain&quot; 中的 'a'。
'[^xyz]'  负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 &quot;plain&quot; 中的'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:匹配字符并替换
  例子:
  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()
  ^ 、$ 和 \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)  # 不过不是最后一个换行符,需要开启多行模式才能匹配
  分组匹配
  举一个×××号码的例子
import re  
id = '姓名:XXX ×××号码:31010119990919935x 籍贯: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我没用小括号,性别。
  上面是元祖的形式返回,还可以用字典返回,需要定义Key
import re  
id = '姓名:XXX ×××号码:31010119990919935x 籍贯: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看字典
  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)  # 和上面一样,返回了所有的非数字组合
  sub 匹配并替换
  sub(pattern, repl, string, count=0, flags=0)
  pattern:要匹配的正则表达式
  repl:要替换的字符串
  string:带匹配和替换的字符串
  count:匹配和替换的次数,默认0全部匹配
  flags:3种匹配模式,默认不开启
import re  
string = r&quot;C:\Users\Public\Pictures\test.jpg&quot;
  
string2 = re.sub(r'\\',r'/',string)  # 将\替换成/
  
print(string2)
  
string3 = re.sub(r'\\',r'/',string,2)  # 规定只替换2次,默认是0全部替换
  
print(string3)
  
text = &quot;Alex is a goodboy , he is coll , clever , and so on...&quot;
  
text2 = re.sub('\s+,\s+',',',text)  # 把字符串中(,)逗号前后的空字符都去掉
  
print(text)
  
print(text2)
  作业
  作业一:ATM+购物商城程序
  额度 15000或自定义
  实现购物商城,买东西加入购物车,调用信用卡接口结账
  可以提现,手续费5%
  支持多账户登录
  支持账户间转账
  记录每月日常消费流水
  提供还款接口
  ATM记录操作日志
  提供管理接口,包括添加账户、用户额度,冻结账户等。。。
  用户认证用装饰器
  补充说明:

  •   使用软件开发目录规范来组织目录结构,存放代码、配置文件和数据
  •   用户认证要用装饰器,验证登录状态要在每一个方法里都能用,这里要用装饰器。登录成功后会生成一个用户信息的全局变量,每次只要去调用一个这个全局变量就能验证用户的登录状态
  •   购物商城之前的作业已经做过,可以直接拿来稍微改一下后使用
  作业二:模拟计算器开发
  实现加减乘除及拓号优先级解析
  用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式后,必须自己解析里面的(),+,-,*,/符号和公式(不能调用eval等类似功能偷懒实现),运算后得出结果,结果必须与真实的计算器所得出的结果一致
  补充说明:

  •   练习正则的使用
  •   先用正则找到()里的内容,把()计算出来替换掉
  •   然后按级别,再解析乘除的运算
  •   最后解析计算加减

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-549811-1-1.html 上篇帖子: Python自动化开发学习3 下篇帖子: Python入门篇(六)之函数
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表