当前位置:首页 > 有云笔记 > PYTHON2 > 正文内容

函数基础 、 函数高级应用 、 模块

小白3年前 (2022-03-04)PYTHON2111410

函数进阶

创建函数

def语句

  • 标题行由 def 关键字、函数的名字,以及参数的集合(如果有的话)组成

  • def 子句的剩余部分包括了一个虽然可选但是强烈推荐的文档字串,和必需的函数体

前向引用

  • 函数不允许在函数未声明之前,对其进行引用或者调用

def foo():# 定义函数foo(),先调用bar()函数,报错,下面定义以后,报错取消
    print('in foo')
bar()
def bar():# 定义函数bar()
print('in bar')
foo()# 函数foo()已经被定义,可以直接调用

注意

  • 定义函数时,函数的先后顺序不重要,重要的是 函数在什么位置被调用

调用函数

  • 使用一对圆括号() 调用函数,如果没有圆括号,只是对函数的引用

  • 任何输入的参数都是必须放置在括号中

>>> def foo():# 定义函数foo()
...     print('in foo')
... 
>>> foo# 调用函数时,函数名后必须有小括号,否则返回一个位置对象
>>> foo()# 函数得正确调用方式

关键字参数

  • 关键字参数的概念仅仅针对函数的调用

  • 这种理念是 让调用者通过函数调用中的参数名字来区分参数

  • 这种规范允许参数不按顺序

  • 位置参数应写在关键字参数前面

>>> def get_age(name, age):#关键字参数的使用,定义函数get_age(name,age), 形参:name和age
...     print('%s is %s years old' % (name, age))
...
>>> get_age('tom', 20)#调用函数get_age(name,age),name为‘tom’,age为20
>>> get_age(20, 'tom')#会按照顺序将实参传递给对应得形参,name=20,age='tom'
>>> get_age(age=20, name='tom')#使用关键字进行传参
>>> get_age(age=20, 'tom')#关键字参数【age=20】要写在位置参数【'tom'】的后面
>>> get_age(20, name='tom')#传递实参时,位置参数name在第一个位置,在这里获取了两个name的值
>>> get_age('tom', age=20)#传递实参时,name值为'tom',age值为20,正确

练习 1:简单的加减法数学游戏

需求

  • 随机生成两个100以内的数字

  • 随机选择加法或是减法

  • 总是使用大的数字减去小的数字

创建新的python文件math_game.py ,构建加减法数学游戏程序的结构

def exam():#函数exam(), 功能:出题,让用户作答
    pass
def main():#函数main(), 功能:主函数,用户选择继续答题或退出
    while 1:             #永久循环
        exam()          #调用函数,让用户作答
        #判断是否继续,yes或no,strip() 去掉字符串两边空白符,只判断第一个字符
        yn = input('Continue(y/n)?').strip()[0]
        if yn in 'nN':         #用户输入第一个字符是n或N时,退出程序
            print('\nBye-bye')
            break              #退出while循环
if __name__ == '__main__':
    main()

编写出题函数 exam()


# 导入random模块的randint方法【随机产生一个整数】和choice方法【随机取出一个元素】
from  random import  randint, choice
def exam():
    nums = [randint(1, 100) for i in range(2)]#使用列表解析,产生两个1 ~ 100的随机整数
    nums.sort(reverse=True)        #让列表nums中的元素,从大到小排序
    op = choice('+-')              #随机取出运算符'+'或'-'
    if op == '+':      #当op为'+'或'-'时,得到列表nums中两个元素的正确计算结果
        result = nums[0] + nums[1]
    else:
        result = nums[0] - nums[1]
    prompt = '%s %s %s = '  % (nums[0], op, nums[1])#给用户显示出题信息
    answer = int(input(prompt))#定义变量answer,获取用户输入得答案
    if answer == result:    #判断用户答案和正确结果是否相等,打印相应信息
        print('Very Good!!!')
    else:
        print('Wrong Answer!!!')
def main():#函数main(), 功能:主函数,用户选择继续答题或退出
    while 1:             #永久循环
        exam()           #调用函数,让用户作答
        yn = input('Continue(y/n)?').strip()[0]    
        if yn in 'nN':         #用户输入第一个字符是n或N时,退出程序
            print('\nBye-bye')
            break              #退出while循环
if __name__ == '__main__':
    main()

测试


[root@localhost xxx]# python math_game.py 
65 - 30 = 10            #给出一个错误结果10
Wrong Answer!!!
Continue(y/n)?a         #这里只判断第一个字母为n或N,输入任意字符都代表yes
25 - 10 = 15            #给出一个正确结果15
Very Good!!!
Continue(y/n)?N0        #这里只判断第一个字母为n或N,则为不继续执行
Bye-bye

匿名函数

  • python 允许用 lambda 关键字创造匿名函数

  • 匿名是因为不需要以标准的 def 方式来声明

  • 一个完整的 lambda "语句"代表了一个表达式,这个表达式定义体必须和声明放在同一行



# 使用def定义函数add(x,y)
def add(x, y):
    return  x + y
if __name__ == '__main__':
print(add(10, 5))
myadd = lambda x, y: x + y# 定义匿名函数,赋值给myadd,x和y作为形参
print(myadd(10, 20))

filter() 函数

  • filter(func, seq): 调用一个布尔函数 func 来迭代遍历每个序列中的元素;返回一个使 func 返回值为 true 的元素的序列

  • 如果布尔函数比较简单,直接使用 lambda 匿名函数就显得非常方便了

filter(func, seq)函数的使用,如果seq序列中的元素,传入函数func后,返回值为True,则保留下来


from random import  randint#导入random模块的randint方法(随机产生一个整数)
def func1(x):#定义函数func1(x),奇数返回True, 偶数返回False
    return True if x % 2 == 1 else False
if __name__ == '__main__':
    nums = [ randint(1, 100) for i in range(10)]   #10个100以内的随机数
print(nums)                                    #打印nums
result = filter(func1, nums)                   #filter()过滤结果
result2 = filter(lambda x: True if x % 2 == 1 else False, nums)
print(list(result))               #将result转换为列表,打印
print(list(result2))

map() 函数

  • map(func, seq): 调用一个函数func 来迭代遍历每个序列中的元素;返回一个经过func处理过的元素序列

map(func, seq)函数的使用,将seq序列中的元素,传入函数func后,经过处理后全部保留下来


from random import  randint
def func2(x):# 定义函数func2(x),函数返回结果:(x * 2) + 1
    return x * 2 + 1
if __name__ == '__main__':
    nums = [ randint(1, 100) for i in range(10)]   # 10个100以内的随机数
    print(nums)                                    # 打印nums
result3 = map(func2, nums)    # 结果为:nums经过函数func2处理后的序列对象
result4 = map(lambda x: x * 2 + 1, nums)
print(list(result3))          # 将result3转换为列表,打印
print(list(result4))

总结

 函数基础 、 函数高级应用 、 模块

变量作用域

全局变量

  • 标识符的作用域是定义为其声明在程序里的可应用范围,也就是变量的可见性

  • 在一个模块中最高级别的变量有全局作用域

  • 全局变量的一个特征是除非被删除掉,否则他们会存活到脚本运行结束,且对于所有的函数,他们的值都是可以被访问的

全局变量的使用


>>> x = 10# 定义全局变量x
>>> def func1():# 定义函数func1(),函数内部可以直接使用变量x
...     print(x)
... 
>>> func1()#调用函数func1(),结果为10

局部变量

  • 局部变量只是暂时的存在,仅仅只依赖于定义他们的函数现阶段是否处于活动

  • 当一个函数调用出现时,其局部变量就进入声明它们的作用域。在那一刻,一个新的局部变量名为那个对象创建了

  • 一旦函数完成,框架被释放,变量将会离开作用域

局部变量只在函数内部起作用

>>> def func2():#定义函数func2(), 其中的变量a为局部变量,只在函数内部有效
...     a = 10
...     print(a)
... 
>>> def func3():#定义函数func2(), 其中的变量a为局部变量,只在函数内部有效
...     a = 'hello'
...     print(a)
... 
>>> func2()#调用函数func2(),结果为10
>>> func3()#调用函数func3(), 结果为hello
>>> a#查看a的值,没有被定义,函数内部的a为局部变量,只在该函数内部有效

如果局部变量与全局变量有相同的名称,那么函数运行时,局部变量的名称将会把全局变量的名称遮盖住


>>> x = 100# 定义全局变量x
>>> def func5():# 声明函数func5(), 函数内有局部变量x=200
...     x = 200
...     print(x)
... 
>>> func5()# 局部变量
200
>>> x# 查看x【全局变量】,没有发生变化
100

global 语句

  • 因为全局变量的名字能被局部变量给遮盖掉

  • 为了明确地引用一个已命名的全局变量,必须使用 global 语句


>>> x = 100#定义全局变量x
>>> def func6():#定义函数func6()
...     global x      #引用全局变量x
...     x = 200       #为全局变量x赋值为200
...     print(x)      #打印变量x的值
... 
>>> func6()#调用函数func6()
>>> x

查找变量或函数的顺序

  • 首先在函数的内部去查找

  • 函数内部没有,然后去全局去查找,看是否定义

  • 全局也没有,最后会去内建函数中查找


# 验证python查找变量或函数的顺序,定义函数func7(),统计字符'abcd'的长度
>>> def func7():
...     print(len('abcd'))
... 
>>> func7()#调用函数,结果为4,正确
>>> len#全局查看是否有len,没有,不存在
# 先在函数func7()内部查找方法len(),再在全局查找,最后在内建中查找len()

生成器

Python 使用生成器对延迟操作提供了支持。所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。这也是生成器的主要好处。

Python有两种不同的方式提供生成器:

  • 生成器函数:

    • 常规函数定义,但是,使用 yield 语句而不是 return 语句返回结果

    • yield 语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行


def generator(array):
    for i in array:
        return i
    
gen = generator([1,2,3,4,5])
print(type(gen))
# 输出
# <class 'int'>
# ====================================
def generator(array):
    for i in array:
        yield(i)
        
gen = generator([1,2,3,4,5])
print(type(gen))
# 输出
<class 'generator'>

生成器表达式:

  • 类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表


>>> from random import randint
>>> nums = [randint(1, 100) for i in range(10)]#产生10个1~99的随机数
>>> nums
>>> nums2 = (randint(1, 100) for i in range(10))#() 会产生一个随机数的生成器对象
>>> nums2#为一个对象,不占用空间,使用时才会产生数据
>>> for i in nums2:#使用for循环遍历nums2中的元素,成功
...     print(i)
... 
>>> ['192.168.1.%s' %i for i in range(1, 255)]#使用列表解析产生一个254个元素的大列表,占据比较大的内存空间
>>> ips = ('192.168.1.%s' %i for i in range(1, 255))#() 会产生一个IP地址的生成器对象【包含254个IP地址】
>>> ips#为一个对象,不占用空间,使用时才会产生数据
>>> for ip in ips:#使用for循环可以遍历出ips中的所有元素,成功
...     print(ip)
  • Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:


>>> sum((x ** 2 for x in range(4))
# 而不用多此一举的先构造一个列表:
>>> sum([x ** 2 for x in range(4)])
  • 生成器的好处在于延迟操作,需要的时候再使用,所以会节省空间


sum([i for i in range(100000000)])
sum((i for i in range(100000000)))
  • 注意事项

    • 在函数内部,有很多 yield 返回中间结果;

    • 程序向函数取值时,当函数执行到第1个yield时,会暂停函数运行并返回中间结果;

    • 当主程序再次调用函数时,函数会从上次暂停的位置继续运行,当遇到第2个yield,会再次暂停运行函数并返回数据;

    • 重复以上操作,一直到函数内部的yield全部执行完成为止

    • 生成器的唯一注意事项就是:生成器只能遍历一次

    • 自定义生成器函数的过程

练习 2:文件生成器

需求:通过生成器完成以下功能

  • 使用函数实现生成器

  • 函数接受一个文件对象作为参数

  • 生成器函数每次返回文件的 10 行数据


#定义生成器函数gen_block(),功能:每次取10行记录
def gen_block(fobj):
    lines = []           #每次存储10行记录
    counter = 0          #计数器,用于每次从文件中取10行记录
    
    for line in fobj:         #从文件对象中,一行一行读取记录
        lines.append(line)    #将读取的行记录,存储在列表lines中
        counter += 1          #统计读取了几行记录
        if counter == 10:     #当读取10行记录时,执行代码块
            yield  lines      #暂停运行函数,给函数返回列表lines
            lines = []        #再次读取时,继续运行函数,清空lines
            counter = 0       #再次读取时,counter复位到0,重新计数
            
    if lines:            #当lines不为空时,返回剩余的行记录【不足10行】
        yield lines
if __name__ == '__main__':
    fname = '/etc/passwd'         #读取数据的文件
    fobj = open(fname)            #打开文件,默认只读数据
   
    for block in gen_block(fobj): #调用生成器函数,每次打印10行记录
        print(block)              #打印每次读取的记录
        print('*' * 50)           #打印50个*, 区分每次读取的记录
    fobj.close()                  #打开文件,就需要关闭文件

模块详解

模块

基本概念

  • 模块支持从逻辑上组织 python 代码

  • 当代码量变得非常大的时候,最好把代码分成一些有组织的代码段

  • 代码片段相互间有一定的联系,可能是一个包含数据成员和方法的类,也可能是一组相关但彼此独立的操作函数

  • 这些代码段是共享的,所有 python 允许 “调入” 一个模块,允许使用其他模块的属性来利用之前的工作成果,实现代码重用

作用

  • 模块可以实现代码的重用,导入模块,就可以使用模块中已经定义好的类,函数和变量,减少代码的冗余性

模块文件

  • 模块是从逻辑来组织 python 代码的方法,文件是物理层上组织模块的方法

  • 一个文件被看作是一个独立模块,一个模块也可以被看作是一个文件

  • 模块的文件名就是模块的名字加上扩展名 .py

搜索路径

  • 模块的导入需要一个叫做 “路径搜索” 的过程

  • python 在文件系统 “预定义区域” 中查找要调用的模块

  • 搜索路径在 sys.path 中定义

  • 也可以通过 PYTHONPATH 环境变量引入自定义目录

导入模块

查看模块的默认搜索路径


>>> import sys#导入模块sys
>>> sys.path#path, 查看python搜索模块时的默认查找路径

模块导入方法

  • 使用import导入模块

  • 可以在一行导入多个模块,但是可读性会下降

  • 可以只导入模块的某些属性

  • 导入模块时,可以为模块取别名


>>> import time, os, sys
>>> from random import choice
>>> import pickle as p
  • 当导入模块时,模块的顶层代码会被执行

  • 一个模块不管被导入(import)多少次,只会被加载(load)一次


[root@py01 ~]# cat foo.py
hi = 'hello'
print(hi)
[root@py01 ~]# python3
>>> import foo
Hello                        #第一次导入,执行print语句
>>> import foo       #再次导入,print语句不再执行

如果要让自己写模块也可以在任何位置被搜索到,可以将模块文件拷贝到 sys.path 中的 '/root/mypy/lib/python3.6/site-packages' 路径下



[root@localhost day02]# mkdir /tmp/mylibs#自定义目录,用于存放自己的模块文件【目录可以随意指定】
[root@localhost day02]# cp qsort.py /tmp/mylibs/#拷贝qsort.py 文件到/tmp/mylibs中,作为一个模块文件
[root@localhost day02]# export PYTHONPATH=/tmp/mylibs#声明全局变量
[root@localhost day02]# cd /home/#切换到任意目录下,该目录不存在qsort.py
[root@localhost home]# python3 #进入到python的交互式解释器下
>>> import qsort#导入模块qsort成功
补充:导入任意目录下的模块文件
[root@localhost home]# cd  /root/PycharmProjects/py02/day02/
[root@localhost day02]# mkdir mytest#当前路径下,创建目录mytest
[root@localhost day02]# vim mytest/star.py#mytest目录,定义python文件star.py
hi = 'Hello World!'
def pstar(n=30):
    print('*' * n)
[root@localhost day02]# python3#进入到python的交互式解释器下
>>> import mytest.star#导入mytest目录下的star模块,python中说法为:导入mytest包下star模块
>>> mytest.star.hi
>>> mytest.star.pstar()

内置模块

hashlib 模块

  • hashlib 用来替换 MD5 和 sha 模块,并使他们的API一致,专门提供hash算法

  • 包括md5、sha1、sha224、sha256、sha384、sha512,使用非常简单、方便


# 使用hashlib模块,计算bytes类型数据的md5值
>>> import hashlib
# 一次读取所有数据,计算出文件的md5值,适合于小文件数据
>>> m = hashlib.md5(b'123456')#计算b'123456' 的md5值,返回一个对象
>>> m.hexdigest()#以16进制的方式,显示m的md5值
# 每次读取少量数据,最后计算出文件的md5值,适合于大文件数据
>>> m1 = hashlib.md5()#返回一个空数据的md5值
>>> m1.update(b'12')#更新b'12'的md5值
>>> m1.update(b'34')
>>> m1.update(b'56')
>>> m1.hexdigest()#以16进制的方式,显示m1的md5值,结果相同

练习 3:计算文件 md5 值

需求

  • 编写用于计算文件 md5 值的脚本

  • 文件名通过位置参数获得

  • 打印出文件 md5 值


# 创建新的python文件check_md5.py,计算文件的md5值
import sys           #导入模块sys, 通过argv[1]获取位置参数
import hashlib       #计算文件的md5值
def check_md5(fname):    #使用def 定义函数check_md5(),计算文件的md5值
    m = hashlib.md5()         #返回一个空数据的md5值
    with open(fname, 'rb') as fobj:
        while 1:
            data = fobj.read(4096)     #每次读取4096个字节【4k】
            if not data:               #data为空时,退出while循环
                break
            m.update(data)             #更新data的md5值
    return  m.hexdigest()              #返回给函数16进制的mdf值
    
if __name__ == '__main__':
    print(check_md5(sys.argv[1]))   
[root@localhost xxx]# python3 check_md5.py /etc/hosts
54fb6627dbaa37721048e4549db3224d

tarfile 模块

  • tarfile模块允许创建、访问 tar 文件

  • 同时支持 gzip、bzip2 格式


>>> import tarfile#在local下, 导入模块tarfile【实现文件的打包和解压】
>>> tar = tarfile.open('/tmp/demo.tar.gz', 'w:gz')#以'w:gz'的格式,打开包文件,文件不存在则会自动创建
>>> tar.add('/etc/hosts')#向包文件中压缩文件/etc/hosts
>>> tar.add('/etc/security')#向包文件中压缩目录/etc/security
>>> tar.close()#关闭文件
[root@localhost day02]# ls -l /tmp/demo.tar.gz 
>>> tar = tarfile.open('/tmp/demo.tar.gz')#打开文件,文件已经存在,则不需要指定类型,python会自动选择
>>> tar.extractall(path='/var/tmp')#解压到 /var/tmp目录下,不指定解压到当前目录
>>> tar.close()#关闭文件
[root@localhost day02]# ls /var/tmp/etc/#查看软件包demo.tar.gz是否解压成功


 函数基础 、 函数高级应用 、 模块  您阅读本篇文章共花了: 

分享到:

    扫描二维码推送至手机访问。

    版权声明:本文由有云转晴发布,如需转载请注明出处。

    本文链接:https://yyzq.cf/?id=132

    分享给朋友:

    发表评论

    访客

    ◎欢迎参与讨论,请在这里发表您的看法和观点。