目录
- 1. 类和对象的概念
- 类
- 对象
- 类和对象的关系
- 2. 定义简单的类(只包含方法)
- 3. 创建对象
- 4. self参数
- 5. 类的初始化方法
- 在初始化方法内部定义属性
- 在初始化方法内部接收参数定义属性
- 6. 类的内置方法使用
- `__del__` 方法
- `__str__` 方法
- 7. 身份运算符
- is 与 == 区别:
- 8. 私有属性和方法
- 9. 继承
- 单继承
- 继承的语法
- 继承的传递性
- 方法的重写
- 关于 super
- 父类的 私有属性 和 私有方法
- 多继承
- 多继承的使用注意事项
- 查看object类的内置方法
- 10. 多态
- 11. 类的结构
- 类对象
- 类属性的使用
- 属性的获取机制
- 类方法的使用
- 12. 静态方法
- 13. 单例
- 单例的实现步骤
- 初始化方法的调用次数
- 14.迭代器
- iter()函数与next()函数
- 自定义可迭代的对象
1. 类和对象的概念
类
类 是对一群具有 相同 特征 或者 行为 的事物的一个统称,是抽象的,不能直接使用
- 类的 特征 被称为 属性
- 类的 行为 被称为 方法
对象
对象 是由类创建出来的一个具体存在,可以直接使用,由哪一个类创建出来的对象,就拥有在 哪一个类 中定义的属性和方法
类和对象的关系
- 类是模板,对象 是根据 类 这个模板创建出来的,应该 先有类,再有对象
- 类 只有一个,而 对象 可以有很多个
- 类 中定义了什么 属性和方法,对象 中就有什么属性和方法
2. 定义简单的类(只包含方法)
在 Python 中要定义一个只包含方法的类,语法格式如下:
class 类名:
def 方法1(self, 参数列表):
pass
def 方法2(self, 参数列表):
pass
方法 的定义格式和之前学习过的函数 几乎一样,区别在于第一个参数必须是 self,稍后会介绍 self
注意
- 类名 的 命名规则 要符合 大驼峰命名法
- 调用类的方法时,不需要传递 self 参数
例如:
class Cat:
"""这是一个猫类"""
def eat(self):
print("小猫在吃鱼")
def drink(self):
print("小猫在喝水")
3. 创建对象
当一个类定义完成之后,要使用这个类来创建对象,语法格式如下:
对象变量 = 类名()
例如上面定义的Cat类之后创建对象可以这样操作:
tom = Cat()
tom.drink()
tom.eat()
注意:
在 Python 中使用类 创建对象之后,tom 变量中 仍然记录的是 对象在内存中的地址,也就是 tom 变量 引用 了 新建的Cat对象。
使用 print 输出 对象变量,默认情况下,是能够输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址(十六进制表示),例如:
<main.Cat object at 0x1031a0a00>
4. self参数
在 Python 中,要 给对象设置属性,非常的容易, 只需要 对象名 . 属性名 =xxx
的形式就可以设置一个属性了, 例如:
tom = Cat()
# 设置对象的属性 name
tom.name = "Tom"
print(tom.name)
但是这种在类的外部定义属性的方式是不推荐使用, 因为不利于维护, 另外在运行时,没有找到属性,程序会报错, 通常会将属性定义在类的内部, 稍后会介绍。
当调用对象的方法时, 方法的第一个参数self就指代当前调用者对象, 例如:
class Cat:
"""这是一个猫类"""
def eat(self, food):
print("self==> %s, 正在吃:%s" % (self, food))
tom = Cat()
tom.eat("鱼")
print("tom==>", tom)
输出结果:
self==> <__main__.Cat object at 0x102c74a00>, 正在吃:鱼
tom==> <__main__.Cat object at 0x102c74a00>
从结果可以看出self和tom的输出的引用指向的地址都是同一个对象, 由此也可以证明self代表的就是调用该方法的对象。
既然self指代的是当前类的对象, 那么自然可以通过self来访问该对象的 属性和方法
5. 类的初始化方法
当使用 类名()
创建对象时,会 自动 执行以下操作:
- 为对象在内存中 分配空间 —— 创建对象
- 为对象的属性 设置初始值 —— 初始化方法(init), 这个 初始化方法 就是
__init__
方法,__init__
是对象的内置方法, 它是专门用于给对象初始化属性使用的
在初始化方法内部定义属性
- 在
__init__
方法内部使用self.属性名 = 属性的初始值
就可以 定义属性 - 在定义属性时,如果 不知道设置什么初始值,可以设置为 None,None 关键字 表示 什么都没有,可以将 None 赋值给任何一个变量
- 定义属性之后,再使用 Cat 类创建的对象,都会拥有该属性
class Cat:
def __init__(self):
print("这是一个初始化方法")
# 定义用 Cat 类创建的猫对象都有一个 name 的属性
self.name = "Tom"
self.color = "黑色"
def eat(self):
print("%s 爱吃鱼" % self.name)
def print_info(self):
print("内部调用,name:%s,color%s" % (self.name, self.color))
# 使用类名()创建对象的时候,会自动调用初始化方法 __init__
tom = Cat()
tom.eat()
tom.print_info()
# 外部修改属性的值
tom.name = "Jack"
tom.color = "白色"
# 在类外面访问对象的属性
print("外部调用,name:%s,color:%s" % (tom.name, tom.color))
输出结果:
这是一个初始化方法
Tom 爱吃鱼
内部调用,name:Tom,color黑色
外部调用,name:Jack,color:白色
在初始化方法内部接收参数定义属性
如果希望在 创建对象的同时,就设置对象的属性,那么需要给__init__
方法添加参数
, 然后在创建对象时,使用 类名(属性1, 属性2...)
创建, 例如:
class Cat:
def __init__(self, name, color):
# 定义属性并使用参数赋值
self.name = name
self.color = color
def print_info(self):
print("当前对象:%s,name:%s,color%s" % (self, self.name, self.color))
# 使用初始化参数创建对象
tom = Cat("Tom", "黑色")
tom.print_info()
jack = Cat("Jack", "白色")
jack.print_info()
输出结果:
当前对象:<__main__.Cat object at 0x105304a00>,name:Tom,color黑色
当前对象:<__main__.Cat object at 0x105319160>,name:Jack,color白色
6. 类的内置方法使用
__del__
方法
当一个 对象被从内存中销毁 前,会 自动 调用__del__
方法, 一个对象的生命周期开始是从调用 类名() 创建时,而生命周期的结束就是__del__
回调的时候, 对象销毁后就不能再使用了.
class Cat:
def __init__(self, name):
# 定义属性并使用参数赋值
self.name = name
print("%s 创建了" % self.name)
def __del__(self):
print("%s 销毁了" % self.name)
# tom 是一个全局变量
tom = Cat("Tom")
# del 关键字可以删除一个对象
del tom
# 对象删除后,就不能再使用了,会报错
print(tom)
输出结果:
Tom 创建了
Tom 销毁了
Traceback (most recent call last):
File "/Users/chenyousheng/workspace/python/Learn/day01/main.py", line 128, in <module>
print(tom)
NameError: name 'tom' is not defined
__str__
方法
在 Python 中,使用 print 输出 对象变量,默认情况下,会输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址(十六进制表示)
如果希望使用 print 输出 对象变量 时,能够打印 自定义的内容,就可以利用 __str__
这个内置方法了, return的时候返回自定义的内容。
class Cat:
def __init__(self, name):
self.name = name
def __str__(self):
return "这是一只肥猫:%s" % self.name
tom = Cat("Tom")
print(tom)
输出结果:
这是一只肥猫:Tom
7. 身份运算符
身份运算符用于 比较 两个对象的 内存地址 是否一致 —— 是否是对同一个对象的引用
在 Python 中针对 None 比较时,建议使用 is 判断
is 与 == 区别:
is 用于判断 两个变量 引用对象是否为同一个
== 用于判断 引用变量的值 是否相等
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> b is a
False
>>> b == a
True
8. 私有属性和方法
在 定义属性或方法时,在 属性名或者方法名前 增加 两个下划线
,定义的就是 私有 属性或方法
- 私有属性 就是 对象 不希望公开的 属性
- 私有方法 就是 对象 不希望公开的 方法
class Women:
def __init__(self, name):
self.name = name
# 私有属性赋值
self.__age = 18
# 方法私有
def __secret(self):
print("我的年龄是 %d" % self.__age)
xiaofang = Women("小芳")
# 私有属性,外部不能直接访问
# print(xiaofang.__age)
# 私有方法,外部不能直接调用
# xiaofang.__secret()
有什么办法可以强制访问私有属性和方法?
在调用的时候在属性或者方法前加上_类名
, 例如上面代码中改成如下方式调用就可以访问了
xiaofang._Women__age
xiaofang._Women__secret()
因此,Python 中,并没有 真正意义 的 私有。
9. 继承
面向对象三大特性:
- 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中
- 继承 实现代码的重用,相同的代码不需要重复的编写
- 多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度
单继承
继承的概念:子类 拥有 父类 的所有 方法 和 属性
继承的语法
class 子类名(父类名):
- 子类 继承自 父类,可以直接 享受 父类中已经封装好的方法,不需要再次开发
- 子类 中应该根据 职责,封装 子类特有的 属性和方法
继承的传递性
C 类从 B 类继承,B 类又从 A 类继承, 那么 C 类就具有 B 类和 A 类的所有属性和方法
方法的重写
当 父类 的方法实现不能满足子类需求时,可以对方法进行 重写(override)
重写 父类方法有两种情况:
-
覆盖 父类的方法
如果父类的方法实现 和 子类的方法实现,完全不同,就可以使用 覆盖 的方式,在子类中 重新编写 父类的方法实现, 重写之后,在运行时,只会调用 子类中重写的方法,而不再会调用 父类封装的方法。
具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法 -
对父类方法进行 扩展
如果子类的方法实现 中 包含 父类的方法实现 ,父类原本封装的方法实现 是 子类方法的一部分,就可以使用 扩展 的方式- 在子类中 重写 父类的方法
- 在需要的位置使用 super().父类方法 来调用父类方法的执行
- 代码其他的位置针对子类的需求,编写 子类特有的代码实现
关于 super
在 Python 中 super 是一个 特殊的类,super() 就是使用 super 类创建出来的对象, 常用在重写父类方法时,调用父类中封装的方法实现
在 Python 2.x 时,如果需要调用父类的方法,还可以使用以下方式:
父类名.方法(self)
这种方式,目前在 Python 3.x 还支持这种方式,但是这种方法 不推荐使用,因为一旦 父类发生变化,方法调用位置的 类名 同样需要修改
# 父(基)类
class Animal:
def __init__(self, name):
self.name = name
def eat(self, food):
print("%s正在吃%s" % (self.name, food))
def drink(self):
print("%s正在喝水" % self.name)
def sleep(self):
print("%s正在睡觉" % self.name)
def run(self):
print("%s正在奔跑" % self.name)
# 子类
class Dog(Animal):
def bark(self):
print("%s正在吠..." % self.name)
# 重写父类的方法
def sleep(self):
print("%s趴在地上睡觉" % self.name)
# 孙子类
class XiaoTianDog(Dog):
# 扩展父类的方法
def bark(self):
print("%s往天上飞去" % self.name)
super().bark()
dog = Dog("旺财")
# 调用父类方法
dog.eat("热狗")
# 调用重载父类的方法
dog.sleep()
# 调用自己的方法
dog.bark()
xiao_tian_dog = XiaoTianDog("哮天犬")
# 调用扩展父类的方法
xiao_tian_dog.bark()
输出结果:
旺财正在吃热狗
旺财趴在地上睡觉
旺财正在吠...
哮天犬往天上飞去
哮天犬正在吠...
父类的 私有属性 和 私有方法
子类对象 不能 在自己的方法内部,直接 访问 父类的 私有属性 或 私有方法
子类对象 可以通过 父类 的 公有方法 间接 访问到父类的 私有属性 或 私有方法
多继承
子类 可以拥有 多个父类,并且具有 所有父类 的 属性 和 方法, 语法如下:
class 子类名(父类名1, 父类名2...):
多继承的使用注意事项
如果 不同的父类 中存在 同名的方法和属性,子类对象 在调用时,会调用 哪一个父类的呢?
Python 中针对 类 提供了一个 内置属性 __mro__
可以查看 方法 和 属性 的搜索顺序, MRO是 method resolution order,主要用于 在多继承时判断 方法、属性 的调用 路径
class A:
def __init__(self):
self.name = "A"
def test(self):
print("A test run...")
class B:
def __init__(self):
self.name = "B"
def test(self):
print("B test run...")
class C(A, B):
# 就是一个空语句,不做任何事情,一般用做占位语句
pass
c = C()
c.test()
print(c.name)
# 查看C类的方法和属性的搜索顺序
print(C.__mro__)
输出结果:
A test run...
A
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
从最后一个结果也可以看到此时C类的方法和属性的搜索顺序是: C>A>B
- 在搜索时,是按照 mro 的输出结果 从左至右 的顺序查找的
- 如果在当前类中 找到,就直接执行,不再搜索
- 如果 没有找到,就查找下一个类 中是否有,如果找到,就直接执行,不再搜索
- 如果找到最后一个类,还没有找到,程序报错
注意:
在 Python 3.x 中定义类时,如果没有指定父类,会 默认使用 object 作为该类的 基类 而Python 2.x则不会, 因此为了保证编写的代码能够同时在 Python 2.x 和 Python 3.x 运行!如果没有父类,建议统一继承自 object
class 类名(object):
查看object类的内置方法
class D(object):
pass
print(dir(D()))
输出结果:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
10. 多态
多态 更容易编写出出通用的代码,做出通用的编程,以适应需求的不断变化!
多态是以 继承 和 重写父类方法 为前提的
class Dog(object):
def __init__(self, name):
self.name = name
def game(self):
print("%s 蹦蹦跳跳的玩耍..." % self.name)
class XiaoTianDog(Dog):
def game(self):
print("%s 飞到天上去玩耍..." % self.name)
class Person(object):
def __init__(self, name):
self.name = name
def game_with_dog(self, dog):
print("%s 和 %s 快乐的玩耍..." % (self.name, dog.name))
# 让狗玩耍
dog.game()
# 1. 创建狗对象
wangCai = Dog("旺财")
xiaoTian = XiaoTianDog("哮天犬")
# 2. 创建一个小明对象
xiaoming = Person("小明")
# 3. 让小明调用和狗玩的方法
xiaoming.game_with_dog(wangCai)
xiaoming.game_with_dog(xiaoTian)
输出结果:
小明 和 旺财 快乐的玩耍...
旺财 蹦蹦跳跳的玩耍...
小明 和 哮天犬 快乐的玩耍...
哮天犬 飞到天上去玩耍...
11. 类的结构
- 每一个对象 都有自己 独立的内存空间,保存各自不同的属性
- 多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用 传递到方法内部
类对象
Python 中 一切皆对象:
- class AAA: 定义的类属于 类对象,在程序运行时,类 同样会被加载到内存,在程序运行时,类对象 在内存中 只有一份,使用 一个类 可以创建出 很多个对象实例
- obj1 = AAA() 属于 实例对象
类对象除了封装 实例 的 属性 和 方法外,类对象 还可以拥有自己的 属性 和 方法
- 类属性
- 类方法
通过类名.
的方式可以 访问类的属性 或者 调用类的方法
类属性的使用
类属性 不会用于记录 具体对象的特征,通常用来记录 与这个类相关 的特征
class Tool(object):
# 使用赋值语句,定义类属性,记录创建工具对象的总数
count = 0
def __init__(self, name):
self.name = name
# 针对类属性做一个计数+1
Tool.count += 1
# 创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("铁锹")
# 查看实例对象创建的个数
print("现在创建了 %d 个工具" % Tool.count)
输出结果:
现在创建了 3 个工具
属性的获取机制
在 Python 中 属性的获取 存在一个 向上查找机制
, 首先会在实例对象内部查找,如果没有找到就会向上查找类对象的属性
因此,要访问类属性有两种方式:
- 类名.类属性
- 对象.类属性 (不推荐)
注意:
使用对象.类属性 = 值
赋值语句,只会 给对象添加一个属性,而不会影响到 类属性的值
还是上面的Tool类为例:
# 创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("铁锹")
# 这种方式只会添加一个实例属性,而不会修改类属性
tool3.count = 100
# 查看实例对象创建的个数
print("现在创建了 %d 个工具" % Tool.count)
print("tool1-->count:%s" % tool1.count)
print("tool2-->count:%s" % tool2.count)
print("tool3-->count:%s" % tool3.count)
输出结果:
现在创建了 3 个工具
tool1-->count:3
tool2-->count:3
tool3-->count:100
注意:
- 当对象的实例属性和类属性同名时, 通过实例对象调用那么会优先调用实例属性, 使用类名调用会优先调用类属性
- 当存在继承关系时, 优先查找子类, 搜索属性如下:
子类实例属性>父类实例属性>子类类属性>父类类属性
class A:
name = "classA"
def __init__(self):
self.name = "A"
class B(A):
name = "classB"
def __init__(self):
super().__init__()
self.name = "B"
b = B()
print(b.name)
print(B.name)
输出结果:
B
classB
修改如下:
class A:
name = "classA"
def __init__(self):
self.name = "A"
class B(A):
name = "classB"
def __init__(self):
super().__init__()
# self.name = "B"
b = B()
print(b.name)
print(B.name)
输出结果:
A
classB
再次修改:
class A:
name = "classA"
def __init__(self):
# self.name = "A"
pass
class B(A):
name = "classB"
def __init__(self):
super().__init__()
# self.name = "B"
b = B()
print(b.name)
print(B.name)
输出结果:
classB
classB
继续修改:
class A:
name = "classA"
def __init__(self):
# self.name = "A"
pass
class B(A):
# name = "classB"
def __init__(self):
super().__init__()
# self.name = "B"
b = B()
print(b.name)
print(B.name)
输出结果:
classA
classA
类方法的使用
类方法 就是针对 类对象
定义的方法,在 类方法 内部可以直接访问 类属性 或者调用其他的 类方法
语法如下:
@classmethod
def 类方法名(cls):
- 类方法需要用 修饰器
@classmethod
来标识,告诉解释器这是一个类方法 - 类方法的 第一个参数 应该是 cls
- 由 哪一个类 调用的方法,方法内的 cls 就是 哪一个类的引用
- 这个参数和 实例方法 的第一个参数是 self 类似
- 使用其他名称命名也可以,不过习惯使用 cls
- 通过 类名.类方法名调用,调用方法时,不需要传递 cls 参数
- 类方法也支持通过实例对象调用
- 在方法内部可以通过 cls. 访问类的属性,也可以通过 cls. 调用其他的类方法
class Tool(object):
# 使用赋值语句,定义类属性,记录创建工具对象的总数
count = 0
def __init__(self, name):
self.name = name
# 针对类属性做一个计数+1
Tool.count += 1
@classmethod
def show_tool_count(cls):
"""显示工具对象的总数"""
print("工具对象的总数 %d" % cls.count)
# 创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("铁锹")
# 查看实例对象创建的个数
# 通过类名调用
Tool.show_tool_count()
# 通过实例对象调用
tool1.show_tool_count()
输出结果:
工具对象的总数 3
工具对象的总数 3
12. 静态方法
如果需要在 类 中封装一个方法,这个方法既 不需要 访问 实例属性 或者调用 实例方法,也不需要访问 类属性 或者调用 类方法,那么这个方法封装成一个 静态方法
语法如下:
@staticmethod
def 静态方法名():
- 静态方法 需要用 修饰器
@staticmethod
来标识,告诉解释器这是一个静态方法 - 通过 类名. 调用 静态方法,当然也可以使用实例 对象. 来调用
- 静态方法内无法访问类和实例的属性及方法
class Dog(object):
@staticmethod
def run():
# 静态方法内无法访问类和实例的属性及方法
print("狗在跑...")
def __init__(self, name):
self.name = name
dog = Dog("旺财")
dog.run()
Dog.run()
13. 单例
单例是一种设计模式, 是让类创建的对象,在系统中只有唯一的一个实例,在介绍python的单例时,我们先来了解下python的__new__方法
__new__方法
使用 类名() 创建对象时,Python 的解释器 首先 会 调用 __new__
方法为对象 分配空间。
__new__
是一个 由 object 基类提供的 内置的静态方法,主要作用有两个:
- 在内存中为对象 分配空间
- 返回 对象的引用
Python 的解释器获得对象的 引用 后,将引用作为 第一个参数 self,传递给__init__
方法, 重写__new__
方法 一定要return super().__new__(cls)
否则 Python 的解释器 得不到 分配了空间的 对象引用,就不会调用对象的初始化方法
注意:
__new__
是一个静态方法,在调用时需要 主动传递 cls 参数
class MusicPlayer(object):
def __new__(cls, *args, **kwargs):
# 如果不返回任何结果,那么得到的实例是None
print("__new__被调用")
return super().__new__(cls)
def __init__(self):
print("__init__被调用...初始化音乐播放对象")
player = MusicPlayer()
print(player)
输出结果:
__new__被调用
__init__被调用...初始化音乐播放对象
<__main__.MusicPlayer object at 0x100a31a30>
单例的实现步骤
- 定义一个 类属性,初始值是 None,用于记录 单例对象的引用
- 重写
__new__
方法 - 如果 类属性 is None,调用父类方法分配空间,并在类属性中记录结果
- 返回 类属性 中记录的 对象引用
class MusicPlayer(object):
# 定义类属性记录单例对象引用
single_instance = None
def __new__(cls, *args, **kwargs):
# 1. 判断类属性是否已经被赋值
if cls.single_instance is None:
cls.single_instance = super().__new__(cls)
# 2. 返回类属性的单例引用
return cls.single_instance
player1 = MusicPlayer()
player2 = MusicPlayer()
print(player1)
print(player2)
print(player1 == player2)
输出结果:
<__main__.MusicPlayer object at 0x104b15850>
<__main__.MusicPlayer object at 0x104b15850>
True
初始化方法的调用次数
使用 类名() 创建对象时,Python 的解释器都会自动调用两个方法:
__new__
分配空间__init__
对象初始化
有没有什么办法可以让__init__
方法只调用一次?
__init__
方法的回调次数是没有办法左右的,但是我们可以让__init__
方法内的初始化逻辑控制在只执行一次。可以给类对象添加一个类属性作为标记, 当__init__
方法调用的时候判断下标记, 避免__init__
方法内部逻辑调用多次, 示例代码如下:
class MusicPlayer(object):
# 记录是否执行过初始化动作
init_flag = False
def __init__(self):
# 判断标记,没有初始化才进行初始化
if not MusicPlayer.init_flag:
print("初始化播放器")
MusicPlayer.init_flag = True
player1 = MusicPlayer()
player2 = MusicPlayer()
输出结果:
初始化播放器
从结果可以看出,虽然创建了2个对象, 但是__init__方法内的初始化操作仅初始化了一次。
14.迭代器
迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
认识可迭代的对象
我们已经知道可以对list、tuple、str等类型的数据使用for…in…的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代。
可以使用 isinstance() 判断一个对象是否是 Iterable 对象:
from typing import Iterable
def main():
print(isinstance([], Iterable))
print(isinstance({}, Iterable))
print(isinstance('abc', Iterable))
print(isinstance(100, Iterable))
if __name__ == '__main__':
main()
输出结果:
True
True
True
False
iter()函数与next()函数
list、tuple等都是可迭代对象,我们可以通过**iter()函数获取这些可迭代对象的迭代器。然后我们可以对获取到的迭代器不断使用next()**函数来获取下一条数据。iter()函数实际上就是调用了可迭代对象的__iter__
方法。
for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。
def main():
my_list = [1, 2, 3, 4]
# 获得迭代器
it = iter(my_list)
while True:
try:
# 获取下一个元素
element = next(it)
print(element)
except StopIteration:
# 遍历到最后一个元素的时候灰抛出这个异常
break
if __name__ == '__main__':
main()
自定义可迭代的对象
步骤如下:
- 可迭代的对象需要实现__iter__方法,内部持有迭代器对象,通过该方法返回迭代器对象
- 迭代器对象持有可迭代的容器对象和记录当前迭代位置的变量,需要实现__next__方法,在该方法内实现迭代操作; 同时迭代器对象也需要实现__iter__方法,返回自身即可
代码实现如下:
class MyList(object):
def __init__(self):
self.items = []
def add(self, val):
self.items.append(val)
def __iter__(self):
# 注意: 这里传self即可,不需要传self.items
myiter = MyIterator(self)
return myiter
class MyIterator(object):
def __init__(self, mylist):
self.mylist = mylist
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current < len(self.mylist.items):
value = self.mylist.items[self.current]
# 更新当前位置
self.current += 1
return value
else:
# 没有可迭代的元素后,继续调用__next__方法就会抛出异常
raise StopIteration
def main():
my_list = MyList()
my_list.add(1)
my_list.add(2)
my_list.add(3)
for value in my_list:
print(value)
if __name__ == '__main__':
main()