python常见的面试题目
一、python迭代器是什么
1、迭代是什么意思
汉语中,迭代的意思就是更替、轮换 。我们这里说的迭代就是 轮替的访问集合元素的一种方式,专业点就是遍历元素。
2、可迭代对象 (Iterable)
先说定义和怎么判断是不是可迭代对象
如果一个对象实现了__iter__方法,那么这个对象就是可迭代对象 可以通过isinstance方法来检测,列表、元组、字典、字符串和集合等都是可迭代对象。
from collections.abc import Iterable
class MyIterator:
def __init__(self, data):
self.index = 0
self.data = data
def __iter__(self):
pass
a=MyIterator(3)
print("a 是否是可以迭代对象", isinstance(a, Iterable))
#打印结果实 a 是否是可以迭代对象 True
python中我们最常用的就是 通过 for…in…的形式进行遍历元素,如果这个元素可以通过这个方式遍历,那么这个元素的数据类型就是可以迭代的,我们也把这个数据类型的数据 叫做可迭代对象。
当我们调用__iter__ 这个方法的时候会有下面的步骤. 总之目的就是为了创建一个迭代器。
- 检查对象是否实现了 iter 方法,如果实现了就调用它,获取一个迭代器
- 如果没有实现 iter 方法,但是实现了 getitem 方法,Python 会创建一个迭代
器,尝试按顺序(从索引 0 开始)获取元素。 - 如果尝试失败,Python 抛出 TypeError 异常,通常会提示“C object is not iterable”(C
对象不可迭代),其中 C 是目标对象所属的类。
3、迭代器(Iterator)
先说定义和怎么判断是不是迭代器
如果一个对象同时实现了__iter__方法和__next__方法,它就是迭代器
from collections.abc import Iterable
from collections.abc import Iterator
class MyIterator:
def __init__(self, data):
self.index = 0
self.data = data
def __iter__(self):
pass
def __next__(self):
pass
a = MyIterator(3)
print("a 是否是可以迭代对象:", isinstance(a, Iterable)) # 输出结果是:a 是否是可以迭代对象: True
print("a 是否是可以迭代器: ", isinstance(a, Iterator)) # 输出结果是:a 是否是可以迭代器: True
4、可迭代对象和迭代器的关系是什么
可迭代的对象和迭代器之间的逻辑的关系:Python 从可迭代的对象中获取迭代器。
迭代器一定是可迭代对象,反之则不成立,可迭代对象的__iter__方法必须返回一个迭代器
# 创建一个可迭代对象(列表)
my_list = [1, 2, 3, 4, 5]
# 使用iter()函数从可迭代对象中获取迭代器
my_iterator = iter(my_list)
# 使用迭代器的__next__()方法获取元素
print(next(my_iterator)) # 输出:1
print(next(my_iterator)) # 输出:2
print(next(my_iterator)) # 输出:3
# ... 可以继续调用next()直到引发StopIteration异常
既然说,Python 从可迭代的对象中获取迭代器,那就是说,iter调用可迭代对象,生成的对象会有next这个方法。
那这个next是怎么加上去的呢,可以得出,iter这个方法下,return的 是一个包含next命令的对象。很多时候,iter返回迭代器自身,即return self (这种情况,对象本身就是一个装饰器)或者return 一个新的迭代器
5、当iter返回的是迭代器自身的话(iter函数里 是 return self)
- 在这个例子中,SimpleIterator 类实现了迭代器协议。当我们创建一个 SimpleIterator 对象并尝试迭代它时,for 循环首先调用 iter 方法来获取迭代器。由于 iter 方法返回迭代器自身,因此 for 循环能够使用 next 方法来逐个访问元素。但是,一旦所有元素都被访问过,再次尝试迭代将不会输出任何内容,因为迭代器已经耗尽。
class SimpleIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
# 返回迭代器自身
return self
def __next__(self):
if self.index < len(self.data):
result = self.data[self.index]
self.index += 1
return result
else:
raise StopIteration
# 使用迭代器
it = SimpleIterator([1, 2, 3])
# 第一次迭代
for item in it:
print(item) # 输出: 1 2 3
# 尝试第二次迭代(将会失败,因为迭代器已经耗尽)
for item in it:
print(item) # 不输出任何内容,因为迭代器已经耗尽
6、当iter返回的是一个新的迭代器
-
在这个例子中,MyCollection 是一个可迭代对象,它有一个 iter 方法,该方法返回一个新的 MyCollectionIterator 实例。每次对 my_collection 进行迭代时,都会创建一个新的迭代器,因此可以多次迭代同一个 MyCollection 对象而不会受到之前迭代状态的影响。
-
而 MyCollectionIterator 类本身就是一个迭代器,它的 iter 方法返回自身。这是因为迭代器对象本身就是用于迭代的,不需要创建额外的迭代器对象。
-
总结来说,iter 方法返回的对象必须实现迭代器协议,即必须有一个 next 方法,并且当迭代结束时必须抛出 StopIteration 异常。对于迭代器类本身来说,它通常返回自身作为迭代器;而对于可迭代对象来说,它可能会返回一个新的迭代器对象来封装对内部数据的访问。
class MyCollection:
def __init__(self, data):
self.data = data
def __iter__(self):
# 创建一个新的迭代器对象,封装对 self.data 的访问
return MyCollectionIterator(self.data)
class MyCollectionIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
# 迭代器返回自身
return self
def __next__(self):
if self.index < len(self.data):
result = self.data[self.index]
self.index += 1
return result
else:
raise StopIteration
# 使用可迭代对象
my_collection = MyCollection([1, 2, 3])
# 第一次迭代
for item in my_collection:
print(item) # 输出: 1 2 3
# 尝试第二次迭代(将会成功,因为每次迭代都创建了一个新的迭代器)
for item in my_collection:
print(item) # 输出: 1 2 3
二、python生成器是什么
- 生成器的作用是,在需要用到一个东西的时候,生成需要的东西,而不是一下子都生成,从而会导致浪费资源。
- 生成器是一种特殊的迭代器,即也拥有 next和 iter 方法。
- python有两种方式提供生成器
- 生成器函数
- 生成器表达式
1、python有两种方式提供生成器------生成器函数
-
如果一个函数内部有yield这个关键字,那么该函数就是一个生成器函数,调用生成器函数的时候,虽然看上去是调用函数,实际上是创建一个 生成器对象 。这个yield的作用和就是return 的返回的作用,但是yield不会跳出函数外。只是会暂停再这个位置。
-
如果是第一次运行,即首次调用next()
- 如果是第一次运行,就从def 的位置开始执行,一直运行到 yield 停下来。
- 把yield 后面的关键字 返回。当作next()的返回值。
- 生成器暂停本次运行,直到再次被next调用。
-
如果不是第一次运行,即后续调用next()
- 如果不是第一次运行,就从上一次的yield 暂停的语句的下一句开始执行,即上一次的yield 的下一句开始执行。
- 调用next(),如果函数中存在其他yield语句,执行会再次在yield处暂停,并返回其后面的值。
- 继续调用next(),如果函数中没有更多的yield语句,或者已经执行到了函数的末尾,那么生成器会引发StopIteration异常,表示没有更多的值可以产生了。
-
通过这种方式,生成器允许我们逐步地、按需地从一个潜在的无限序列中获取值,而不需要一次性计算或存储整个序列。这使得生成器在处理大量数据或创建复杂迭代逻辑时非常高效。
def fib_generator():
print("---1---")
num1 = 1
num2 = 1
while True:
print("---2---")
temp_num = num1
print("---3---")
num1, num2 = num2, num1 + num2
print("---4---")
yield temp_num
print("---5---")
fib = fib_generator()
print("执行第1次的next函数---开始--")
num = next(fib)
print("执行第1次的next函数---结束--")
print("打印第1次next返回的结果--开始--")
print(num)
print("打印第1次next返回的结果--结束--")
print("执行第2次的next函数---开始--")
num = next(fib)
print("执行第2次的next函数---结束--")
print("打印第2次next返回的结果--开始--")
print(num)
print("打印第2次next返回的结果--结束--")
2、python有两种方式提供生成器------生成器表达式
生成器表达式很简单,只要把一个列表生成式的 [ ] 改成 ( )
nums = [x for x in range(5)]
print(type(nums))
print(nums)
nums2 = (x for x in range(5))
print(type(nums2))
print(nums2)
三、python生成器的优点体现简单的例子
既然说生成器是一种特殊的迭代器,先用迭代器实现一个功能,再用生成器实现一下,简单对比一下。
class FibIterator(object):
"""斐波那契数列迭代器"""
def __init__(self):
# num1用来保存前前一个数,初始值为数列中的第一个数1
self.num1 = 1
# num2用来保存前一个数,初始值为数列中的第二个数1
self.num2 = 1
def __next__(self):
"""被next()函数调用来获取下一个数"""
temp_num = self.num1
self.num1, self.num2 = self.num2, self.num1+self.num2
return temp_num
def __iter__(self):
"""迭代器的__iter__返回自身即可"""
return self
fib = FibIterator()
# 因为fib是迭代器所以不必使用iter()函数,直接使用next()函数即可
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
可以看到上面的代码,虽然能够实现功能,但是稍稍有些复杂,下面是使用的生成器,
def fib_generator():
num1 = 1
num2 = 1
while True:
temp_num = num1
num1, num2 = num2, num1+num2
yield temp_num
# 生成斐波那契数列
fib = fib_generator()
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
print(dir(fib)) #查看这个fib的方法,拥有iter和next就是迭代器