函数进阶
创建函数
def 语句
- 标题行由 def 关键字、函数的名字,以及参数的集合(如果有的话)组成
- def 子句的剩余部分包括了一个虽然可选但是强烈推荐的文档字串,和必需的函数体
前向引用
1 2 3 4 5 6 7 8
| def foo(): print('in foo') bar()
def bar(): print('in bar')
foo()
|
注意
- 定义函数时,函数的先后顺序不重要,重要的是 函数在什么位置被调用
调用函数
- 使用一对圆括号() 调用函数,如果没有圆括号,只是对函数的引用
- 任何输入的参数都是必须放置在括号中
1 2 3 4 5
| >>> def foo(): ... print('in foo') ... >>> foo >>> foo()
|
关键字参数
- 关键字参数的概念仅仅针对函数的调用
- 这种理念是 让调用者通过函数调用中的参数名字来区分参数
- 这种规范允许参数不按顺序
- 位置参数应写在关键字参数前面
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def get_info(name, age=20): print("%s is %s years old" % (name, age))
get_info("nfx") get_info("nfx", 18) get_info(18, "nfx")
get_info(name="nfx", age=18) get_info(age=18, name="nfx")
get_info("nfx", age=20)
print("hello", "world", sep="---", end="!!\n")
|
练习 1:简单的加减法数学游戏
需求
- 随机生成两个100以内的数字
- 随机选择加法或是减法
- 总是使用大的数字减去小的数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import random def exam(): nums = [random.randint(1,100) for i in range(2)] nums.sort(reverse=True) tmp = random.choice("+-") if tmp == "+": result = nums[0] + nums[1] else: result = nums[0] - nums[1] answer = int(input("%s %s %s = ?? 请作答: " % (nums[0], tmp, nums[1]))) if answer == result: print("Very Good~~~") else: print("Wrong answer!!!")
def show_menu(): while True: exam() tmp = input("退出(n/N): ") if tmp in "nN": print("Byebye~") break
if __name__ == '__main__': show_menu()
|
匿名函数
- python 允许用 lambda 关键字创造匿名函数
- 匿名是因为不需要以标准的 def 方式来声明
- 一个完整的 lambda “语句”代表了一个表达式,这个表达式定义体必须和声明放在同一行
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def add(x, y): return x+y def func01(num): return True if num % 2 == 1 else False test = lambda num: True if num % 2 == 1 else False print(test(100)) myadd = lambda x, y: x+y print(myadd(100, 200)) print(add(1, 2))
|
filter() 函数
- filter(func, seq): 调用一个布尔函数 func 来迭代遍历每个序列中的元素;返回一个使 func 返回值为 true 的元素的序列
- 如果布尔函数比较简单,直接使用 lambda 匿名函数就显得非常方便了
filter(func, seq)函数的使用,如果seq序列中的元素,传入函数func后,返回值为True,则保留下来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
def func01(num): return True if num % 2 == 1 else False def func02(num): return True if num > 5 else False
if __name__ == '__main__': list01 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] data1 = filter(func01, list01) print("data1:", list(data1)) data2 = filter(func02, list01) print("data2:", list(data2)) data3 = filter( lambda num: True if num % 2 == 0 else False,list01 ) print("data3:", list(data3)) d4=filter(lambda num:True if num>6 else False,list01) print("data4:", list(d4))
|
map() 函数
- map(func,seq): 调用一个函数func 来迭代遍历每个序列中的元素;返回一个经过func处理过的元素序列
map(func, seq)函数的使用,将seq序列中的元素,传入函数func后,经过处理后全部保留下来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def func01(num): return num * 2 + 10 if __name__ == '__main__': list01 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] data1 = map(func01, list01) print("data1:", list(data1)) data2 = map(lambda num: num+5, list01) print("data2:", list(data2)) d3 = map(lambda num:True if num>5 else False,list01) print("data3:", list(d3))
|
总结
![image-20210902173003230]()
变量作用域
全局变量
- 标识符的作用域是定义为其声明在程序里的可应用范围,也就是变量的可见性
- 在一个模块中最高级别的变量有全局作用域
- 全局变量的一个特征是除非被删除掉,否则他们会存活到脚本运行结束,且对于所有的函数,他们的值都是可以被访问的
全局变量的使用
1 2 3 4 5
| >>> x = 10 >>> def func1(): ... print(x) ... >>> func1()
|
局部变量
- 局部变量只是暂时的存在,仅仅只依赖于定义他们的函数现阶段是否处于活动
- 当一个函数调用出现时,其局部变量就进入声明它们的作用域。在那一刻,一个新的局部变量名为那个对象创建了
- 一旦函数完成,框架被释放,变量将会离开作用域
局部变量只在函数内部起作用
1 2 3 4 5 6 7 8 9 10 11
| >>> def func2(): ... a = 10 ... print(a) ... >>> def func3(): ... a = 'hello' ... print(a) ... >>> func2() >>> func3() >>> a
|
如果局部变量与全局变量有相同的名称,那么函数运行时,局部变量的名称将会把全局变量的名称遮盖住
1 2 3 4 5 6 7 8 9
| >>> x = 100 >>> def func5(): ... x = 200 ... print(x) ... >>> func5() 200 >>> x 100
|
global 语句
- 因为全局变量的名字能被局部变量给遮盖掉
- 为了明确地引用一个已命名的全局变量,必须使用 global 语句
1 2 3 4 5 6 7 8 9
| >>> x = 100 >>> def func6(): ... global x ... x = 200 ... print(x) ...
>>> func6() >>> x
|
查找变量或函数的顺序
- 首先在函数的内部去查找
- 函数内部没有,然后去全局去查找,看是否定义
- 全局也没有,最后会去内建函数中查找
1 2 3 4 5 6 7
| >>> def func7(): ... print(len('abcd')) ... >>> func7() >>> len
|
生成器
Python 使用生成器对延迟操作提供了支持。所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。这也是生成器的主要好处。
Python有两种不同的方式提供生成器:
生成器函数:
- 常规函数定义,但是,使用 yield 语句而不是 return 语句返回结果
- yield 语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
def func02(): a = 1 yield a b = "hello" yield b c = [1, 2] yield c gen2 = func02() print("gen2:", gen2)
print(gen2.__next__()) print(gen2.__next__()) for item in gen2: print("for:", item)
|
生成器表达式:
- 类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import random list01 = [random.randint(1, 5) for i in range(5)] print(list01)
gen01 = (random.randint(1, 5) for i in range(5)) print(gen01)
print("data:", gen01.__next__()) print("data:", gen01.__next__()) for item in gen01: print("for:", item)
|
- 生成器的好处在于延迟操作,需要的时候再使用,所以会节省空间
1 2
| sum([i for i in range(100000000)]) sum((i for i in range(100000000)))
|
注意事项
- 生成器的唯一注意事项就是:生成器只能遍历一次
- 自定义生成器函数的过程
- 在函数内部,有很多 yield 返回中间结果;
- 程序向函数取值时,当函数执行到第1个yield时,会暂停函数运行并返回中间结果;
- 当主程序再次调用函数时,函数会从上次暂停的位置继续运行,当遇到第2个yield,会再次暂停运行函数并返回数据;
- 重复以上操作,一直到函数内部的yield全部执行完成为止
练习 :文件生成器
需求:通过生成器完成以下功能
- 使用函数实现生成器 yield
- 函数接受一个文件对象作为参数(读文件)
- 生成器函数每次返回文件的 10 行数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
def gen_file(fobj): lines = [] for item in fobj.readlines(): lines.append(item) if len(lines) == 10: yield lines lines.clear() if len(lines) != 0: yield lines if __name__ == '__main__': fr = open("/etc/passwd", mode="r") gen = gen_file(fr) for item in gen: print(item) print("============================") fr.close()
|
模块详解
模块
基本概念
- 模块支持从逻辑上组织 python 代码
- 当代码量变得非常大的时候,最好把代码分成一些有组织的代码段
- 代码片段相互间有一定的联系,可能是一个包含数据成员和方法的类,也可能是一组相关但彼此独立的操作函数
- 这些代码段是共享的,所有 python 允许 “调入” 一个模块,允许使用其他模块的属性来利用之前的工作成果,实现代码重用
作用
- 模块可以实现代码的重用,导入模块,就可以使用模块中已经定义好的类,函数和变量,减少代码的冗余性
模块文件
- 模块是从逻辑来组织 python 代码的方法,文件是物理层上组织模块的方法
- 一个文件被看作是一个独立模块,一个模块也可以被看作是一个文件
- 模块的文件名就是模块的名字加上扩展名 .py
搜索路径
- 模块的导入需要一个叫做 “路径搜索” 的过程
- python 在文件系统 “预定义区域” 中查找要调用的模块
- 搜索路径在 sys.path 中定义
- 也可以通过 PYTHONPATH 环境变量引入自定义目录
导入模块
查看模块的默认搜索路径
1 2
| >>> import sys >>> sys.path
|
模块导入方法
- 使用import导入模块
- 可以在一行导入多个模块,但是可读性会下降
- 可以只导入模块的某些属性
- 导入模块时,可以为模块取别名
1 2 3
| >>> import time, os, sys >>> from random import choice >>> import pickle as p
|
- 当导入模块时,模块的顶层代码会被执行
- 一个模块不管被导入(import)多少次,只会被加载(load)一次
1 2 3 4 5 6 7
| [root@localhost day07] [root@localhost day07]
[root@localhost day07] [root@localhost day07] [root@localhost day07] >>> import 自定义模块
|
内置模块
hashlib 模块
- hashlib 用来替换 MD5 和 sha 模块,并使他们的API一致,专门提供hash算法
- 包括md5、sha1、sha224、sha256、sha384、sha512,使用非常简单、方便
1 2 3 4 5 6 7 8 9 10 11 12
| >>> import hashlib
>>> m = hashlib.md5(b'123456') >>> m.hexdigest()
>>> m1 = hashlib.md5() >>> m1.update(b'12') >>> m1.update(b'34') >>> m1.update(b'56') >>> m1.hexdigest()
|
练习 5:计算文件 md5 值
需求
- 编写用于计算文件 md5 值的脚本
- 文件名通过位置参数获得
- 打印出文件 md5 值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import hashlib
def check_md5(fname): m = hashlib.md5() with open(fname, mode='rb') as fobj: while 1: data = fobj.read(4096) if len(data) == 0: break m.update(data) return m.hexdigest() if __name__ == '__main__': print(check_md5("/etc/hosts"))
[root@localhost xxx] 54fb6627dbaa37721048e4549db3224d
|
tarfile 模块
- tarfile模块允许创建、访问 tar 文件
- 同时支持 gzip、bzip2 格式
1 2 3 4 5 6 7 8 9 10 11
| >>> import tarfile >>> tar = tarfile.open('/tmp/demo.tar.gz', 'w:gz') >>> tar.add('/etc/hosts') >>> tar.add('/etc/security') >>> tar.close() [root@localhost day02]
>>> tar = tarfile.open('/tmp/demo.tar.gz') >>> tar.extractall(path='/var/tmp') >>> tar.close() [root@localhost day07]
|