Python基础学习(20)递归详解 shutil 模块 logging 模块
一、今日大纲
- 递归详解
- shutil 模块
- logging 模块
二、递归详解
针对之前的斐波那契数列Fibonacci Sequence
问题,经过不断思考,运用递归、生成器、循环等方法,总结出了以下四种方法:
-
递归方法1:
# 递归方法1 @log_interval def Fibonacci(n): def inner(args): if args == 1 or args == 0: return 1 return inner(args - 1) + inner(args - 2) r = inner(n) return r
-
递归方法2:
# 递归方法2 @log_interval def Fibonacci(n): def inner(args, a=1, b=1): if args == 0: return a a, b = b, a + b return inner(args - 1, a, b) return inner(n)
-
循环方法:
# 非递归方法 def Fibonacci(n): a = 1 b = 1 for i in range(n): a, b = b, a + b return a
-
生成器方法
# 生成器方法 def Fibonacci(n): i = 0 def inner(args): a = 1 yield a b = 1 for i in range(args): a, b = b, a + b yield a for i in inner(n): pass return i
为了计算比较下列方法的时间复杂度,设计了如下装饰器来比较四种方法的效率。
def read_dir(dirname):
# if os.listdir(dirname) == []:
# return
for i in os.listdir(dirname):
if os.path.isfile(os.path.join(dirname, i)):
print(i)
if os.path.isdir(os.path.join(dirname, i)):
read_dir(os.path.join(dirname, i))
嵌上装饰器后,我们分别向四个函数传入了不同的参数,下表是每个方法传输的不同参数以及所用时间:
传入参数 | 返回时间 | |
---|---|---|
递归方法1 | 30 | interval: 0.4075314998626709 |
递归方法2 | 50 | interval: 0.0010001659393310547 |
循环方法 | 50000 | interval: 0.044003963470458984 |
生成器方法 | 50000 | interval: 0.04400515556335449 |
根据上表我们可知,效率方面生成器方法 ≈ 循环方法 > 递归方法2 >> 递归方法1
。
递归方法1:将一个问题通过分治法分解成两个子问题,调用函数的次数也必将是最多的,所以其效率理所应当归为最低;
递归方法2:将方法1返回的两个子问题集合成了一个问题,大幅度地减少了函数的调用次数,性能产生了很大的提升,但是由于递归自身方法的限制,效率仍然不高;
循环方法:利用了和递归方法2同样的思想,有点类似于链表头插法保存Pre-Node
和Node
的思想,不断向下滚动生成所需的结果,由于只有一层循环,所以效率非常高。
生成器方法:利用刚刚学习的yield
构建生成器的方法,也可以只需要一层循环实现斐波那契数列的计算,本质上也属于循环方法,效率和上面的循环方法基本一致。
三、shutil 模块
shutil 模块对文件和文件集合提供了许多高级操作,特别是提供了支持文件复制和删除的函数。
-
拷贝文件
# `shutil.copy2(old_path, new_path)` shutil.copy2(r'D:PythonPython Projectday20lianjia.html', r'D:PythonPython Projectday21lianjia.html')
-
拷贝目录
# shutil.copytree(old_path, new_path, # ignore=shutil.ignore_patterns('*.pyc')) # ignore=shutil.ignore_patterns('*.py') 表示不要所有的py文件 shutil.copytree(r'D:PythonPython Projectday21day09', r'D:PythonPython Projectday21day09.bak', ignore=shutil.ignore_patterns('*.py'))
-
删除目录(慎用)
# shutil.rmtree(path, ignore_errors=True) # ignore_errors=True 表示无视删除的提示(比如文件正在占用) shutil.rmtree('day09', ignore_errors=True)
-
移动文件/目录
# shutil.move(old_path, new_path, copy_function=shutil.copy2)
-
获取磁盘使用空间
total, used, free = shutil.disk_usage('c:\') print('当前磁盘共: %iGB, 已使用: %iGB, 剩余: %iGB' %(total / 1073741824, used / 1073741824, free/1073741824)) # 1073741814 = 1024^3 # %i是一种表十进制的占位符和%d基本没有区别(找到了之后会写在这里)
-
压缩文件
# shutil.make_archive(compressed_file_path, 'zip', file_path) shutil.make_archive('day09_z', 'zip', 'day09.bak')
-
解压文件
# shutil.unpack_archive(compressed_file_path, 'zip', file_path) shutil.unpack_archive('day09_z.zip', 'day09_unz', 'zip')
四、logging 模块
-
日志的意义
- 用来排除错误
- 用来做数据分析
-
日志的意义——以网络购物商城为例
一个项目需要构建一个数据库存放足够重要的内容;而还有一些相对来说没那么重要的内容,我们没必要将它们放入数据库,这时,本地构建日志就成了我们的最佳选择。
如购物商城中:
数据库:什么时间买了什么商品、购物车中放置了哪些商品存入;
本地日志:用户登录时间、搜索记录、用户关闭时间、浏览商品清单等;
比如我们拥有 10W 客户,每天有 1W 条购买记录需要存放在数据库中,每天就会有10W+ 条和购买相关的信息由于数据库容量限制和网络传输限制,需要存放在本地日志中;我们进行数据分析、操作审计、排查BUG等情况下,均需要检查日志记录的情况,所以记录日志对于项目来说非常重要;而记录日志,就需要 logging 模块。
-
日志信息的等级
由于需要记录的信息众多,所以我们必须要对记录的内容进行重要性排序,logging 模块为我们提供了以下五种重要性:
# 输出内容是有等级的,重要性从上至下依次升高:默认处理warning级别以上的所有信息 # logging.debug('debug message') # logging.info('info message') # logging.warning('warning message') # logging.error('error message') # logging.critical('critical message')
logging 模块默认处理
warning
级别以上的所有信息,而我们可以通过logging.basicConfig(level=logging.DEBUG)
吧重要性调整为DEBUG
等级,调整为其它等级同理。 -
日志的设置
-
输出到屏幕
logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s[line : %(lineno)d] -%(module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S %p') logging.warning('warning test') # 2020-07-18 21:22:00 PM - root - WARNING[line : 66] -03 logging 模块: warning test
-
输出到文件
logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s[line : %(lineno)d] -%(module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S %p', filename='tmp.log', level=logging.DEBUG, filemode='a' ) logging.debug('debug test') logging.warning('warning test1') logging.warning('warning test2')
-
同时向屏幕和文件上输出
fh = logging.FileHandler('tmp.log', encoding='utf-8') # 定义log的编码类型和文件路径 sh = logging.StreamHandler() logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s[line : %(lineno)d] -%(module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S %p', level=logging.DEBUG, # handlers=[fh, sh] ) logging.warning('warning test1') logging.warning('warning test2') logging.warning('warning test3')
-
定时/定长记录日志
import time from logging import handlers # 按照大小做切割,每个日志1024字节,每五个替换一次 rh = handlers.RotatingFileHandler('myapp.log', maxBytes=1024, backupCount=5) # 按时间切,按秒切,只记录距今6s的记录 th = handlers.TimedRotatingFileHandler(filename='x2.log', when='s', interval=6, encoding='utf-8') sh = logging.StreamHandler() logging.basicConfig( handlers=[rh, sh, th], format='%(asctime)s - %(name)s - %(levelname)s[line : %(lineno)d] -%(module)s: %(message)s', datefmt="%Y-%m-%d %H:%M:%S %p", level=logging.DEBUG ) for i in range(19999): time.sleep(0.5) logging.warning('warning test1')
-