文章目录
- 函数基础
- 编写简单的加减法数学游戏
- 匿名函数
- filter函数
- map函数
- 函数高级应用
- 变量作用域
- 全局变量
- 局部变量
- global
- 函数式编程
- 偏函数
- 递归函数
- 创建qsort.py文件,实现递归快速排序:
- 生成器
- 模块
- 模块和文件
- 导入模块
- 内置模块
- hashlib模块
- 编写hash_check.py 实现md5sum功能
- tarfile模块
- 编写backup.py脚本,实现备份:
函数基础
- 函数定义时,不执行其中的代码
- 所以函数定义的先后顺序无所谓
def func1():
print('in func1')
func2()
def func2():
print('in func2')
func1() # 不会报错,因为此时func2已定义
- 函数的参数部分,直接给定一个名字,如arg,称作位置参数
- 函数的参数部分,给定的形式像key=val这样,称作关键字参数
>>> def func1(name, age):
... print('%s is %s years old' % (name, age))
...
>>> func1('tom', 20) # OK
tom is 20 years old
>>> func1(20, 'tom') # 语法正确,语义不对
20 is tom years old
>>> func1(age=20, name='tom') # OK
tom is 20 years old
>>> func1(age=20, 'tom') # Error,关键字参数必须在位置参数后面
>>> func1(20, name='tom') # Error,变量name得到了两个值
>>> func1('tom', age=20) # OK
tom is 20 years old
>>> func1() # Error,参数太少
>>> func1('tom', 20, 30) # Error,参数太多
- 在定义函数时,参数名前加上*,表示使用元组接收参数
>>> def func1(*canshu):
... print(canshu)
...
>>> func1()
()
>>> func1('tom')
('tom',)
>>> func1('tom', 20)
('tom', 20)
- 在定义函数时,参数名前加上**,表示使用字典接收参数
>>> def func2(**kw_canshu):
... print(kw_canshu)
...
>>> func2()
{}
>>> func2(name='tom')
{'name': 'tom'}
>>> func2(name='tom', age=20)
{'name': 'tom', 'age': 20}
- 调用函数时,在参数名前加*表示将序列拆开
>>> def func3(x, y):
... print(x + y)
...
>>> l = [10, 20]
>>> t = (100, 200)
>>> func3(*l)
30
>>> func3(*t)
300
- 调用函数时,在参数名前加**,表示把字典拆成key=val的形式
>>> def func4(name, age):
... print('%s is %s years old' % (name, age))
...
>>> user = {'name': 'tom', 'age': 20}
>>> func4(**user) # func4(name='tom', age=20)
tom is 20 years old
编写简单的加减法数学游戏
随机生成两个100以内的数字
随机选择加法或是减法
总是使用大的数字减去小的数字
如果用户答错三次,程序给出正确答案
运行方式:
1 + 1 = 2
Very Good!!!
Continue(y/n)? y
1 + 2 = 0
Wrong
1 + 2 = 1
Wrong
1 + 2 = 2
Wrong
1 + 2 = 3
Continue(y/n)? n
Bye-bye
程序:
import random
def add(x,y):
return x+y
def sub(x,y):
return x-y
def exam():
nums=[random.randint(1,100) for i in range(2)]
cmds={'+':add,'-':sub}
#nums.sort() 升序
#nums.reverse() # 翻转
nums.sort(reverse=True) # reverse=True ,降序
op =random.choice('+-')
#result=cmds[op](nums[0],nums[1])
result=cmds[op](*nums)
counter=0
prompt = '%s %s %s = ' % (nums[0], op, nums[1])
while counter <3:
try:
answer = int(input(prompt))
except:
print()
continue
if answer == result:
print('不错哟!!!')
break
else:
print('不对哟!!!')
counter +=1
else:
print('%s%s' % (prompt,result))
def main():
while 1:
exam()
try:
yn = input('continue y/n ?').strip()[0]
except IndexError:
continue
except (KeyboardInterrupt,EOFError):
yn = 'n'
if yn in 'nN':
print('\nBye-bye')
break
if __name__=='__main__':
main()
匿名函数
- 当函数体只有一行代码时,可以通过关键字lambda创建匿名函数
>>> def add(x, y):
... return x + y
...
# 以上函数可以改为以下形式:
>>> myadd = lambda x, y: x + y # x,y是参数,x+y的结果自动成为返回值返回,不用return
>>> myadd(10, 20) # 调用函数
filter函数
- 它接受两个参数
- 第一个参数是函数,该函数接受一个参数,返回值必须是真或假
- 第二个参数是序列对象
- filter将序列中的每个值传给第一个函数,返回真保留,返回假过滤掉
from random import randint
def func1(x):
'接受一个数字,奇数返回True,偶数返回False'
return True if x % 2 == 1 else False
if __name__ == '__main__':
nums = [randint(1, 100) for i in range(10)]
print(nums)
result = filter(func1, nums)
result2 = filter(lambda x: True if x % 2 == 1 else False, nums)
print(list(result))
print(list(result2))
map函数
- 它接受两个参数
- 第一个参数是函数,该函数接受一个参数,将该数据处理后返回
- 第二个参数是序列对象
- map将序列中的每个值传给第一个函数,处理结果保存
函数高级应用
变量作用域
全局变量
- 在函数外面定义的变量,是全局变量,全局变量在声明开始到程序结束,一直可见
可用
>>> x = 10
>>> def func1():
... print(x)
...
>>> func1()
10
局部变量
- 函数内部的变量是局部变量,只能在函数内部使用
>>> def func2():
... a = 10
... print(a)
...
>>> func2()
10
>>> a # 报错
global
- 如果需要在函数内改变全局变量,需要使用global关键字
>>> x = 10
>>> def func4():
... global x
... x = 100
... print(x)
...
>>> func4()
100
>>> x
100
函数式编程
偏函数
- 使用functools模块中的partial功能,改造现有函数,将其中的某些参数固定,生
成新函数
>>> 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, 2)
102
>>> add(10, 20, 30, 40, 8)
108
>>> from functools import partial
>>> myadd = partial(add, 10, 20, 30, 40)
>>> myadd(5)
105
>>> myadd(2)
102
>>> myadd(8)
108
>>> int('11001100', base=2) # 字符串是2进制数
204
>>> int('10001100', base=2)
140
>>> int2 = partial(int, base=2) # 改造int函数,将base=2固定下来,生成新函
数,名为int2
>>> int2('11000000')
192
递归函数
- 一个函数内部又包含了对自身的调用,就是递归函数
# 数字阶乘是非常直观的递归
5!=5x4x3x2x1
5!=5x4!
5!=5x4x3!
5!=5x4x3x2!
5!=5x4x3x2x1!
1!=1
创建qsort.py文件,实现递归快速排序:
随机生成10个数字
利用递归,实现快速排序
import random
def qsort(seq):
if len(seq) < 2:
return seq
m=seq[0]
l=[]
s=[]
for data in seq[1:]:
if data <= m:
s.append(data)
else:
l.append(data)
return qsort(l)+[m]+qsort(s)
if __name__=='__main__':
nums=[random.randint(1,100) for i in range(10)]
print(nums)
print(qsort(nums))
生成器
- 使用生成器表达式,与列表解析有着一样的语法
>>> ['192.168.1.' + str(i) for i in range(1, 255)]
>>> ips = ('192.168.1.' + str(i) for i in range(1, 255))
>>> for ip in ips:
... print(ip)
- 使用生成器函数。与普通函数不一样,生成器函数可以用yield关键字返回很多中>间值
- 生成器函数的代码不是一次全部执行完,遇到yield将会产生中间值,并停在那里>,下一次向生成器函数取值时,它将继续向下运行。
>>> def mygen():
... yield 100
... a = 10 + 5
... yield a
... b = 100 + 200
... yield b
>>> mg = mygen() # 创建生成器对象
>>> for i in mg:
... print(i)
模块
模块和文件
- 什么是模块
- 什么是模块文件
导入模块
- 模块导入时,将会到sys.path定义的路径下查找模块
>>> import sys
>>> sys.path
['', '/usr/local/lib/python36.zip', '/usr/local/lib/python3.6', '/usr/local/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/site-packages']
- 我们自己编写的模块目录,可以使用PYTHONPATH环境变量定义
[root@localhost day02]# export PYTHONPATH=/var/ftp/nsd2019/nsd1909/py02/day01
>>> import sys
>>> sys.path
['', '/var/ftp/nsd2019/nsd1909/py02/day01', '/usr/local/lib/python36.zip', '/usr/local/lib/python3.6', '/usr/local/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/site-packages']
- 目录也可以当作一个特殊的模块,术语叫作包
[root@localhost day02]# mkdir aaa
[root@localhost day02]# echo 'hi = "hello world"' > aaa/hello.py
[root@localhost day02]# cat aaa/hello.py
hi = "hello world"
>>> import aaa.hello
>>> aaa.hello.hi
'hello world'
内置模块
hashlib模块
- 用于计算数据的哈希值。
- 哈希即hash的音译,它是一个单向加密的算法
- 给定相同的数据,一定可以得到相同的乱码
- 不能通过乱码反向推出原始数据
- 用于存储加密的密码,也可以用于计算文件的完整性
# 计算md5值
>>> import hashlib
>>> m = hashlib.md5(b'123456')
>>> m.hexdigest() # 123456的哈希值
'e10adc3949ba59abbe56e057f20f883e'
# 数据量很大的时候,可以采用分批计算
>>> m1 = hashlib.md5()
>>> m1.update(b'12')
>>> m1.update(b'34')
>>> m1.update(b'56')
>>> m1.hexdigest()
'e10adc3949ba59abbe56e057f20f883e'
编写hash_check.py 实现md5sum功能
实现:python3 hash_check.py 文件,返回hash值
#/usr/local/bin/python3
import hashlib
import sys
def check_hash(fname):
m1=hashlib.md5()
with open(fname,'rb') as f:
while 1:
data=f.read(4096)
if not data:
break
m1.update(data)
#for line in f:
# m1.update(line)
print(m1.hexdigest())
if __name__=='__main__':
check_hash(sys.argv[1])
tarfile模块
- 实现tar包功能、压缩解压缩
>>> import tarfile
# 压缩
>>> tar = tarfile.open('/var/tmp/mytest.tar.gz', 'w:gz')
>>> tar.add('/etc/security')
>>> tar.add('/etc/hosts')
>>> tar.close()
# 解压缩
>>> tar = tarfile.open('/var/tmp/mytest.tar.gz')
>>> tar.extractall(path='/var/tmp') # 不指定解压位置,将会解到当前目录
>>> tar.close()
编写backup.py脚本,实现备份:
需要支持完全和增量备份
周一执行完全备份
其他时间执行增量备份
备份文件需要打包为tar文件并使用gzip格式压缩
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 1:
data = fobj.read(4096)
if not data:
break
m.update(data)
return m.hexdigest()
def full_backup(src, dst, md5file):
# 拼接出备份文件的绝对路径
fname = '%s_full_%s.tar.gz' % (os.path.basename(src), strftime('%Y%m%d'))
fname = os.path.join(dst, fname)
# 打包压缩
tar = tarfile.open(fname, 'w:gz')
tar.add(src)
tar.close()
# 计算每个文件的md5值,保存到字典中
md5dict = {}
for path, folders, files in os.walk(src):
for file in files:
k = os.path.join(path, file)
md5dict[k] = check_md5(k)
# 将md5字典保存到文件中
with open(md5file, 'wb') as fobj:
pickle.dump(md5dict, fobj)
def incr_backup(src, dst, md5file):
# 拼接出备份文件的绝对路径
fname = '%s_incr_%s.tar.gz' % (os.path.basename(src), strftime('%Y%m%d'))
fname = os.path.join(dst, fname)
# 计算每个文件的md5值,保存到字典中
md5dict = {}
for path, folders, files in os.walk(src):
for file in files:
k = os.path.join(path, file)
md5dict[k] = check_md5(k)
# 取出前一天的md5值
with open(md5file, 'rb') as fobj:
old_md5 = pickle.load(fobj)
# 字典的key是文件名,key在今天有,昨天没有,就是新增的文件, 文件的md5值不一样,就是改动的文件
# 新增的文件和改动的文件需要备份
tar = tarfile.open(fname, 'w:gz')
for k in md5dict:
if old_md5.get(k) != md5dict[k]:
tar.add(k)
tar.close()
# 更新md5文件
with open(md5file, 'wb') as fobj:
pickle.dump(md5dict, fobj)
if __name__ == '__main__':
src = '/tmp/demo/security'
dst = '/tmp/demo/backup'
md5file = '/tmp/demo/backup/md5.data'
if strftime('%a') == 'Mon':
full_backup(src, dst, md5file)
else:
incr_backup(src, dst, md5file)
#准备:
# mkdir /tmp/demo
# cp -r /etc/security/ /tmp/demo
# mkdir /tmp/demo/backup