Python從基礎到精通day7

函數

  • 關鍵字參數:在函數調用時,參數是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,關鍵字參數必須在位置參數後
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument
>>> func1(20, name='tom') # Error,name得到了多個值
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func1() got multiple values for argument 'name'
>>> func1('tom', age=20)  # OK
tom is 20 years old
>>> func1()  # Error,參數不夠
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func1() missing 2 required positional arguments: 'name' and 'age'
>>> func1('tom', 20, 200)  # Error,參數太多
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func1() takes 2 positional arguments but 3 were given
  • 聲明函數時,在參數名前加上*,表示使用元組接收參數
def func1(*canshu):
    print(canshu)

if __name__ == '__main__':
    func1()
    func1('tom')
    func1('hao', 123, 'jerry', 456)
  • 聲明函數時,在參數名前加上**,表示使用字典接收參數
def func2(**canshu):
    print(canshu)

if __name__ == '__main__':
    func2()
    func2(name='tom', age=20)
  • 調用函數時,在數據類型前加*表示將序列對象拆開
  • 調用函數時,在數據類型前加**表示將字典對象拆成key=val的形式
def func3(name, age):
    print('%s is %s years old' % (name, age))

if __name__ == '__main__':
    user = ['tom', 20]
    func3(user[0], user[1])
    func3(*user)  #*的作用是將列表拆開並把值當作位置參數傳進去
    d1 = {'age': 20, 'name': 'jerry'}
    func3(d1['name'], d1['age'])
    func3(**d1)  想當於把字典拆爲: # func3(name='jerry', age=20)

案例1

隨機出100以內加減法
共有三次機會,三次都答錯後輸出正確答案

from random import randint,choice
def exam():  #用於出題讓用戶作答
    #隨機生成兩個字符,並排序
    nums = [randint(1,100) for i in range(2)] #從1-100之間隨機取出2個數
    #randint(1,100)就是從1-100裏取值
    #range(2)的值爲0,1;從0開始,結束不包含
    #range(2)	可用list(range(2))展開
    nums.sort() #默認將值升序排列
    nums.reverse()  #將值反轉,由大到小排列。

    #隨機生成加減法
    op = choice('+-')

    #計算標準答案
    if op == '+':
        result = nums[0] + nums[1]
    else:
        result = nums[0] - nums[1]

     #判斷用戶答案是否正確
    promat = '%s %s %s = ' % (nums[0],op,nums[1])
    n = 0
    while n < 3:
        try :
            answer = int(input(promat))
        except (KeyboardInterrupt,EOFError):
            print('\nBye-Bye')
            exit()
        except ValueError:
            print('無效的值請重試')
            return

        if answer == result:
            print('回答正確,真棒!')
            break
        print('不對喲,在想想~')
        n += 1
    else: #while循環不執行的時候執行print語句
        print('%s %s  %s的答案是 %s' %(nums[0],op,nums[1],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':  #如果輸入N/n就退出循環,其他字符則繼續循環
            print('\nBye-bye')
            break

if __name__ == '__main__':
    main()

測試運行

[root@python day7]# python3 lianxi.py 
86 + 37 = 123
回答正確,真棒!
Continue(y/n)?y
73 - 5 = 66
不對喲,在想想~
73 - 5 = 55
不對喲,在想想~
73 - 5 = 44
不對喲,在想想~
73 -  5的答案是 68
Continue(y/n)?n
Bye-bye

匿名函數

  • 當函數體代碼只有一行時,可以使用匿名函數
  • 語法:
lambda 參數...: 表達式
def func1(x,y):
    return  x + y
if __name__ == '__main__':
    result  = func1(20,30)
    print(result)

改寫爲匿名函數:

result2 =  lambda x,y : x + y
    print(result2(30,40))

案例1代碼優化1.0

將原先計算正確結果修改爲函數類型,定義一個字典,通過匹配加號減號來實現調用函數
from random import randint,choice
def jiafa(x,y):
    return x + y  #將結果返回

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

def exam():  
    func1 = {'+':jiafa,'-':jianfa}
    nums = [randint(1,100) for i in range(2)] 
    nums.sort() 
    nums.reverse()  
    
    op = choice('+-')

    #通過字典調用函數計算答案
    result = func1[op](nums[0],nums[1])

案例1代碼優化2.0

from random import randint,choice
def exam():  
    func1 = {'+':lambda x,y : x+y,'-':lambda x,y: x - y }   #匿名函數
    nums = [randint(1,100) for i in range(2)] #從1-100之間隨機取出2個數
    nums.sort() #升序
    nums.reverse()  #反轉

    #隨機生成加減法
    op = choice('+-')

    #使用聲明函數
    result = func1[op](*nums)  *nums自動將列表拆分

filter函數

  • 它接收一個函數和一個序列對象作爲參數
  • 函數接收一個參數,必須返回真或假
  • 序列對象中的每個值作爲函數的參數進行調用,返回真的值保留,假的過濾掉
from random import randint

def func1(x):
    # x % 2只有1或0兩種情況,1爲真,0爲假
    return x % 2

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: x % 2, nums)
    print(list(result2))

map函數

  • 它接收一個函數和一個序列對象作爲參數
  • 函數接收一個參數,對其進行加工,返回加工的結果
  • map函數將序列對象中的每個值分別交給函數加工,保留所有的加工結果
from random import randint

def func1(x):
    return x * 2

if __name__ == '__main__':
    nums = [randint(1, 100) for i in range(10)]
    print(nums)
    result = map(func1, nums)
    print(list(result))
    result2 = map(lambda x: x * 2, nums)
    print(list(result2))

變量

  • 定義在函數外面的變量,叫全局變量。全局變量在它定義開始一直到程序結束,一直可見可用。
>>> x = 10
>>> def func1():
...     print(x)
... 
>>> func1()
10
  • 定義在函數內部的變量,以及函數的參數,都是局部變量。局部變量只能在函數內部使用
>>> def func2():
...     a = 10
...     print(a)
... 
>>> func2()
10
>>> print(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
  • 如果局部和全局有相同的變量名,那麼局部變量將會遮蓋住全局變量。
>>> x = 10
>>> def func3():
...     x = 'hello world'
...     print(x)
... 
>>> func3()
hello world
>>> print(x)
10
  • 如果真的需要在局部改變全局變量,需要在函數內部先通過global進行聲明
>>> x = 10
>>> def func4():
...     global x
...     x = 'hello world'
...     print(x)
... 
>>> func4()
hello world
>>> print(x)
hello world

偏函數

  • 通過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, 1)
101
>>> add(10, 20, 30, 40, 3)
103
>>> from functools import partial
>>> myadd = partial(add, 10, 20, 30, 40)
>>> myadd(5)
105
>>> myadd(1)
101
>>> myadd(3)
103
  • 使用偏函數改造int函數,使之可以將2進制、8進制、16進制字符串數字轉換爲10進制
>>> int('1010')
1010
>>> int('1010', base=2)
10
>>> int('1010', base=8)
520
>>> int('1010', base=16)
4112
>>> int('10101001', base=2)
169
>>> int2 = partial(int, base=2)
>>> int2('10101001')
169

遞歸函數(瞭解)

  • 函數的代碼塊又包含了對自己的調用
  • 遞歸函數往往可以使用循環替代
5!=5x4x3x2x1
4!=4x3x2x1
5!=5x4!
5!=5x4x3!
4!=4x3x2!
5!=5x4x3x2x1!
1!=1

# 一個數如果是1,那麼它的階乘就是1;如果這個數不是1,那麼它的階乘等於它乘以它下一個數的階乘
def func1(x):
    if x == 1:
        return 1
    return x * func1(x - 1)

生成器

  • 生成器表達式:與列表解析語法一樣
>>> nums = (randint(1, 100) for i in range(10))
>>> nums
<generator object <genexpr> at 0x7fdac0f6c200>
>>> for i in nums:
...   print(i)
  • 生成器函數:本質上是函數。只不過生成器函數可以通過yield關鍵字返回很多中間結果。
>>> def mygen():
...   yield 100
...   a = 10 + 10
...   yield a
...   yield 200
... 
>>> mg = mygen()
>>> mg
<generator object mygen at 0x7fdac0f6c258>
>>> for i in mg:
...   print(i)
... 
100
20
200

模塊

  • 模塊導入時,python到sys.path指定的路徑下搜索模塊
  • 可以設置PYTHONPATH環境變量指定自定義的模塊搜索路徑
[root@localhost day02]# mkdir /opt/mylibs
[root@localhost day02]# cp qsort.py /opt/mylibs
[root@localhost day02]# export PYTHONPATH=/opt/mylibs
[root@localhost day02]# cd /tmp/
[root@localhost tmp]# python3
>>> import qsort
>>> import sys
>>> sys.path
['', '/opt/mylibs', '/usr/local/lib/python36.zip', '/usr/local/lib/python3.6', '/usr/local/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/site-packages']
  • 在python中,目錄被稱作包,可以當成是一個特殊的模塊
[root@localhost day02]# ls mypkgs/
hi.py
[root@localhost day02]# cat mypkgs/hello.py 
hi = "Hello World!"
>>> import hello   # Error,當前目錄沒有hello.py
>>> mypkgs.hello.hi
'Hello World!'

hashlib模塊

  • 用於計算數據的hash值
>>> import hashlib
>>> m = hashlib.md5(b'123456')
>>> m.hexdigest()
'e10adc3949ba59abbe56e057f20f883e'

[root@localhost day02]# echo -n 123456 > /tmp/d.txt
[root@localhost day02]# md5sum /tmp/d.txt
e10adc3949ba59abbe56e057f20f883e  /tmp/d.txt

# 如果數據量較大,可以分批計算
>>> m1 = hashlib.md5()
>>> m1.update(b'12')
>>> m1.update(b'34')
>>> m1.update(b'56')
>>> m1.hexdigest()
'e10adc3949ba59abbe56e057f20f883e'

案例2 校驗文件的md5值

import sys
import hashlib

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()  #將數據返回

if __name__ == '__main__':
    print(check_md5(sys.argv[1]))  #生成值之後需要print出來

測試:
[root@python day7]# python3 myerror.py /etc/hosts
54fb6627dbaa37721048e4549db3224d
[root@python day7]# md5sum /etc/hosts
54fb6627dbaa37721048e4549db3224d /etc/hosts

可以看到利用python校驗出的md5值與系統命令校驗出的值是相同的。

tarfile模塊

  • 用於壓縮、解壓縮
# 壓縮
>>> import tarfile
>>> tar = tarfile.open('/tmp/a.tar.gz', 'w:gz')
>>> tar.add('/etc/hosts')
>>> tar.add('/etc/security')
>>> tar.close()
[root@localhost day02]# ls /tmp/a.tar.gz 
/tmp/a.tar.gz
[root@localhost day02]# tar tvzf /tmp/a.tar.gz

# 解壓縮
>>> tar = tarfile.open('/tmp/a.tar.gz')
>>> tar.extractall(path='/var/tmp')
>>> tar.close()
[root@localhost day02]# ls /var/tmp/etc/	
hosts  security

案例3 備份文件

每週一做完全備份其他時間做差異備份
from time import strftime
import hashlib
import os
import tarfile
import pickle

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):
    #完全備份,將src目錄打包放到dst中,並將md5值記錄到md5file
    #拼接出備份文件的絕對路徑
    fname = os.path.basename(src)  #取出原目錄下的文件名
    fname = '%s_full_%s.tar.gz' % (fname,strftime('%Y%m%d')) #將文件賦上日期
    fname = os.path.join(dst,fname) #將文件拼接爲絕對路徑


    #開始打包備份
    tar = tarfile.open(fname,'w:gz')
    tar.add(src)
    tar.close()

    #完全備份後,生成所有備份文件的md5值
    md5dict = {}
    for lujing,mulu,wenjian in os.walk(src):
        for file in wenjian:
            k = os.path.join(lujing,file)    #生成絕對路徑
            md5dict[k] = check_md5(k)    #md5dict['/tmp/demo/test': '2131456789agsjq']
            #k = '/etc/passwd'
            #d1 = {}
            #d1[k] = '123456'
            #print(K)  ------>  {'/etc/passwd':'123456'}

    #將md5存入文件
    with open(md5file,'wb') as fobj:
        pickle.dump(md5dict,fobj)


def incr_backup(src,dst,md5file):
    fname = os.path.basename(src)  #取出原目錄下的文件名
    fname = '%s_incr_%s.tar.gz' % (fname,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)

    # 備份新增文件和改動的文件
    tar = tarfile.open(fname, 'w:gz')
    for k in md5dict:  #循環字典的key
        if old_md5.get(k) != md5dict[k]:  #通過key取出md5值在於新的md5值對比
            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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章