python编程语言之进阶语法

news2024/11/28 20:38:52

迭代器

可迭代对象

讲迭代器之前,我们先了解一个概念:可迭代对象(Iterable)。

那么什么是可迭代?什么是对象?

迭代(Iteration),是指通过遍历获取某容器内所有元素,特指遍历获取这个动作。
可迭代 (iterable),是指某个容器类型数据可被for循环遍历获取内部所有成员。

对象(Object),我们会在后面的面向对象部分专门介绍。我们现在先简单了解下什么是对象。
Python从设计之初就是一门面向对象的语言,它有一个重要的概念,一切皆对象。数字、字符串、元组、列表、字典、函数、方法、类、模块文件等都是对象,包括我们编写的代码。
对象,也叫实例,可以理解为是一个记录和管理数据的容器,它的成员是属性(property, 变量,属性的值就是数据)和方法(method, 就是操作该对象以及对象内部数据的函数)。
像我们之前学习的字符串,就是一个对象,因此,我们之前可以通过字符串,调用字符串提供的操作来完成字符串的转换大小写,替换,分割等操作。当然,大部分对象都具备了

# 以下是对象的操作示例代码:
class Student(object):
    def __init__(self, name, age):
        self.name = name
        self.age  = age

    def introduction(self):
        print(f"我叫{self.name},今年{self.age}岁")

stu1 = Student(name="小明", age=16)
print(stu1)  # <__main__.Student object at 0x0000020F5A084DC0>
print(stu1.age)  # 16
stu1.introduction()  # 我叫小明,今年16岁

stu2 = Student(name="小白", age=13)
print(stu2)  # <__main__.Student object at 0x000001BC0C7B11F0>
print(stu2.age)  # 13
stu2.introduction()  # 我叫小白,今年13岁

从使用角度来说,能被for循环遍历输出内部每一个成员的都是可迭代对象。

从语法形式来说,能调用__iter__方法的数据对象就是可迭代对象。

我们先编写一个代码来看看python提供的可迭代对象。常见的有:str,list ,tuple,dict,set,文件管道。

ret = "hello".__iter__()
print(ret)  # <str_iterator object at 0x000002365279A520>
ret = [1,2,3].__iter__()
print(ret)  # <list_iterator object at 0x10221b150>
ret = {'name':'xiaoming'}.__iter__()
print(ret)  # <dict_keyiterator object at 0x1022180a8>
ret = {7,8,9}.__iter__()
print(ret)  # <set_iterator object at 0x1021ff9b0>

简单判断一个数据是否是可迭代对象。

set_data = {'a',2,'c','b'}
for i in setvar:
	print(i)
    
# dir() 获取对象所有内置方法
data = dir(set_data)
print(data)
print('__iter__' in data)  # True

迭代器

迭代器对象,简称迭代器,是增强版的可迭代对象。

任何一个对象,只要内部实现了__iter__()就是一个可迭代对象(iterable)。当然专业的说,就是实现了可迭代协议的对象。

任何一个对象,只要内部实现了__iter__()__next__()就是一个迭代器(iterator)。当然专业的说,就是实现了迭代器协议的对象。

所以,迭代器一定是可迭代对象,可迭代对象不一定是迭代器。

把一个可迭代对象变成迭代器对象有2种写法:

# 方式1:所有的可迭代对象都有一个__iter__(),该方法的作用就是把可迭代对象转换成迭代器对象。
可迭代对象.__iter__()

# 方式1:python内置函数iter转换,两个本质上一样,iter(可迭代对象) 就是 可迭代对象.__iter__()
iter(可迭代对象)

查看一个对象是否是可迭代对象或迭代器:

from collections.abc import Iterable, Iterator

data = [1, 2, 3, 4]
print(isinstance(data, Iterable))  # True       # 查看是不是可迭代对象
print(isinstance(data, Iterator))  # False      # 查看是不是迭代器
# 在 python中容器数据类型都是可迭代对象,但是并非迭代器,那么我们就可以进行装换。
# 方式1:
print(isinstance(data.__iter__(), Iterator))    # True
# 方式2:
print(isinstance(iter(data), Iterator))         # True

迭代器的特性就是能够调用__next__方法依次计算出迭代器中的每一个值。基于此就可以实现无论是否数据为序列对象,都可以通过迭代取值的方式完成读取成员值的操作。

set_data = {"A", "B", "D", 100}
# data = iter(set_data)    # 方法一
data = set_data.__iter__() # 方法二
print(data)
res = data.__next__()
print(res)
res = data.__next__()
print(res)
res = next(data) # next(data),就是 data.__next(),本质上没有区别,仅仅写法不同。
print(res)
res = next(data)
print(res)
# 迭代器中所有值都被提取完成后,再次取值,python会以抛出一个StopIteration异常告诉我们,没有值了。
# 这并不代表错误发生,而是一种迭代完成的标志,防止出现无限循环。
# res = next(data)

上面的代码操作可以发现,迭代器迭代取值的过程,并非通过索引而是通过__next__方法或next()函数来完成的。

for循环的本质

之前的学习只知道for循环是用来遍历某个数据对象的。但for循环内部到底是怎么工作的,关键字in后面可以放什么数据类型呢?

x = [1, 2, 3]
# for循环的形式:
for item in x:
    print(item)

关键字in后面数据对象data必须是可迭代对象。

for 循环首先会调用可迭代对象data内的__iter__方法返回一个迭代器,然后再调用这个迭代器的next方法将取到的值赋给item,即for后面定义的变量item。循环一次,调用一次next方法,直至遇到StopIteration异常,for循环内部捕获并处理该异常后结束迭代过程。

在这里插入图片描述

while循环模拟遍历过程

data = iter([10,20,30,40])
while True:
    item = data.__next__()
    print(item)

while循环模拟捕获处理异常过程

data = iter([10,20,30,40])
while True:
    try:
        item = data.__next__()
        print(item)
    except StopIteration:
        break

自定义迭代器

因为我们没有学到面向对象,所以以下代码,大家可以了解一下即可,该例子的用途仅仅只是为了演示迭代器的用途而已。

class Fib(object):

    def __init__(self, max):
        self.max = max
        self.n, self.a, self.b = 0, 0, 1

    def __iter__(self):
        return self

    def __next__(self):
        if self.n < self.max:
            r = self.b
            self.a, self.b = self.b, self.a + self.b  # 这次结果作为下次的初始值
            self.n = self.n + 1
            return r
        raise StopIteration()


for i in Fib(10):
    print(i)

可迭代对象与迭代器比较

可迭代对象的优点:

  1. 访问速度快。
  2. 访问方式灵活,可多次、重复、任意选择范围访问。
  3. 内置方法和函数比较多。

可迭代对象的缺点:

  1. 耗费内存,数据一次性存储在内存空间。
  2. 取值过于灵活,不限制方向,有时候会操作(例如:index out of range)

迭代器的优点:

  1. 惰性序列,惰性执行,挤牙膏,一次取一条数据,直到取值完毕
  2. 节省内存空间,可用于遍历读取大文件,海量数据。

迭代器的缺点:

  1. 访问速度慢。
  2. 内置操作比较少。
  3. 访问方式死板,方向不可逆,不能反复,只能向下取值。

生成器

生成器对象,简称生成器(generator)算得上是Python语言中最吸引人的特性之一。

生成器是一种特殊的迭代器,不过这种迭代器更加优雅。它不需要再像上面一样定义__iter__() __next__() 方法了,只需要在函数中使用到一个 yiled 关键字,所以也叫生成器函数。

生成器是一种特殊的迭代器,因此生成器也具备了迭代器的特点,例如惰性序列,惰性执行,节省内存空间等。

声明和使用

# 简单来说,一个函数里面只要出现了yield关键字,那么就是一个生成器函数。所以生成器函数的用法与函数类似,比函数功能更多。
def gen():
    yield 100
    yield 200
    yield 300

# 调用生成器函数,返回值是一个生成器对象
g1 = gen()
# 生成器对象只能用于迭代遍历,或者next()取值。
for item in g1:
    print(item)

# 每次调用生成器函数,都会产生一个新的生成器对象。
g2 = gen()
for item in g2:
    print(item)

yield

yield 是一个python内置的关键字,它的作用有一部分类似return,可以把函数内部的数据提供给外界调用处。但是不同的是,return 会终止函数的执行,yield 不会终止函数的执行,而是暂停了。两者都会返回一个结果,但return只能执行一次给函数的调用处返回值,而yield是可以执行多次给next()方法返回值,而且yield还可以接受外界send()方法的传值。所以更准确的来说,yield是暂停程序的执行权并记录了程序当前的运行状态的关键字,同时也是生成器内部与外界进行数据传递的通道

def gen(): # 生成器返回值
    yield "A", "B"
    yield "C", "D"
    yield "E", "F"

g1 = gen()

ret = g1.__next__()
print(ret)

ret = g1.__next__()
print(ret)

ret = g1.__next__()
print(ret)

# ret = g1.__next__()
# print(ret)

上面代码可以观察到,生成器函数调用时只会返回一个生成器对象。只有当生成器对象调用__next__方法时才会触发函数体代码执行,直到遇到关键字yield停止,将yield后的值作为返回值返回,所以,yield类似于return的功能,但不同于return的是,return返回,函数结束;而yield将函数的状态挂起,等待生成器对象再次调用__next__方法时,函数从挂起的位置后的第一条语句继续运行直到再遇见yield并返回其后的值;如果不断调用__next__方法,最后一次进入函数体,待执行代码不再有yield此时报出迭代异常的错误。

用生成器来实现斐波那契数列

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1

for i in fib(10):
    print(i)

next()仅可以从生成器中获取yield的返回值,而send()不仅可以从生成器中获取yield的返回值,还可以发送数据到生成器内部给上一个yield接收到。

def gen2():
    key = 0
    print(">>>>> 嘟嘟,开车了")
    while True:
        food = yield "第%s次" % key
        print(f"内部接收到了{food}")
        key +=1

g3 = gen2()
g3.send(None) # g3.__next__() 预激活生成器,让生成器内部执行到第一个yield位置,否则无法通过send传递数据给内部的yield
for item in ["苹果","芒果"]:
    res = g3.send(item)
    print(f"res={res}")

因为生成器中yield关键字的特征是暂停函数执行,所以可以让多个任务程序交替执行。这也是协程的实现原理,后面我们学习的异步编程里面协程课程内容基础。

import time
def gen1():
    while True:
        print("--1")
        yield
        print("--2")
        time.sleep(1)

def gen2():
    while True:
        print("--3")
        yield
        print("--4")
        time.sleep(1)

if __name__ == "__main__":
    g1 = gen1()
    g2 = gen2()
    for i in range(3):
        next(g1)
        print("主程序!")
        next(g2)

yield from

yield from可以将一个可迭代对象变成一个迭代器返回,也可以用于多个生成器之间进行嵌套调用。因此yield from 也叫委派生成器。

def gen():
	data = ["A","B","C","D"]
	# yield data
	yield from data

g1 = gen()
for item in g1:
	print(item)
def gen1():
    a = 0
    while True:
        # print("+++++++")
        a = yield a**2

def gen2(gen):
    yield from gen

if __name__ == '__main__':
    g1 = gen1()
    g2 = gen2(g1)
    g2.send(None)
    for i in range(5):
        # print(">>>> %s" % i)
        print(g2.send(i))

课堂作业

1. yieldreturn的区别都有哪些?

2. 可迭代对象、迭代器、生成器三者的关系?

推导式

推导式,实际上就是一种代码简写方式。是通过一行代码完成循环判断,并遍历出一系列数据的编写代码方式。语法:

成员 for 循环 ... if 判断 ...

推导式种类有4种:

# 1. 列表推导式,结果是一个列表
[item for item in Iterable]
# 2. 集合推导式,结果是一个集合
{item for item in Iterable}
# 3. 字典推导式,结果是一个字典
{a:b for a,b in iterable.items()}
# 4. 生成器表达式,结果是一个生成器
(item for item in Iterable)

列表推导式

# 1. 把列表中的每一个数字进行取整。
data = [3, 30.5, 9.99, 10.03]
ret  = [int(item) for item in data]
print(ret) # [3, 30, 9, 10]


# 2. 找出1~100之间的偶数
data = [i for i in range(101) if i % 2 == 0]
print(data)
# [0, 2, 4, ...., 100]

# 3. 找出列表中年龄大于12的男生
data = [
    {"name": "小明", "age": 12},
    {"name": "大明", "age": 11},
    {"name": "小白", "age": 13},
    {"name": "大白", "age": 15},
]
ret = [item["name"] for item in data if item["age"]>12]
print(ret) # ['小白', '大白']

# 4. 给两个列表成员的男生与女生配对,列出所有组合。
female = ["小白","小红","小花"]
male   = ["小黑","小绿","小草"]

lst = [(i, j) for i in female for j in male]
print(lst)
# [('小白', '小黑'), ('小白', '小绿'), ('小白', '小草'), ('小红', '小黑'), ('小红', '小绿'), ('小红', '小草'), ('小花', '小黑'), ('小花', '小绿'), ('小花', '小草')]

集合推导式

# 1. 计算列表中每个值的平方,自带去重功能
data = [1,-1,2,-3, 3]
ret = {item**2 for item in data}
print(ret) # {1, 4, 9}

# 2. 对列表中的所有数字进行取整,并去重保留偶数
data = [11.5, 10.5, 10.3, 11.8, 12.0]
ret = {int(item) for item in data if int(item) % 2 == 0}
print(ret) # {10, 12}

# 3. 找出列表中所有人的姓氏,重复去除。
data = ["张小明", "王小龙", "李大白", "李小龙", "张三风", "张无忌"]
ret = {item[0] for item in data}
print(ret) # {'李', '王', '张'}

字典推导式

# 1. 对字典中没有成员的值进行取整
data = {'B': 10.50, 'A': 50.33}
ret = {k: int(v) for k,v in data.items()}
print(ret)

# 2. 统计列表中各个字符出现的次数
data = ["A", "B", "C", "A", "C", "A"]
ret = {item: data.count(item) for item in data}
print(ret) # {'A': 3, 'B': 1, 'C': 2}


# 3. 把字典中大小写对应的成员进行数值相加,并把键改成大写
data = {'a': 10, 'b': 34, 'A': 7, 'C': 3, "c": 10}
ret = {k.upper(): data.get(k.lower(), 0) + data.get(k.upper(), 0) for k in data.keys()}
print(ret)

生成器表达式

# 获取1~10的整数的平方
N = (i**2 for i in range(1,11))

课堂作业

1. 把列表中所有字符变成小写
   data = ["ADDD","dddDD","DDaa","sss"]
2. 翻转字典成员的键值
   
3. 找到嵌套列表中名字,首尾是同一个字的人。
4. 把列表中长度小于3的字符串成员剔除
5. 生成所有大小写的字母列表
6. 有两个列表,分别存放了去过两个不同城市工作的人员名单
   beijing = ['小明','小白','小黑','小蓝','小五']
   shanghai =['小红','小白','小黑','小李']
   6.1. 取出既去过beijing又去过shanghai的名单列表
   6.2. 取出只去过beijing,而没有去过shanghai的名单列表
   6.3. 取出只去过shanghai,而没有去过beijing的名单列表

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/769230.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EasyCVR录像阈值配置未生效,是什么原因?

有用户反馈&#xff0c;在平台中设置了录像阈值不生效&#xff0c;导致磁盘爆满。针对该反馈&#xff0c;我们立即进行了排查。 EasyCVR基于云边端一体化架构&#xff0c;可支持多协议、多类型设备接入&#xff0c;在视频能力上&#xff0c;平台可实现视频直播、录像、回放、检…

用C语言对学生成绩进行排序(归并排序与基数排序)

一.前言 我们内部排序已经学了插入排序&#xff08;直接插入排序、折半插入排序、希尔排序&#xff09;&#xff0c;交换排序&#xff08;冒泡排序、快速排序&#xff09;&#xff0c;选择排序&#xff08;简单选择排序、堆排序&#xff09;&#xff0c;这些都属于内部排序&…

ShardingSphere分库分表实战之绑定表

&#x1f680; ShardingSphere &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&…

DAY48:动态规划(十二)完全平方数(类似零钱兑换)+单词拆分(注意背包思路!)

文章目录 279.完全平方数&#xff08;类似零钱兑换&#xff09;思路DP数组含义递推公式初始化遍历顺序 最开始的写法&#xff1a;有1个用例没过修改完整版总结 139.单词拆分&#xff08;递推公式注意&#xff09;思路1&#xff1a;遍历单词分割点DP数组含义递推公式初始化遍历顺…

基于Java+SpringBoot+Vue前后端分离旅游网站详细设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

导航菜单 改变背景色

直接参考官网上的案例即可 //active-text-color 点击时修改字体颜色 // background-color 背景色 // text-color 字体颜色<el-menudefault-active"2"class"el-menu-vertical-demo"open"handleOpen"close"handleClose"background…

Java:控制流程 + 数组 详解(原理 + 用法 + 例子)

目录 控制流程块作用域if 条件语句for while 循环switch 多重选择break continue 中断控制流程语句 大数值数组多维数组字符串类型数组Array.sort() 数组排序for each 循环 控制流程 块作用域 块&#xff08;即复合语句&#xff09;是指由一对大括号{}括起来的若干条简单的 Ja…

ARP解析MAC地址的全过程(ARP的工作机制)

目录 ARP解析MAC地址的过程&#xff1a; 源码等资料获取方法 以太网环境下&#xff0c;同一个网段的主机之间需要互相知道对方的MAC地址&#xff0c;才能访问。 TCP/IP协议栈从上层到下层的封装过程中&#xff0c;第三层封装需要知道目的IP&#xff0c;第二层封装需要知道目…

Linux下安装Mysql (CentOS 7) 详解

文章目录 前言环境检查查看是否安装MySql查看系统版本 源安装安装mysql的yum源官网下载从windows上传到linuxrz命令 方法2&#xff1a; 安装Mysql常见错误密钥问题安装后查看mysql是否可以工作查看是否安装成功启动服务 登录mysql配置文件方法&#xff08;免密码&#xff09; 使…

linux 安装 cuda

需求&#xff1a; inux 下安装 cuda 进程&#xff1a; 先查看一下系统版本 uname -a查看能支持什么版本的cudacuda toolkit 下载 wget https://developer.download.nvidia.com/compute/cuda/11.1.0/local_installers/cuda_11.1.0_455.23.05_linux.run sudo sh cuda_11.1.0_4…

MySql冷门但是很有用的语句

目录 1 查看当前的所有执行的进程 查看简略信息 查看详细信息 2 在所有数据库中查询包含某个字段的表 精确 模糊 1 查看当前的所有执行的进程 查看简略信息 show processlist 查看详细信息 show full processlist 终止进程 kill id 2 在所有数据库中查询包含某个字段…

gurobi安装vs配置gurobi

gurobi安装&vs配置gurobi 1、注册账号并登录 2、下载gurobi optimizer 3、获取license:User Portal (gurobi.com) online course可以免ip验证。 4、GENERATE NOW会生成&#xff0c;打开cmd进入gurobi安装路径&#xff08;如F:\gurobi1001\win64\bin>&#xff09;&am…

分布式事务 Seata

分布式事务 Seata 事务介绍分布式理论Seata 介绍Seata 部署与集成Seata TC Server 部署微服务集成 Seata XA 模式AT 模式AT 模式执行过程读写隔离写隔离读隔离 实现 AT 模式 TCC 模式TCC 模式介绍实现 TCC 模式 Saga 模式Seata 四种模式对比 事务介绍 事务&#xff08;Transac…

分布式光伏监控系统运维系统实时查看数据分布式光伏电站监控管理

光伏电站是一种利用太阳能发电的设施&#xff0c;随着人们对可再生能源的需求不断增加&#xff0c;光伏电站的建设也越来越普遍。但是&#xff0c;光伏电站的运营和管理需要高质量的监控系统来确保其正常运行。本文将介绍光伏电站监控系统的组成及其原理。 详细软件具体需求可…

php连接上mysql数据库该的配置方法

用mysql官方的管理工具workbench&#xff1a; 打开导出界面后&#xff0c;下一步&#xff0c;选择csv格式&#xff0c;导出后excel就能打开了 如果你需要在程序代码中导出&#xff0c;需要找到对应代码的excel处理库。 如php 的 phpExcel( 最新版已更名为 phpoffice/phpspread…

vue3组件中使用live2d看板娘(官方包形式)

文章目录 先看最终效果吧关于官方包下载使用 vue3中调整使用基础使用关于样式调整 vue中Html主页调试&#xff08;备用调试方案&#xff09; 先看最终效果吧 看着还可以&#xff0c;其实还有很多问题没解决&#xff0c;因为是完全靠js渲染&#xff0c;实际上这个live2d的canvas…

前端开发多人协作的团队项目时应该要配置的一些规则

本文主要记录了团队开发一个前端项目需要进行的一些前期配置&#xff0c;例如Eslint语法检验&#xff0c;prettierrc格式化规则&#xff0c;以及提交代码时的规则等等。 目录 1.搭建项目 2.Eslint配置&#xff08;代码检验工具&#xff09; 2.1安装Eslint 2.2配置Eslint 2…

【Ajax】笔记-取消请求

在进行AJAX(Asynchronous JavaScript and XML) 请求时&#xff0c;有时候我们需要取消正在进行的请求。取消请求可以帮助我们提高用户体验&#xff0c;病减少不必要的网络流量和服务器负载。 取消请求的方法 在AJAX请求中&#xff0c;我们可以使用以下方法来取消正在进行的请求…

【大虾送书第三期】进阶高级Python开发工程师,不得不掌握的Python高并发编程

目录 ✨写在前面 ✨主要内容 ✨本书特色 ✨关于作者 &#x1f990;博客主页&#xff1a;大虾好吃吗的博客 &#x1f990;专栏地址&#xff1a;免费送书活动专栏地址 写在前面 Python成为时下技术革新的弄潮儿&#xff0c;全民Python的发展趋势让人们不再满足于简单地运行Python…

MacBook Java开发环境搭建记录

一、Homebrew的镜像设置 对于Java JDK的安装&#xff0c;我们更推荐使用Homebrew来进行安装管理。但Homebrew的curl国外源的下载速度实在是一言难尽&#xff0c;时常还会发生无法访问的情况。 那么我们此时的解决方法就有两种了&#xff0c;第一种便是使用全局的VPN代理进行下载…