函數
- 關鍵字參數:在函數調用時,參數是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)