【python--迭代生成器闭包面向对象继承多态】

🚀 作者 :“码上有前”
🚀 文章简介 :深度学习
🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬
在这里插入图片描述

python--迭代生成器闭包面向对象继承多态

  • 往期内容
  • 1.迭代
    • for...in
    • 字典的迭代
    • 列表迭代
  • 生成器
    • 推导式的弊端
    • 创建生成器
    • 生成器的迭代
    • 生成器的调用
    • 生成器对象值的获取
  • 迭代器
  • 高阶函数
    • map
    • reduce
    • filter
    • sorted
    • 匿名函数lambda
    • 装饰器
  • 模块与包
    • 模块
  • 面向对象
    • 访问控制
  • 继承与多态
    • 静态语言和动态语言
    • 小结

往期内容

【Python–vscode常用快捷键,必收藏!】
【Python–代码规范 】
【Python --优雅代码写法】
【Python–Python2.x与Python3.x的区别】
【Python–Web应用框架大比较】
【Python—内置函数】
【Python—六大数据结构】
【python–迭代生成器闭包面向对象继承多态】
【Python–定时任务的四种方法】
【Python–迭代器和生成器的区别】
【Python–读写模式全解】
【Python–高级教程】
【Python–网络编程之DHCP服务器】
【Python–网络编程之Ping命令的实现】
【Python–网络编程之TCP三次握手】

1.迭代

如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。
可迭代对象: 包括字符串,列表,元组,,集合,字典

from collections.abc import Iterable
is_str_Iterable = isinstance('abc', Iterable)  # str可迭代
print("字符串是否是可迭代对象:", is_str_Iterable)is_int_Iterable = isinstance(1234, Iterable)  # int不可迭代
print("整数是否是可迭代对象:", is_int_Iterable)is_float_Iterable = isinstance(1234.5, Iterable)  # float不可迭代
print("浮点数是否是可迭代对象:", is_float_Iterable)is_complex_Iterable = isinstance(1234+5j, Iterable)  # complex不可迭代
print("复数是否是可迭代对象:", is_complex_Iterable)is_list_Iterable = isinstance([], Iterable)  # list不可迭代
print("列表是否是可迭代对象:", is_list_Iterable)is_tuple_Iterable = isinstance((), Iterable)  # 元组可迭代
print("元组是否是可迭代对象:", is_tuple_Iterable)is_dict_Iterable = isinstance({}, Iterable)  # 字典可迭代
print("字典是否是可迭代对象:", is_dict_Iterable)# 判断生成器是否是可迭代对象
g = (x * x for x in range(10))
is_g_Iterable = isinstance(g, Iterable)  # 集合可迭代
print("生成器是否是可迭代对象:", is_g_Iterable)set_type = {2,32,'12','34'}
print("set_type类型", type(set_type))  # type类型 <class 'set'>
is_dict_Iterable = isinstance(set_type, Iterable)  # 集合可迭代
print("集合是否是可迭代对象:", is_dict_Iterable)# 验证一下
for s in set_type:print(s)

for…in

# d表示可迭代对象
d = {'a': 1, 'b': 2, 'c': 3}
for key in d:print(key)

字典的迭代

# 字典的item()
# 这个方法列表和元组都没有这个方法
dict = {"name":"li","school":"nux","city":"yinchuan"}
l = ['a', 'b', 'c']
tup = ('a', 'b', 'c')
for key,value in dict.items():print(key,value)for value in dict.values():print(f"value:{value}")for key in dict.keys():print(f"key:{key}")

列表迭代

# 提供了一种可列举的 键值对的方法enumerate
# 使用enumberate(['a', 'b', 'c'])将列表变成对应的索引对
for k, v in enumerate(['a', 'b', 'c']):print(k,v)

生成器

推导式的弊端

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
缺点:容量有限,访问元素开销大,浪费空间

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

创建生成器

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

L = [x * x for x in range(10)]
print(list(range(10)), type(list(range(10))))
print(L)g = (x * x for x in range(10))
print(g, type(g))  # <class 'generator'>
# <generator object <genexpr> at 0x00000229A6939A10>

创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。

我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

g = (x * x for x in range(10))
print(g, type(g))  # <class 'generator'>
# <generator object <genexpr> at 0x00000229A6939A10>
print(next(g)) # 0
print(next(g)) # 1
print(next(g)) # 4
# 没有更多的元素时,抛出StopIteration的错误。

当然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象

生成器的迭代

g = (x * x for x in range(10))
for n in g:print("生成器对象开始迭代",n)# 所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。# 举例 :斐波那契
def fib(max):n,a,b = 0,0,1while n<max:print("---",b)a,b = b,a+bn+=1return 'done'	fib(6)  

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

# 也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator函数,只需要把print(b)改为yield b就可以了
def fib(max):n, a, b = 0, 0, 1while n < max:yield b # print(b)a, b = b, a + bn = n + 1return 'done'f= f(6)
print(f,type(f)) # <generator object fib at 0x0000018D563F9A10> <class 'generator'># 这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator函数,调用一个generator函数将返回一个generator: 

生成器的调用

# 可以看到,odd不是普通函数,而是generator函数,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next(o)就报错。
def odd():print('step 1')yield 1print('step 2')yield(3)print('step 3')yield(5)next(odd())# step 1 1
next(odd()) # step 1 1next(odd())# step 1 1

生成器对象值的获取

def fib(max):n, a, b = 0, 0, 1while n < max:yield b # print(b)a, b = b, a + bn = n + 1return 'done'g= f(6)
# 将生成器函数赋值给一个变量,然后通过该变量进行遍历生成数
while True:try:x = next(g)print('g:', x)except StopIteration as e:print('Generator return value:', e.value)break

迭代器

我们知道不是生成器对象的str,list,tuple,set,dict都是可以迭代的。而生成器对象也是,有什么方法可以使不是生成器对象str,list,tuple,set,dict变成生成器对象

你可能会问,为什么list、dict、str等数据类型不是Iterator?

#这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。# Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

小结
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

高阶函数

# 既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
# 接受另一个函数作为参数的函数称为高阶函数

map

# 接受两个参数,一个是函数,另一个是iterable# map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。# 如求x的平方
def f(x):return x * xr = map(f, [1, 2, 3, 4])
print(r)  # <map object at 0x000001866456AB60>
l = list(r)
print(l, type(l))  # [1, 4, 9, 16] <class 'list'>

还可以计算任意复杂的函数,比如,把这个list所有数字转为字符串

l_s_l =  list(map(str,[1,2,3,4,5]))
print(l_s_l) # ['1', '2', '3', '4', '5']

reduce

# 再看reduce的用法。reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
# reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)# 在使用前需要导入from functools import reduce
from functools import reduce
# 序列求和reduce(add, [1, 3, 5, 7, 9])

filter

Python内建的filter()函数用于过滤序列。

和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

# 只保留奇数,删除偶数
def is_odd(n):return n % 2 == 1# <filter object at 0x0000025C70D5B8E0>
print(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))# 第一个参数是函数,第二个参数是序列
# 要使用list将filter变成序列
l_filter = list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
print(l_filter)  # [1, 5, 9, 15]

又一实例:计算素数 利用埃氏算法

# 计算素数
# 不断递增
def _odd_iter():n = 1while True:n = n + 2yield n# 定义筛选器
def _not_divisible(n):return lambda x: x % n > 0# 定义一个生成器 不断生成下一个
def primes():yield 2it = _odd_iter()  # 初始序列while True:n = next(it)  # 返回序列的第一个数yield nit = filter(_not_divisible(n), it)  # 构造新序列
# 这个生成器先返回第一个素数2,然后,利用filter()不断产生筛选后的新的序列。# 由于primes()也是一个无限序列,所以调用时需要设置一个退出循环的条件:
# 打印1000以内的素数:
for n in primes():if n < 1000:print(n)else:break
# 注意到Iterator是惰性计算的序列,所以我们可以用Python表示“全体自然数”,“全体素数”这样的序列,而代码非常简洁。

sorted

匿名函数lambda

装饰器

由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。


现在,假设我们要增强()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下

待定

模块与包

模块

在使用Python时,我们经常需要用到很多第三方库,例如,上面提到的Pillow,以及MySQL驱动程序,Web框架Flask,科学计算Numpy等。用pip一个一个安装费时费力,还需要考虑兼容性。我们推荐直接使用Anaconda,这是一个基于Python的数据处理和科学计算平台,它已经内置了许多非常有用的第三方库,我们装上Anaconda,就相当于把数十个第三方模块自动安装好了,非常简单易用。
廖雪峰

面向对象

面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

def print_score(std):print('%s: %s' % (std['name'], std['score']))

假设处理学生成绩可以通过函数实现,比如打印学生的成绩,面向过程考虑的是事件的处理流程。而面向对象首选思考的不是程序的执行流程,而是Student这种数据类型应该被视为一个对象,这个对象拥有name和score这两个属性(Property)。如果要打印一个学生的成绩,首先必须创建出这个学生对应的对象,然后,给对象发一个print_score消息,让对象自己把自己的数据打印出来。

所以,面向对象的设计思想是抽象出Class,根据Class创建Instance。
面向对象的抽象程度又比函数要高,因为一个Class既包含数据,又包含操作数据的方法。

访问控制

如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问

class Student(object):def __init__(self, name, score):# 通过_init_方法把self的属性变成私有的属性self.__name = nameself.__score = scoredef print_score(self):print('%s: %s' % (self.__name, self.__score))# 外部通过调用内部方法获取内部属性
# 但是如果外部代码要获取name和score怎么办?
# 可以给Student类增加get_name和get_score这样的方法:def get_name(self):return self.__namedef get_score(self):return self.__score# 外部通过修改内部方法获取内部属性
# 你也许会问,原先那种直接通过bart.score = 99也可以修改啊,
# 为什么要定义一个方法大费周折?因为在方法中,可以对参数做检查,避免传入无效的参数:def set_score(self, score):self.__score = score# 外界就不能调用了
bart = Student('Bart Simpson', 59)
bart.__name  # AttributeError: 'Student' object has no attribute '__name'# 需要注意的是,在Python中,变量名类似__xxx__的,
# 也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名。# 有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。# 双下划线开头的实例变量是不是一定不能从外部访问呢?
# 其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量:
# 但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把__name改成不同的变量名。# Python本身没有任何机制阻止你干坏事,一切全靠自觉。

继承与多态

继承有什么好处?最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法,因此,Dog和Cat作为它的子类,什么事也没干,就自动拥有了run()方法:

class Animal(object):def run(self):print('Animal is running...')# 对父类的方法进行改进
class Dog(Animal):def run(self):print('Dog is running...')def eat(self):print('Eating meat...')class Cat(Animal):passdog = Dog()
dog.run()# 当子类和父类都存在相同的run()方法时,
# 我们说,子类的run()覆盖了父类的run(),
# 在代码运行的时候,总是会调用子类的run()。
# 这样,我们就获得了继承的另一个好处:多态。

静态语言和动态语言

对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:

class Timer(object):def run(self):print('Start...')

这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

小结

继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写(覆写)

动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

列表推导式与生成表达式的区别

列表推导式与生成式表达式的区别&#xff1a; 列表推导式 res[i for i in range(6)] print(res) 结果&#xff1a; [0, 1, 2, 3, 4, 5] 生成表达式&#xff1a; res(i for i in range(6)) print(res) 结果&#xff1a; <generator object <genexpr> at 0x0000013EAD0…

【医学图像分割 2024】VM-UNet

文章目录 【医学图像分割 2024】VM-UNet摘要1. 介绍2. 预备知识3. 方法3.1 VM-UNet3.2 VSS Block3.3 损失函数 4. 实验4.1 数据集4.2 实现细节4.3 主要结果4.4 消融实验 5. 结果 【医学图像分割 2024】VM-UNet 论文题目&#xff1a;VM-UNet Vision Mamba UNet for Medical Imag…

mysql数据库 mvcc

在看MVCC之前我们先补充些基础内容&#xff0c;首先来看下事务的ACID和数据的总体运行流程 数据库整体的使用流程: ACID流程图 mysql核心日志: 在MySQL数据库中有三个非常重要的日志binlog,undolog,redolog. mvcc概念介绍&#xff1a; MVCC&#xff08;Multi-Version Concurr…

蓝桥杯真题:距离和

import java.util.*; public class 距离和 {public static void main(String[] args){String st"LANQIAO";int ans0;char ch[]st.toCharArray();for(int i0;i<ch.length;i){for(int ji1;j<ch.length;j){ansMath.abs(ch[j]-ch[i]);}}System.out.println(ans);} …

OS内存管理

内存 定义 内存是计算机用于存储数据和程序的硬件设备&#xff0c;它允许计算机快速读取和写入数据。内存通常是临时存储&#xff0c;其内容在计算机关闭或断电时会丢失。内存可存放数据。程序执行前需要先放到内存中才能被CPU处理(缓和CPU与硬盘之间的速度矛盾)。 在多道程…

问题:在额定电压500V以下的电路中,使用的各种用电设备,一般称为(_ _ _)用电设备 #媒体#媒体#媒体

问题&#xff1a;在额定电压500V以下的电路中,使用的各种用电设备,一般称为&#xff08;_ _ _)用电设备 参考答案如图所示

【动态规划】【记忆化搜索】【状态压缩】1681. 最小不兼容性

作者推荐 【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字 本文涉及知识点 动态规划汇总 状态压缩 记忆化搜索 1681. 最小不兼容性 给你一个整数数组 nums​​​ 和一个整数 k 。你需要将这个数组划分到 k 个相同大小的子集中&#xff0c;使得同一…

2024年广东省安全员C证第四批(专职安全生产管理人员)证模拟考试题库及广东省安全员C证第四批(专职安全生产管理人员)理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年广东省安全员C证第四批&#xff08;专职安全生产管理人员&#xff09;证模拟考试题库及广东省安全员C证第四批&#xff08;专职安全生产管理人员&#xff09;理论考试试题是由安全生产模拟考试一点通提供&#…

H5 色彩流动炫酷引导页源码

H5 色彩流动炫酷引导页源码 源码介绍&#xff1a;一款H5色彩流动炫酷引导页源码 下载地址&#xff1a; https://www.changyouzuhao.cn/9961.html

并发编程(1)基础篇

1 概览 1.1 这门课讲什么 这门课中的【并发】一词涵盖了在 Java 平台上的 进程线程并发并行 以及 Java 并发工具、并发问题以及解决方案&#xff0c;同时也会讲解一些其它领域的并发 1.2 为什么学这么课 我工作中用不到并发啊&#xff1f; 那你还是没有接触到复杂项目. …

Redis 数据类型及其常用命令一(string、list、set、zset、hash)

1、简介 Redis 的常用数据类型有十种&#xff0c;分别为&#xff1a;string、list、set、zset、hash、geo、hyperloglog、bitmap、bitfield、stream。熟练使用各种数据类型&#xff0c;能够快速结合场景进行使用。 注&#xff1a;我们所说的数据类型是指 value 的数据类型&…

linux系统配置zabbix监控agent端

目录 客户端配置 启动服务 浏览器工具设置 创建主机群组 创建主机 创建监控项 ​编辑 ​编辑 创建触发器 查看监控 客户端配置 rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm # yum clean allyum install -y zab…