文章目錄
函數基礎
- 函數定義時,不執行其中的代碼
- 所以函數定義的先後順序無所謂
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