python3——函数、模块、生成器

函数进阶

创建函数

def 语句

  • 标题行由 def 关键字、函数的名字,以及参数的集合(如果有的话)组成
  • def 子句的剩余部分包括了一个虽然可选但是强烈推荐的文档字串,和必需的函数体

前向引用

  • 函数不允许在函数未声明之前,对其进行引用或者调用
  • 定义函数时,函数的先后顺序不重要,重要的是 函数在什么位置被调用

调用函数

  • 使用一对圆括号() 调用函数,如果没有圆括号,只是对函数的引用
  • 任何输入的参数都是必须放置在括号中

关键字参数

  • 关键字参数的概念仅仅针对函数的调用
  • 这种理念是 让调用者通过函数调用中的参数名字来区分参数
  • 这种规范允许参数不按顺序
  • 位置参数应写在关键字参数前面
# 默认参数:定义函数,给形参的默认值

def get_info(name, age=20):

    print("%s is %s years old" % (name, age))

# 按照位置传参

get_info("nfx")  # name="nfx"  age=20

get_info("nfx", 18)  # name="nfx"  age=18

get_info(18, "nfx")  # name=18 age="nfx"

# 关键字传参(指名道姓): 可以不按照顺序传参

get_info(name="nfx", age=18)

get_info(age=18, name="nfx")

# 注意: 关键字参数后不能有位置参数

get_info("nfx", age=20)

# get_info(name="nfx", 20): 报错

print("hello", "world", sep="---", end="!!\n")

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

需求

  • 随机生成两个100以内的数字
  • 随机选择加法或是减法
  • 总是使用大的数字减去小的数字
import random
def exam():
    nums = [random.randint(1,100) for i in range(2)]
    nums.sort(reverse=True)# 列表降序排列 [大数, 小数]
    tmp = random.choice("+-")  # 随机出操作符
    # result: 是题目的正确答案
    if tmp == "+":
        result = nums[0] + nums[1]
    else:  # tmp == "-"
        result = nums[0] - nums[1]
    # 10 + 5 = ??  请作答:   answer: 计算的答案
    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 "语句"代表了一个表达式,这个表达式定义体必须和声明放在同一行
def add(x, y):
    return x+y

def func01(num):
    # if num % 2 == 1:
    #     return True
    # else:
    #     return False
    # if表达式
    return True if num % 2 == 1 else False

test = lambda num: True if num % 2 == 1 else False
print(test(100))  # False
myadd = lambda x, y: x+y  # 显示调用不推荐
print(myadd(100, 200))  # x = 100, y = 200
print(add(1, 2))

变量作用域

全局变量

  • 标识符的作用域是定义为其声明在程序里的可应用范围,也就是变量的可见性
  • 在一个模块中最高级别的变量有全局作用域
  • 全局变量的一个特征是除非被删除掉,否则他们会存活到脚本运行结束,且对于所有的函数,他们的值都是可以被访问的

局部变量

  • 局部变量只是暂时的存在,仅仅只依赖于定义他们的函数现阶段是否处于活动
  • 当一个函数调用出现时,其局部变量就进入声明它们的作用域。在那一刻,一个新的局部变量名为那个对象创建了
  • 一旦函数完成,框架被释放,变量将会离开作用域
  • 如果局部变量与全局变量有相同的名称,那么函数运行时,局部变量的名称将会把全局变量的名称遮盖住

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 语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
# 生成器函数

# return: 函数执行的终止

# yield: 表示函数执行的暂停

def func02():

    a = 1

    yield a

    b = "hello"

    yield b

    c = [1, 2]

    yield c

gen2 = func02()

print("gen2:", gen2)

# <generator object func02 at 0x7f94da357a98>

print(gen2.__next__())  # 1

print(gen2.__next__())  # hello

for item in gen2:

    print("for:", item)  # [1, 2]

# gen2.__next__()  # 报错

生成器表达式:

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

import random

list01 = [random.randint(1, 5) for i in range(5)]

print(list01)  # [1, 3, 4, 1, 2]

# 生成器表达式

gen01 = (random.randint(1, 5) for i in range(5))

print(gen01)  # <generator object <genexpr> at 0x7f18cc1c8830>

# 生成器当中的元素只能获取一遍

# for   生成器.__next__()

print("data:", gen01.__next__())  # 第一个数据

print("data:", gen01.__next__())  # 第二个数据

for item in gen01:

    print("for:", item)

# print("data:", gen01.__next__())  # 报错:StopIteration

生成器的好处在于延迟操作,需要的时候再使用,所以会节省空间

sum([i for i in range(100000000)])

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

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

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

      • 在函数内部,有很多 yield 返回中间结果;
      • 程序向函数取值时,当函数执行到第1个yield时,会暂停函数运行并返回中间结果;
      • 当主程序再次调用函数时,函数会从上次暂停的位置继续运行,当遇到第2个yield,会再次暂停运行函数并返回数据;
      • 重复以上操作,一直到函数内部的yield全部执行完成为止
练习 :文件生成器

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

  • 使用函数实现生成器 yield
  • 函数接受一个文件路径作为参数(读文件)
  • 生成器函数每次返回文件的 10 行数据
 需求:通过生成器完成以下功能
#
#     使用函数实现生成器 yield
#     函数接受一个文件路径作为参数(读文件)
#     生成器函数每次返回文件的 10 行数据

#首先:敲定数据存储类型
#set:去重、交并
# dict:查询
# tuple:不变、存储
# list:可变

#程序=数据类型(list)+算法
#1.按行读取文件(while True+readline ,readlines)
#2.每读取一行数据,向列表中添加该行记录
#3.判断此时列表的长度是否为10
#  3.1长度为10:yield返回列表,清空列表元素
#  3.2长度不为10:向列表中添加行数据
#4.文件读取完毕时,需判断列表是否为空,不为空:yield列表数据
def get_file(path):
    lines = []
    with open(path,mode='r') as fr:
        for item in fr.readlines():
            lines.append(item)
            if len(lines) ==10:
                yield lines
                lines.clear()
    if len(lines) != 0:
        yield lines

if __name__ == '__main__':
    for item in get_file("/etc/passwd"):
        print(item)
        print("=======================")

方法二:

    with open(path,mode="r") as fr:
        list01=[]
        n=0
        list01=fr.readlines();
    while len(list01)-n > 0:
        print(n)
        if len(list01)-n >=10:
            list02=[]
            list02=list01[n:n+10]
            yield list02
        else :
            list02=[]
            list02=list01[n:]
            yield list02
        n += 10

模块详解

模块

基本概念

  • 模块支持从逻辑上组织 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@localhost ~]# mkdir /tmp/mylibs

[root@localhost ~]# vim /tmp/mylibs/nfx.py

[root@localhost ~]# cat /tmp/mylibs/nfx.py

def hehe():

    print("hello nfx~")

[root@localhost ~]# python3  # 当前路径: /root

>>> import nfx  # 失败: No module named 'nfx'

# export只对当前终端临时生效

[root@localhost ~]# export PYTHONPATH=/tmp/mylibs

[root@localhost ~]# python3

>>> import sys

>>> sys.path  # 将/tmp/mylibs添加到了模块搜索路径中

['', '/tmp/mylibs', ....]

>>> import nfx  # 成功

>>> nfx.hehe()  # 模块名.函数()

内置模块

hashlib 模块

  • hashlib 用来替换 MD5 和 sha 模块,并使他们的API一致,专门提供hash算法
  • 包括md5、sha1、sha224、sha256、sha384、sha512,使用非常简单、方便
    import hashlib
    #1.一次性计算
    m = hashlib.md5(b"123456")
    print(m.hexdigest())
    
    #2.分批计算
    m2=hashlib.md5()
    m2.update(b"12")
    m2.update(b"34")
    m2.update(b"56")
    print(m2.hexdigest())

    案例:验证大文件

import hashlib
def get_md5(path):
    m = hashlib.md5()
    with open(path,mode="rb") as fr:
        while True:
            data=fr.read(4096*16)
            if len(data)==0:
               break
            else:
                m.update(data)
    return m.hexdigest()
if __name__ == '__main__':
    print(get_md5("/etc/hosts"))

#在linux命令行中输入 md5 /etc/hosts 验证是否一样

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 day07]# ls /var/tmp/etc/        #查看软件包demo.tar.gz是否解压成功

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值