函数基础 函数高级应用 模块

19 篇文章 2 订阅
函数基础
创建函数
def语句
前向引用
调用函数
函数操作符
关键字参数
参数组
匿名函数
lambda
filter函数
map函数

函数基础

创建函数

def 语句

• 函数用def语句创建,语法如下:
def function_name(arguments):
    "function_documentation_string"
    function_body_suite
• 标题行由def关键字,函数的名字,以及参数的集合(如果有的话)组成 
• def 子句的剩余部分包括了一个虽然可选但是强烈推 荐的文档字串,和必需的函数体
# 练习
函数创建没有先后关系,只要在调用时,所有的函数已经存在即可
>>> def foo():
...     print('in foo')
...     bar()
... 
>>> def bar():
...     print('in bar')
... 
>>> foo()
in foo
in bar

前向引用

• 函数不允许在函数未声明之前对其进行引用或者调用
def foo():
    print('in foo')
    bar()
foo()			#报错,因为bar没有定义
--------------------------------------
def foo():
    print('in foo')
    bar()
    
def bar():
    print('in	bar’)
    foo()		#正常执行,虽然bar的定义在foo定义后面

调用函数

函数操作符

• 使用一对圆括号()调用函数,如果没有圆括号,只是对函数的引用 
• 任何输入的参数都必须放置在括号中
>>>	def foo():
	print('Hello world!')
...
>>>	foo()
Hello world!
>>>	foo
<function foo at 0x7f18ce311b18>

关键字参数

• 关键字参数的概念仅仅针对函数的调用 
• 这种理念是让调用者通过函数调用中的参数名字来区分参数 
• 这样规范允许参数缺失或者不按顺序
>>>	def get_info(name, age):
	print("%s's	age	is	%s"	% (name, age))
...
>>>	get_info(23, 'bob')
23's age is	bob
>>>	get_info(age = 23, name	= 'bob')
bob's age is 23
# 练习
    - 只有一个参数名,称作位置参数
    - 参数形式是key=val,称作关键字参数
>>> def get_age(name, age):
...     print('%s is %s years old' % (name, age))
... 
>>> get_age('bob', 20)   # OK
bob is 20 years old
>>> get_age()   # Error,参数太少
>>> get_age('bob', 20, 100)   # Error,参数太多
>>> get_age(20, 'bob')  # 语法没有问题,但是语义不对
20 is bob years old
>>> get_age(age=20, name='bob')  # OK
bob is 20 years old
>>> get_age(age=20, 'bob')  # Error,关键字参数必须在后
>>> get_age(20, name='bob')  # Error,name得到了多个值
>>> get_age('bob', age=20)   # OK
bob is 20 years old

参数组

• python允许程序员执行一个没有显式定义参数的函数
• 相应的方法是通过一个把元组(非关键字参数)或字典(关键字参数)作为参数组传递给函数
func(*tuple_grp_nonkw_args,	**dict_grp_kw_args)
# 练习
如果函数的参数个数是不固定的,可以使用参数组接收参数
    - 在参数名前加\*表示参数是元组
    - 在参数名前加\*\*表示参数是字典
>>> def func1(*args):
...     print(args)
... 
>>> func1()
()
>>> func1('bob')
('bob',)
>>> func1('bob', 123)
('bob', 123)
>>> func1('bob', 123, 'hello', 'aaa')
('bob', 123, 'hello', 'aaa')
>>> def func2(**kwargs):
...     print(kwargs)
... 
>>> func2()
{}
>>> func2(name='bob', age=20)
{'name': 'bob', 'age': 20}

调用函数时,如果参数有\*号,表示把参数拆开。
>>> def add(x, y):
...     print(x + y)
... 
>>> nums = [10, 20]
>>> num_dict = {'x': 100, 'y': 200}
>>> add(nums)  # Error,因为nums传给了x,y没有得到值
>>> add(num_dict)  # Error,同上
>>> add(*nums)  # nums拆成了10, 20
30
>>> add(**num_dict)  # 拆成了x=100, y=200
300

简单的加减法数学游戏

1. 随机生成两个100以内的数字
2. 随机选择加法或是减法
3. 总是使用大的数字减去小的数字
4. 如果用户答错三次,程序给出正确答案
# 练习 1
from random import randint, choice

def exam():
    nums = [randint(1, 100) for i in range(2)]  # 生成两个数字
    nums.sort(reverse=True)  # 降序排列
    op = choice('+-')
    if op == '+':
        result = nums[0] + nums[1]
    else:
        result = nums[0] - nums[1]

    prompt = '%s %s %s = ' % (nums[0], op, nums[1])  # 5 + 3 =
    counter = 0

    while counter < 3:
        try:
            answer = int(input(prompt))
        except:   # 不指定异常可以捕获所有异常,但是不推荐
            print()
            continue

        if answer == result:
            print('Very Good!!!')
            break

        print('Wrong Answer!!!')
        counter += 1
    else:  # 三次全答错,才会输出正确答案
        print('%s%s' % (prompt, result))

if __name__ == '__main__':
    while True:
        exam()
        # 删除用户输入的多余空格,并取出第1个字符
        try:
            yn = input('Continue(y/n)? ').strip()[0]
        except IndexError:
            yn = 'y'
        except (KeyboardInterrupt, EOFError):
            yn = 'n'

        if yn in 'nN':
            print('\nBye-bye')
            break
# 练习 2
from random import randint, choice

def add(x, y):
    return x + y

def sub(x, y):
    return x - y

def exam():
    cmds = {'+': add, '-': sub}
    nums = [randint(1, 100) for i in range(2)]  # 生成两个数字
    nums.sort(reverse=True)  # 降序排列
    op = choice('+-')
    result = cmds[op](*nums)

    prompt = '%s %s %s = ' % (nums[0], op, nums[1])  # 5 + 3 =
    counter = 0

    while counter < 3:
        try:
            answer = int(input(prompt))
        except:
            print()
            continue

        if answer == result:
            print('Very Good!!!')
            break

        print('Wrong Answer!!!')
        counter += 1
    else:
        print('%s%s' % (prompt, result))

if __name__ == '__main__':
    while True:
        exam()
        try:
            yn = input('Continue(y/n)? ').strip()[0]
        except IndexError:
            yn = 'y'
        except (KeyboardInterrupt, EOFError):
            yn = 'n'

        if yn in 'nN':
            print('\nBye-bye')
            break
# 练习 3
from random import randint, choice

def exam():
    cmds = {'+': lambda x, y: x + y, '-': lambda x, y: x - y}
    nums = [randint(1, 100) for i in range(2)]  # 生成两个数字
    nums.sort(reverse=True)  # 降序排列
    op = choice('+-')
    result = cmds[op](*nums)

    prompt = '%s %s %s = ' % (nums[0], op, nums[1])  # 5 + 3 =
    counter = 0

    while counter < 3:
        try:
            answer = int(input(prompt))
        except:
            print()
            continue

        if answer == result:
            print('Very Good!!!')
            break

        print('Wrong Answer!!!')
        counter += 1
    else:
        print('%s%s' % (prompt, result))

if __name__ == '__main__':
    while True:
        exam()
        try:
            yn = input('Continue(y/n)? ').strip()[0]
        except IndexError:
            yn = 'y'
        except (KeyboardInterrupt, EOFError):
            yn = 'n'

        if yn in 'nN':
            print('\nBye-bye')
            break

匿名函数

lambda

• python允许用lambda关键字创造匿名函数 
• 匿名是因为不需要以标准的def方式来声明 
• 一个完整的lambda“语句”代表了一个表达式,这个表达式的定义体必须和声明放在同一行
lambda [arg1[, arg2, ...argN]]: expression
>>>	a =	lambda x, y: x + y
>>>	print(a(3, 4))
7
• 如果函数的代码块非常简单,只有一行,可以使用匿名函数。
• 匿名函数使用lambda关键字定义。
>>> def add(x, y):
...     return x + y
... 
>>> lambda x, y: x + y
<function <lambda> at 0x7f0b96735c80>
>>> 
>>> myadd = lambda x, y: x + y
>>> myadd(10, 20)
30

filter()函数

filter(func, seq):调用一个布尔函数func来迭代遍历每个序列中的元素;返回一个使func返回值为true的元素的序列 
• 如果布尔函数比较简单,直接使用lambda匿名函数 就显得非常方便了
>>>	data = filter(lambda x:	x %	2, [num for num in range(10)])
>>>	print(list(data)) #过滤出10以内的奇数
[1,	3, 5, 7, 9]
# 理论
filter函数可以接受两个参数,第一个参数是函数,它必须返回真或假;第二个参数是个可迭代对象。filter的作用是,将第二个参数中的每一项交给第一个参数(函数)处理,返回值为真保留,否则过滤。
from random import randint

def func1(x):
    return True if x > 50 else False

if __name__ == '__main__':
    nums = [randint(1, 100) for i in range(10)]
    print(nums)
    result = filter(func1, nums)
    print(list(result))
    result2 = filter(lambda x: True if x > 50 else False, nums)
    print(list(result2))

map()函数

map(func, seq):调用一个函数func来迭代遍历每个 序列中的元素;返回一个经过func处理过的元素序列 
• 如果布尔函数比较简单,直接使用lambda匿名函数就显得非常方便了
>>>	data = map(lambda x: x*2+1,	[num for num in range(10)])
>>>	list(data)
[1,	3, 5, 7, 9,	11,	13,	15,	17,	19]
# 理论
map函数可以接受两个参数,第一个参数是函数,第二个参数是个可迭代对象。将可迭代对象中的每项交由map处理,处理的结果保存下来。
from random import randint

def func1(x):
    return True if x > 50 else False

def func2(x):
    return x + 2

if __name__ == '__main__':
    nums = [randint(1, 100) for i in range(10)]
    print(nums)
    result = map(func2, nums)
    print(list(result))
    result2 = map(lambda x: x + 2, nums)
    print(list(result2))
函数高级应用
变量作用域
全局变量
局部变量
global语句
名字空间
函数式编程
偏函数
递归函数
生成器

函数高级应用

变量作用域

全局变量

• 标识符的作用域是定义为其声明在程序里的可应用范围,也就是变量的可见性 
• 在一个模块中最高级别的变量有全局作用域 
• 全局变量的一个特征是除非被删除掉,否则它们的存活到脚本运行结束,且对于所有的函数,他们的值都是可以被访问的
• 在函数外面定义的变量。从定义起始位置开始,一直到程序结束,任何地方均可见可用。

局部变量

• 局部变量只时暂时地存在,仅仅只依赖于定义它们的函数现阶段是否处于活动 
• 当一个函数调用出现时,其局部变量就进入声明它们 的作用域。在那一刻,一个新的局部变量名为那个对象创建了 
• 一旦函数完成,框架被释放,变量将会离开作用域
• 在函数内部定义的变量。只在函数内部使用,离开函数不能使用。函数的参数也可以看作是局部变量。

局部变量(续1)

• 如果局部与全局有相同名称的变量,那么函数运行时,局部变量的名称将会把全局变量名称遮盖住
>>>	x =	4
>>>	def foo():
		x =	10
		print('in foo, x =',	x)
...
>>>	foo()
in foo,	x =	10
>>>	print('in main,	x =', x)
in	main, x	= 4
# 练习
>>> x = 10
>>> def fn1():
...     print(x)
... 
>>> fn1()
10
>>> def fn2():
...     x = 'hello'
...     print(x)  # 局部和全局有相同的变量,局部遮盖住全局变量
... 
>>> fn2()
hello
>>> x
10
>>> def fn3():
...     global x  # 声明函数内部使用的是全局变量x
...     x = 'ni hao'
...     print(x)
... 
>>> fn3()
ni hao
>>> x
'ni hao'

名称查找的时候,先查局部,再查全局,最后查内建。
>>> def fn4():
...     print(len('abc'))
... 
>>> fn4()  # 查到了内建len函数
3
>>> len = 10
>>> fn4()  # 在全局查到len值是10,数字不能调用
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn4
TypeError: 'int' object is not callable

global语句

• 因为全局变量的名字能被局部变量给遮盖掉,所以为 了明确地引用一个已命名的全局变量,必须使用global语句
>>>	x =	4
>>>	def foo():
		global	x
		x =	10
		print('in foo, x =', x)
...
>>>	foo()
in foo,	x =	10
>>>	print('in main,	x =', x)
in	main, x = 10

函数式编程

偏函数

• 偏函数的概念是将函数式编程的概念和默认参数以及可变参数结合在一起 
• 一个带有多个参数的函数,如果其中某些参数基本上固定的,那么就可以通过偏函数为这些参数赋默认值
>>>	from operator import add
>>>	from functools import partial
>>>	add10 =	partial(add, 10)
>>>	print(add10(25))
35
# 练习
• 可以理解为,改造现有函数,将现有函数中的一些参数赋值,生成新函数。
>>> def add(a, b, c, d, e):
...     return a + b + c + d + e
... 
>>> add(10, 20, 30, 40, 5)
105
>>> add(10, 20, 30, 40, 8)
108
>>> add(10, 20, 30, 40, 13)
113
>>> from functools import partial
>>> myadd = partial(add, 10, 20, 30, 40)
>>> myadd(5)
105
>>> myadd(8)
108

简单GUI程序

1. 窗口程序提供三个按钮
2. 其中两个按钮的前景色均为白色,背景色为蓝色
3. 第三个按钮前景色为红色,背景色为红色
4. 按下第三个按钮后,程序退出
# 练习
import tkinter
from functools import partial

window = tkinter.Tk()
lb = tkinter.Label(window, text='Hello World', font='Arial 20')
# b1 = tkinter.Button(window, fg='white', bg='blue', text='Button 1')
# b2 = tkinter.Button(window, fg='white', bg='blue', text='Button 2')
# b3 = tkinter.Button(window, fg='white', bg='blue', text='Button 3')
MyButton = partial(tkinter.Button, window, fg='white', bg='blue')
b1 = MyButton(text='Button 1')
b2 = MyButton(text='Button 2')
b3 = MyButton(text='quit', command=window.quit)
lb.pack()
b1.pack()
b2.pack()
b3.pack()
window.mainloop()

递归函数

• 如果函数包含了对其自身的调用,该函数就是递归的 
• 在操作系统中,查看某一目录内所有文件、修改权限等都是递归的应用
>>>	def func(num):
	if num == 1:
		return	1
	else:
		return num * func(num - 1)
...
>>>	print(func(5))
120
>>>	print(func(10))
3628800
# 练习
一个函数又包括对自身的调用,就是递归函数。
数学上的阶乘就是递归。
def func(n):
    if n == 1:
        return 1

    return n * func(n - 1)
        #  5 * func(4)
        #  5 * 4 * func(3)
        #  5 * 4 * 3 * func(2)
        #  5 * 4 * 3 * 2 * func(1)
        #  5 * 4 * 3 * 2 * 1

if __name__ == '__main__':
    print(func(5))
# ------------------------------------
5! = 5 x 4 x 3 x 2 x 1
5! = 5 x 4!
5! = 5 x 4 x 3!
5! = 5 x 4 x 3 x 2!
5! = 5 x 4 x 3 x 2 x 1!
1! = 1

快速排序

1. 随机生成10个数字
2. 利用递归,实现快速排序
# 理论
• 假定第一个数是中间值。把比这个数小的放到smaller列表,把比它大的数放到larger列表。最后把这三个部分拼接起来。smaller和larger采用相同的办法继续排序,直到列表的长度是01结束。
from random import randint

def qsort(seq):
    if len(seq) < 2:  # 如果长度小于2,不用排序,直接返回
        return seq
    middle = seq[0]   # 假定第一个数是中间值
    smaller = []      # 用于保存比中间值小的数据
    larger = []       # 用于保存比中间值大的数据
    for i in seq[1:]:
        if i < middle:
            smaller.append(i)
        else:
            larger.append(i)
    # 注意middle不是列表,需要把它放到列表中
    return qsort(smaller) + [middle] + qsort(larger)

if __name__ == '__main__':
    nums = [randint(1, 100) for i in range(10)]
    print(nums)
    print(qsort(nums))

生成器

• 从句法上讲,生成器是一个带yield语句的函数 
• 一个函数或者子程序只返回一次,但一个生成器能暂停执行并返回一个中间的结果 
• yield 语句返回一个值给调用者并暂停执行 
• 当生成器的next()方法被调用的时候,它会准确地从离开地方继续
# 理论
• 生成器本质上也是函数。和普通函数不同,普通函数只能通过return返回一个值,但是生成器可以通过yield返回多个中间值。
• 生成器对象只能使用一次。

生成器(续1)

• 与迭代器相似,生成器以另外的方式来运作 
• 当到达一个真正的返回或者函数结束没有更多的值返 回,StopIteration异常就会被抛出
>>>	def simp_gen():
		yield '1'
		yield '2 ->	punch'
>>>	mygen =	simp_gen()
>>>	mygen.next()
'1'
>>>	mygen.next()
'2	->	punch'
>>>	mygen.next()
…	…
StopIteration
# 练习
>>> def mygen():
...     yield 'hello'
...     a = 10 + 20
...     yield a
...     yield 100
... 
>>> mg
<generator object mygen at 0x7faa8cf631a8>
>>> list(mg)
['hello', 30, 100]
>>> list(mg)
[]
>>> mg = mygen()
>>> for i in mg:
...     print(i)
... 
hello
30
100

生成器还可以使用生成器表达式

>>> from random import randint
>>> nums = (randint(1, 100) for i in range(10))
>>> nums
<generator object <genexpr> at 0x7faa8cf63eb8>
>>> list(nums)
[20, 97, 30, 67, 82, 99, 46, 81, 35, 71]
>>> ips = ('192.168.1.%s' % i for i in range(1, 255))
>>> ips
>>> for ip in ips:
...     print(ip)
模板
模块和文件
什么是模块
模块文件
导入模块
搜索路径
模块导入方法
导入和加载
内置模块
hashlib模块
tarfile模块

模板

模块和文件

什么是模块

• 模块支持从逻辑上组织python代码 
• 当代码量变得相当大的时候, 最好把代码分成一些有组织的代码段 
• 代码片段相互间有一定的联系,可能是一个包含数据 成员和方法的类,也可能是一组相关但彼此独立的操作函数 
• 这些代码段是共享的,所以python允许“调入”一 个模块,允许使用其他模块的属性来利用之前的工作成果,实现代码重用
* 文件是物理上组织代码的形式,模块就是逻辑上组织代码的形式。模块名是文件名去掉.py。

模块文件

• 说模块是按照逻辑来组织python代码的方法,文件是物理层上组织模块的方法 
• 一个文件被看作是一个独立模块,一个模块也可以被看作是一个文件 
• 模块的文件名就是模块的名字加上扩展名.py

导入模块

搜索路径

• 模块的导入需要一个叫做“路径搜索”的过程 
• python在文件系统“预定义区域”中查找要调用的模块 
• 搜索路径在sys.path中定义
>>>	import sys
>>>	sys.path
['', '/usr/local/lib/python37.zip', '/usr/local/lib/python3.7', '/usr/local/lib/python3.7/lib-dynload', '/usr/local/lib/python3.7/sitepackages']
    - sys.path定义的路径
    - PYTHONPATH环境变量定义的路径

模块导入方法

• 使用import导入模块 
• 可以在一行导入多个模块,但是可读性会下降 
• 可以只导入模块的某些属性 
• 导入模块时,可以为模块取别名
>>>	import time, os, sys
>>>	from random	import choice
>>>	import pickle as p
# 练习
# 常用的导入方法
>>> import os
>>> from time import strftime

# 不常用的导入方法
>>> import random, datetime
>>> import pickle as p

导入和加载

• 当导入模块时,模块的顶层代码会被执行 
• 一个模块不管被导入(import)多少次,只会被加载(load)一次
[root@py01	~]#	cat	foo.py
hi = 'hello'
print(hi)
[root@py01	~]#	python3
>>>	import foo
Hello					#第一次导入,执行print语句
>>>	import foo			#再次导入,print语句不再执行
# 练习
    - import是导入
    - load是加载。加载就是将模块中的代码运行一遍
    - 不管导入多少次,只会加载一次。

内置模块

hashlib模块

• hashlib用来替换md5和sha模块,并使他们的API一 致,专门提供hash算法 
• 包括md5、sha1、sha224、sha256、sha384、 sha512,使用非常简单、方便
>>>	import hashlib
>>>	m = hashlib.md5()
>>>	m.update('hello	world!')
>>>	m.hexdigest()
'fc3ff98e8c6a0d3087d515c0473f8677'
# 练习
计算哈希值的模块。

    - hash哈希是单向加密的算法。

    - 通过原始数据可以生成固定长度的乱码
    - 原始数据相同,乱码也一定相同
    - 原始数据有微小的不同,乱码也一定完全不同
    - 不能通过乱码反推回原始数据
    - 常用的算法有:md5、sha
    - 经常用于:存储加密密码、文件完整性校验
>>> import hashlib
>>> m = hashlib.md5(b'123456')
>>> m.hexdigest()
'e10adc3949ba59abbe56e057f20f883e'

# 如果需要计算的数据量非常大,可以分批量进行更新
>>> m1 = hashlib.md5()
>>> m1.update(b'12')
>>> m1.update(b'34')
>>> m1.update(b'56')
>>> m1.hexdigest()
'e10adc3949ba59abbe56e057f20f883e'
# 练习
import hashlib, sys

def check_md5(fname):
    m = hashlib.md5()
    with open(fname, 'rb') as fobj:
        while True:
            date = fobj.read(4096)
            if not date:
                break
            m.update(date)
        return m.hexdigest()
if __name__ == '__main__':
    print(check_md5(sys.argv[1]))
    
C:\Users\Administrator>python "C:\Users\Administrator\Desktop\PyChon Python3.8\2.PYTHON2\bay2\mb.py"   C:\Users\Administrator\Desktop\5000点券礼包.mp4
125c12375f76cb7016d839a695f7ef29

tarfile模块

• tarfile模块允许创建、访问tar文件 
• 同时支持gzip、bzip2格式
[root@666 ~]# ls /home/demo/
install.log mima
[root@666 ~]# python3
>>>	import tarfile
>>>	tar	= tarfile.open('/home/demo.tar.gz',	'w:gz')
>>>	tar.add('demo')
>>>	tar.close()
# 练习
- 实现归档压缩功能,支持gzip、bzip2、lzma格式的压缩。
>>> import tarfile # 压缩
>>> tar = tarfile.open('/tmp/demo/myfile.tar.gz', 'w:gz')
>>> tar.add('/etc/security')
>>> tar.add('/etc/hosts')
>>> tar.close()

# 解压缩
>>> tar = tarfile.open('/tmp/demo/myfile.tar.gz', 'r')
# 不指定解压目标,默认解压到当前目录
>>> tar.extractall('/tmp/demo')
>>> tar.close()

备份程序

1. 需要支持完全和增量备份
2. 周一执行完全备份
3. 其他时间执行增量备份
4. 备份文件需要打包为tar文件并使用gzip格式压缩
# 练习
• os.walk方法
>>> os.walk('/tmp/demo/security')
<generator object walk at 0x7f5a2cccfe60>
>>> list(os.walk('/tmp/demo/security'))
[('/tmp/demo/security', ['console.apps', 'console.perms.d', 'limits.d', 'namespace.d'], ['access.conf', 'chroot.conf', 'console.handlers', 'console.perms', 'group.conf', 'limits.conf', 'namespace.conf', 'namespace.init', 'opasswd', 'pam_env.conf', 'sepermit.conf', 'time.conf', 'pwquality.conf']), ('/tmp/demo/security/console.apps', [], ['config-util', 'xserver', 'liveinst', 'setup']), ('/tmp/demo/security/console.perms.d', [], []), ('/tmp/demo/security/limits.d', [], ['20-nproc.conf']), ('/tmp/demo/security/namespace.d', [], [])]
>>> a = list(os.walk('/tmp/demo/security'))
>>> len(a)
5
>>> a[0]
('/tmp/demo/security', ['console.apps', 'console.perms.d', 'limits.d', 'namespace.d'], ['access.conf', 'chroot.conf', 'console.handlers', 'console.perms', 'group.conf', 'limits.conf', 'namespace.conf', 'namespace.init', 'opasswd', 'pam_env.conf', 'sepermit.conf', 'time.conf', 'pwquality.conf'])
>>> a[1]
('/tmp/demo/security/console.apps', [], ['config-util', 'xserver', 'liveinst', 'setup'])
* os.walk()方法返回的数据由多个元组构成。每个元组又有三项,这三项分别是:路径字符串、该路径下的目录列表、该路径下的文件列表。
>>> list(os.walk('/tmp/demo/security'))
[
    ('父目录', ['父目录下的子目录列表'], ['父目录下的文件列表']),
    ('子目录1', ['子目录1下的孙目录列表'], ['子目录下的文件列表']),
    ('子目录2', ['子目录2下的孙目录列表'], ['子目录下的文件列表']),
]
# -
>>> for path, folders, files in os.walk('/tmp/demo/security'):
...     for file in files:
...         os.path.join(path, file)
# 00 2 * * * mybbt.py
import os
import tarfile
import hashlib
import pickle
from time import strftime

def check_md5(fname):
    m = hashlib.md5()

    with open(fname, 'rb') as fobj:
        while True:
            data = fobj.read(4096)
            if not data:
                break
            m.update(data)

    return m.hexdigest()

def full_backup(src_dir, dst_dir, md5file):
    # 拼接出目标文件的绝对路径
    fname = os.path.basename(src_dir)
    fname = '%s_full_%s.tar.gz' % (fname, strftime('%Y%m%d'))
    fname = os.path.join(dst_dir, fname)

    # 完全备份,即把整个目录打包压缩
    tar = tarfile.open(fname, 'w:gz')
    tar.add(src_dir)
    tar.close()

    # 计算每个文件的md5值,保存到字典中
    md5dict = {}
    for path, folders, files in os.walk(src_dir):
        for file in files:
            key = os.path.join(path, file)
            md5dict[key] = check_md5(key)

    # 将md5字典保存到pickle存储器中
    with open(md5file, 'wb') as fobj:
        pickle.dump(md5dict, fobj)

def incr_backup(src_dir, dst_dir, md5file):
    # 拼接出目标文件的绝对路径
    fname = os.path.basename(src_dir)
    fname = '%s_incr_%s.tar.gz' % (fname, strftime('%Y%m%d'))
    fname = os.path.join(dst_dir, fname)

    # 计算每个文件的md5值,保存到字典中
    md5dict = {}
    for path, folders, files in os.walk(src_dir):
        for file in files:
            key = os.path.join(path, file)
            md5dict[key] = check_md5(key)

    # 取出前一天的md5值
    with open(md5file, 'rb') as fobj:
        old_md5 = pickle.load(fobj)

    # 备份新增的文件和已改动的文件
    tar = tarfile.open(fname, 'w:gz')
    for key in md5dict:
        if old_md5.get(key) != md5dict[key]:
            tar.add(key)
    tar.close()

    # 更新md5文件
    with open(md5file, 'wb') as fobj:
        pickle.dump(md5dict, fobj)

if __name__ == '__main__':
    src_dir = '/tmp/demo/security'
    dst_dir = '/tmp/demo'
    md5file = '/tmp/demo/md5.data'
    if strftime('%a') == 'Mon':
        full_backup(src_dir, dst_dir, md5file)
    else:
        incr_backup(src_dir, dst_dir, md5file)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值