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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章