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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值