鱼C论坛

 找回密码
 立即注册
查看: 525|回复: 23

[已解决][课后作业] 第048讲:魔法方法:迭代器 | 课后测试题 动动手第1题求教

[复制链接]
发表于 2024-4-18 11:42:21 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
写一个迭代器,要求输出至今为止的所有闰年。如:
>>> leapYears = LeapYear()
>>> for i in leapYears:
        if i >=2000:
                print(i)
        else:
                break

2012
2008
2004
2000
import datetime as dt

class LeapYear:
    def __init__(self):
        self.now = dt.date.today().year

    def isLeapYear(self, year):
        if (year%4 == 0 and year%100 != 0) or (year%400 == 0):
            return True
        else:
            return False
        
    def __iter__(self):
        return self

    def __next__(self):
        while not self.isLeapYear(self.now):
            self.now -= 1

        temp = self.now
        self.now -= 1
        
        return temp

请问如果不引入datetime模块,如何做这个迭代器?
最佳答案
2024-4-18 12:11:48
本帖最后由 jackz007 于 2024-4-18 12:53 编辑
class LeapYear:
    def __init__(self , x) :               # 为 __init__() 添加一个参数,用于输入当前年份
        self . org = x
        self . now = x

    def reset(self) :                      # 添加此函数,用于重新初始化迭代器
        self . now = self . org

    def isLeapYear(self, year):
        if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
            return True
        else:
            return False
        
    def __iter__(self):
        return self

    def __next__(self) :
        while self . now >= 0 and not self . isLeapYear(self . now) :
            self . now -= 1
        if self . now >= 0 :
            self . now -= 1
        else:
            raise StopIteration()
        return self . now + 1
ax = LeapYear(2024)
for x in ax :
    print(x)
ax . reset()         # 重新初始化迭代器
for x in ax :
    print(x)

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2024-4-18 11:42:31 | 显示全部楼层
可以使用time模块来获取当前的年份。下面是不使用datetime模块的代码实现:

import time

class LeapYear:
    def __init__(self):
        self.now = time.localtime().tm_year

    def isLeapYear(self, year):
        if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
            return True
        else:
            return False
        
    def __iter__(self):
        return self

    def __next__(self):
        while not self.isLeapYear(self.now):
            self.now -= 1

        temp = self.now
        self.now -= 1
        
        return temp

leapYears = LeapYear()
for i in leapYears:
    if i >= 2000:
        print(i)
    else:
        break

输出结果与使用datetime模块的代码相同。注意,这里使用了time模块中的`time.localtime().tm_year`来获取当前年份。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-18 11:43:59 | 显示全部楼层
FishC 发表于 2024-4-18 11:42
可以使用time模块来获取当前的年份。下面是不使用datetime模块的代码实现:

import time

我的意思是不引用模块,只用代码进行书写,如何实现
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-18 11:44:11 | 显示全部楼层
15779327169 发表于 2024-04-18 11:43
我的意思是不引用模块,只用代码进行书写,如何实现


非常抱歉,我误解了您的问题。如果不引用任何模块,可以使用一个简单的算法来判断闰年并生成迭代器。下面是使用代码实现的例子:
class LeapYear:
    def __init__(self):
        self.now = 2022 # 设定当前年份为2022

    def is_leap_year(self, year):
        return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
        
    def __iter__(self):
        return self
    
    def __next__(self):
        while not self.is_leap_year(self.now):
            self.now -= 1
            
        temp = self.now
        self.now -= 1
        return temp


leap_years = LeapYear()
for year in leap_years:
    if year >= 2000:
        print(year)
    else:
        break

这个例子中,我们手动设定了当前年份为2022,然后使用`is_leap_year`函数判断是否为闰年。迭代器会返回每一个闰年的年份,直到年份小于2000为止。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-18 11:47:13 | 显示全部楼层
FishC 发表于 2024-4-18 11:44
非常抱歉,我误解了您的问题。如果不引用任何模块,可以使用一个简单的算法来判断闰年并生成迭代器。下 ...

请做一个简单的迭代器,并把代码里类和对象相互传递的步骤详细写出来,谢谢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-18 11:47:32 | 显示全部楼层
15779327169 发表于 2024-04-18 11:47
请做一个简单的迭代器,并把代码里类和对象相互传递的 ...


当我们使用迭代器时,类和对象之间会相互传递以实现迭代功能。下面是一个简单的迭代器示例,详细说明了类和对象之间的传递步骤。
class MyIterator:
    def __init__(self, max_num):
        self.max_num = max_num
        self.current_num = 0

    def __iter__(self):
        # 返回迭代器对象本身
        return self

    def __next__(self):
        if self.current_num < self.max_num:
            self.current_num += 1
            return self.current_num
        else:
            raise StopIteration

# 创建迭代器对象
my_iterator = MyIterator(5)

# 通过迭代器对象进行迭代
for num in my_iterator:
    print(num)

在这个例子中,我们定义了一个名为`MyIterator`的迭代器类。在类中,我们实现了`__iter__`和`__next__`方法。

- `__iter__`方法返回迭代器对象本身,即返回`self`。
- `__next__`方法用于获取迭代器的下一个元素。如果还有元素可迭代,它会返回下一个元素;否则,它会引发`StopIteration`异常。

接下来,我们按照以下步骤使用迭代器:

1. 创建迭代器对象:通过实例化`MyIterator`类创建一个迭代器对象`my_iterator`,并传入最大值参数为5。
2. 迭代:使用`for`循环来遍历迭代器对象`my_iterator`。在每次迭代时,会自动调用迭代器对象的`__next__`方法,获取下一个元素。
3. 打印结果:在每一次迭代过程中,打印当前元素`num`。

输出结果将会是:
1
2
3
4
5

通过这个例子,我们可以看到类和对象之间的相互传递,迭代器对象扮演着连接类和迭代过程的桥梁,使得我们能够方便地通过对象来进行迭代。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-18 11:57:57 | 显示全部楼层
FishC 发表于 2024-4-18 11:47
当我们使用迭代器时,类和对象之间会相互传递以实现迭代功能。下面是一个简单的迭代器示例,详细说明了 ...

很好,我理解了一些,但是如何让`MyIterator`类实例化的对象`my_iterator`能够重复使用,你提供的 for循环只能打印一次,无法多次使用,请改进,并说明实现方式
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-18 11:58:11 | 显示全部楼层
15779327169 发表于 2024-04-18 11:57
很好,我理解了一些,但是如何让`MyIterator`类实例化 ...


要让`MyIterator`类实例化的对象`my_iterator`能够重复使用,可以在`MyIterator`类中添加一个`reset()`方法,用于将迭代器的状态重置到初始状态。下面是改进后的代码示例:class MyIterator:    def __init__(self, max_num):        self.max_num = max_num        self.current_num = 0    def __iter__(self):        return self    def __next__(self):        if self.current_num < self.max_num:            self.current_num += 1            return self.current_num        else:            raise StopIteration    def reset(self):        self.current_num = 0# 创建迭代器对象my_iterator = MyIterator(5)# 第一次迭代for num in my_iterator:    print(num)    # 输出结果为:1 2 3 4 5# 重置迭代器对象my_iterator.reset()# 第二次迭代for num in my_iterator:    print(num)    # 输出结果为:1 2 3 4 5在改进后的代码中,我们添加了一个名为`reset`的方法,在该方法中将`current_num`重置为0,恢复到初始状态。通过调用`my_iterator.reset()`来重置迭代器的状态。这样,在第二次`for`循环中,迭代器对象`my_iterator`会重新开始迭代,输出相同的结果。通过这种方式,我们可以实现多次使用迭代器对象而不需要重新创建新的迭代器对象。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-18 12:11:48 | 显示全部楼层    本楼为最佳答案   
本帖最后由 jackz007 于 2024-4-18 12:53 编辑
class LeapYear:
    def __init__(self , x) :               # 为 __init__() 添加一个参数,用于输入当前年份
        self . org = x
        self . now = x

    def reset(self) :                      # 添加此函数,用于重新初始化迭代器
        self . now = self . org

    def isLeapYear(self, year):
        if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
            return True
        else:
            return False
        
    def __iter__(self):
        return self

    def __next__(self) :
        while self . now >= 0 and not self . isLeapYear(self . now) :
            self . now -= 1
        if self . now >= 0 :
            self . now -= 1
        else:
            raise StopIteration()
        return self . now + 1
ax = LeapYear(2024)
for x in ax :
    print(x)
ax . reset()         # 重新初始化迭代器
for x in ax :
    print(x)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-18 13:03:34 | 显示全部楼层

谢谢大神! 我慢慢学习消化
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-18 13:05:20 | 显示全部楼层
FishC 发表于 2024-4-18 11:58
要让`MyIterator`类实例化的对象`my_iterator`能够重复使用,可以在`MyIterator`类中添加一个`reset()` ...

请规范代码书写
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-18 13:05:44 | 显示全部楼层


当编写代码时,遵循一些规范和最佳实践可以提高代码的可读性和可维护性。以下是一些常见的代码书写规范:

1. 缩进:使用4个空格进行缩进,不要使用Tab键。

2. 行长度:每行代码尽量保持在80个字符以内。

3. 空格:在运算符和逗号周围添加空格,增加可读性。例如:`x = 10 + 5`。

4. 命名规范:变量名、函数名和类名使用小写字母,多个单词之间使用下划线分隔。例如:`my_variable`。

5. 注释:在代码中添加适当的注释来解释代码的目的和功能。注释应该清晰、简洁,并用于增加代码的可读性。

6. 函数和方法:为函数和方法添加文档字符串(docstring),描述其功能、参数和返回值等信息。

7. 异常处理:在可能引发异常的代码块中使用`try-except`语句进行异常处理,以防止程序崩溃。

8. 模块导入:将所有的导入语句放在文件的开头,按照标准库模块、第三方模块和自定义模块的顺序进行分组导入。

以下是一个示例代码,展示了如何按照规范书写代码:
# 导入模块
import os
import sys

# 定义函数,添加文档字符串
def add_numbers(a, b):
    """
    Returns the sum of two numbers.
    
    Args:
        a (int): The first number.
        b (int): The second number.
    
    Returns:
        int: The sum of the two numbers.
    """
    return a + b

# 主程序
if __name__ == '__main__':
    # 获取用户输入
    num1 = int(input("Enter the first number: "))
    num2 = int(input("Enter the second number: "))
    
    # 调用函数并打印结果
    result = add_numbers(num1, num2)
    print("The sum is:", result)

以上是一些常见的代码书写规范,当然还有其他的规范可以根据团队或个人的习惯进行调整。重要的是始终保持一致性,以便代码更易于阅读和理解。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-18 13:07:02 | 显示全部楼层
FishC 发表于 2024-4-18 13:05
当编写代码时,遵循一些规范和最佳实践可以提高代码的可读性和可维护性。以下是一些常见的代码书写规范 ...

不对,我的意思是你回答的
要让`MyIterator`类实例化的对象`my_iterator`能够重复使用,可以在`MyIterator`类中添加一个`reset()`方法,用于将迭代器的状态重置到初始状态。下面是改进后的代码示例:class MyIterator:    def __init__(self, max_num):        self.max_num = max_num        self.current_num = 0    def __iter__(self):        return self    def __next__(self):        if self.current_num < self.max_num:            self.current_num += 1            return self.current_num        else:            raise StopIteration    def reset(self):        self.current_num = 0# 创建迭代器对象my_iterator = MyIterator(5)# 第一次迭代for num in my_iterator:    print(num)    # 输出结果为:1 2 3 4 5# 重置迭代器对象my_iterator.reset()# 第二次迭代for num in my_iterator:    print(num)    # 输出结果为:1 2 3 4 5在改进后的代码中,我们添加了一个名为`reset`的方法,在该方法中将`current_num`重置为0,恢复到初始状态。通过调用`my_iterator.reset()`来重置迭代器的状态。这样,在第二次`for`循环中,迭代器对象`my_iterator`会重新开始迭代,输出相同的结果。通过这种方式,我们可以实现多次使用迭代器对象而不需要重新创建新的迭代器对象。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
请把代码写规范,而不是写出一排,我无法引用
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-18 13:07:20 | 显示全部楼层
15779327169 发表于 2024-04-18 13:07
不对,我的意思是你回答的
要让`MyIterator`类实例化 ...


非常抱歉造成了困扰,以下是规范书写的代码示例:
class MyIterator:
    def __init__(self, max_num):
        self.max_num = max_num
        self.current_num = 0
        
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.current_num < self.max_num:
            self.current_num += 1
            return self.current_num
        else:
            raise StopIteration
            
    def reset(self):
        self.current_num = 0

# 创建迭代器对象
my_iterator = MyIterator(5)

# 第一次迭代
for num in my_iterator:
    print(num)
    # 输出结果为:1 2 3 4 5

# 重置迭代器对象
my_iterator.reset()

# 第二次迭代
for num in my_iterator:
    print(num)
    # 输出结果为:1 2 3 4 5

在这个改进后的代码中,我们按照规范对代码进行了缩进、空格的调整,并适当添加了注释。希望这样能更好地满足您的需求。再次抱歉给您带来的不便!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-18 13:10:22 | 显示全部楼层
FishC 发表于 2024-4-18 13:07
非常抱歉造成了困扰,以下是规范书写的代码示例:

有什么方法可以在第一次迭代后自动初始化self.current_num = 0 而不需要在定义一个reset方法,通过调用reset方法实现初始化。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-18 13:10:39 | 显示全部楼层
15779327169 发表于 2024-04-18 13:10
有什么方法可以在第一次迭代后自动初始化self.current ...


您可以在 `__next__` 方法中添加一个判断语句,用于检查是否是第一次迭代。如果是第一次迭代,则将 `self.current_num` 设为 0。以下是相应的代码示例:
class MyIterator:
    def __init__(self, max_num):
        self.max_num = max_num
        self.current_num = None

    def __iter__(self):
        self.current_num = 0  # 在第一次迭代时初始化
        return self

    def __next__(self):
        if self.current_num is None:
            raise StopIteration

        if self.current_num < self.max_num:
            self.current_num += 1
            return self.current_num
        else:
            self.current_num = None  # 标记为结束迭代
            raise StopIteration


# 创建迭代器对象
my_iterator = MyIterator(5)

# 第一次迭代
for num in my_iterator:
    print(num)  # 输出结果为:1 2 3 4 5

# 第二次迭代
for num in my_iterator:
    print(num)  # 不输出任何结果,已结束迭代

在这个改进后的代码中,我们在 `__iter__` 方法中将 `self.current_num` 初始化为 0,以确保在第一次迭代之前已经被正确设置了。然后,在 `__next__` 方法中,我们使用 `self.current_num` 是否为 `None` 来判断是否是第一次迭代。如果是第一次迭代,则进行初始化操作。这样,不需要额外的 `reset` 方法,就能自动在第一次迭代后将 `self.current_num` 重置为 0。如果之后再次迭代,就会直接进入正常的迭代流程。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-18 13:14:01 | 显示全部楼层
FishC 发表于 2024-4-18 13:10
您可以在 `__next__` 方法中添加一个判断语句,用于检查是否是第一次迭代。如果是第一次迭代,则将 `se ...

还有没有更简单的方法? 比如在__iiter__ 或 __next__之后直接加入一个初始化self.current_num = 0的初始化
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-18 13:14:14 | 显示全部楼层
15779327169 发表于 2024-04-18 13:14
还有没有更简单的方法? 比如在__iiter__ 或 __next__ ...


很抱歉,上述的方法可能并不是最简单的。在 `__iter__` 或 `__next__` 方法后直接添加一个初始化语句是不行的,因为这些方法在每次迭代中都会被调用,而不仅仅是第一次迭代。

以下是一种更简单的方法,使用 `itertools.count()` 函数来取代手动实现的计数器:
import itertools

class MyIterator:
    def __iter__(self):
        self.counter = itertools.count(0)  # 使用itertools.count()生成计数器对象
        return self
    
    def __next__(self):
        self.current_num = next(self.counter)  # 从计数器对象中获取下一个数字
        if self.current_num > 5:  # 设置结束迭代的条件
            raise StopIteration
        return self.current_num

# 创建迭代器对象
my_iterator = MyIterator()

# 迭代输出
for num in my_iterator:
    print(num)  # 输出结果为:0 1 2 3 4 5

这里,我们使用了 `itertools.count()` 生成了一个无穷计数器对象 `self.counter`,然后每次调用 `__next__` 方法时,通过 `next(self.counter)` 来获取下一个数字。当达到结束迭代的条件时,我们使用 `raise StopIteration` 来终止迭代。这样就省去了手动控制和重置计数器的需要,使代码更加简洁。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-18 13:16:22 | 显示全部楼层
FishC 发表于 2024-4-18 13:14
很抱歉,上述的方法可能并不是最简单的。在 `__iter__` 或 `__next__` 方法后直接添加一个初始化语句是 ...

我用这种方法也可以实现对实例对象的重复调用,请说明原理
class MyIterator:
    def __init__(self, max_num):
        self.max_num = max_num
        self.current_num = 0  # 在第一次迭代时初始化
    def __iter__(self):
        self.current_num = 0
        return self

    def __next__(self):
        if self.current_num < self.max_num:
            self.current_num += 1
            return self.current_num
        else:
            raise StopIteration


# 创建迭代器对象
my_iterator = MyIterator(5)

# 第一次迭代
for num in my_iterator:
    print(num)  # 输出结果为:1 2 3 4 5

# 第二次迭代
for num in my_iterator:
    print(num)  # 不输出任何结果,已结束迭代
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-18 13:16:35 | 显示全部楼层
15779327169 发表于 2024-04-18 13:16
我用这种方法也可以实现对实例对象的重复调用,请说明 ...


这种方法可以实现对实例对象的重复调用的原理是,在每次迭代开始前,通过 `__iter__` 方法将迭代器的状态重置到初始状态。在第一次迭代时,我们会执行 `self.current_num = 0` 来重新初始化计数器。然后在 `__next__` 方法中,逐步增加计数器并返回当前数字,直到达到结束迭代的条件。

第二次迭代时,由于 `__iter__` 方法会将计数器重置为初始状态,所以 `self.current_num` 又被设置为0。当我们进行迭代时,由于计数器已经等于0,无法满足 `self.current_num < self.max_num` 的条件,因此立即触发 `raise StopIteration` 来结束迭代,不再输出结果。

这样就实现了对实例对象的重复调用,每次迭代都从初始状态开始。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-9-22 14:27

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表