python3:函数基础及高级应用、模块(hashlib,tarfile)

函数基础

  • 函数定义时,不执行其中的代码
  • 所以函数定义的先后顺序无所谓
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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章