当前位置: 首页 > news >正文

面向对象的使用

目录

  • 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, 参数列表):passdef 方法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 = nameself.color = colordef 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 = nameprint("%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 = namedef __str__(self):return "这是一只肥猫:%s" % self.nametom = 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)
重写 父类方法有两种情况:

  • 覆盖 父类的方法
    如果父类的方法实现 和 子类的方法实现,完全不同,就可以使用 覆盖 的方式,在子类中 重新编写 父类的方法实现, 重写之后,在运行时,只会调用 子类中重写的方法,而不再会调用 父类封装的方法。
    具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法

  • 对父类方法进行 扩展
    如果子类的方法实现 中 包含 父类的方法实现 ,父类原本封装的方法实现 是 子类方法的一部分,就可以使用 扩展 的方式

    1. 在子类中 重写 父类的方法
    2. 在需要的位置使用 super().父类方法 来调用父类方法的执行
    3. 代码其他的位置针对子类的需求,编写 子类特有的代码实现

关于 super

在 Python 中 super 是一个 特殊的类,super() 就是使用 super 类创建出来的对象, 常用在重写父类方法时,调用父类中封装的方法实现

在 Python 2.x 时,如果需要调用父类的方法,还可以使用以下方式:
父类名.方法(self)
这种方式,目前在 Python 3.x 还支持这种方式,但是这种方法 不推荐使用,因为一旦 父类发生变化,方法调用位置的 类名 同样需要修改

# 父(基)类
class Animal:def __init__(self, name):self.name = namedef 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):# 就是一个空语句,不做任何事情,一般用做占位语句passc = 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):passprint(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 = namedef 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 = namedef 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 = 0def __init__(self, name):self.name = name# 针对类属性做一个计数+1Tool.count += 1# 创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("铁锹")# 查看实例对象创建的个数
print("现在创建了 %d 个工具" % Tool.count)

输出结果:

现在创建了 3 个工具

属性的获取机制

在 Python 中 属性的获取 存在一个 向上查找机制, 首先会在实例对象内部查找,如果没有找到就会向上查找类对象的属性
因此,要访问类属性有两种方式:

  1. 类名.类属性
  2. 对象.类属性 (不推荐)

注意:
使用 对象.类属性 = 值 赋值语句,只会 给对象添加一个属性,而不会影响到 类属性的值

还是上面的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"passclass 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"passclass 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 = 0def __init__(self, name):self.name = name# 针对类属性做一个计数+1Tool.count += 1@classmethoddef 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):@staticmethoddef run():# 静态方法内无法访问类和实例的属性及方法print("狗在跑...")def __init__(self, name):self.name = namedog = 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):# 如果不返回任何结果,那么得到的实例是Noneprint("__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 = Nonedef __new__(cls, *args, **kwargs):# 1. 判断类属性是否已经被赋值if cls.single_instance is None:cls.single_instance = super().__new__(cls)# 2. 返回类属性的单例引用return cls.single_instanceplayer1 = 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 = Falsedef __init__(self):# 判断标记,没有初始化才进行初始化if not MusicPlayer.init_flag:print("初始化播放器")MusicPlayer.init_flag = Trueplayer1 = MusicPlayer()
player2 = MusicPlayer()

输出结果:

初始化播放器

从结果可以看出,虽然创建了2个对象, 但是__init__方法内的初始化操作仅初始化了一次。

14.迭代器

迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
认识可迭代的对象
我们已经知道可以对list、tuple、str等类型的数据使用for…in…的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代。
可以使用 isinstance() 判断一个对象是否是 Iterable 对象:

from typing import Iterabledef 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:# 遍历到最后一个元素的时候灰抛出这个异常breakif __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.itemsmyiter = MyIterator(self)return myiterclass MyIterator(object):def __init__(self, mylist):self.mylist = mylistself.current = 0def __iter__(self):return selfdef __next__(self):if self.current < len(self.mylist.items):value = self.mylist.items[self.current]# 更新当前位置self.current += 1return valueelse:# 没有可迭代的元素后,继续调用__next__方法就会抛出异常raise StopIterationdef 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()

相关文章:

面向对象的使用

目录1. 类和对象的概念类对象类和对象的关系2. 定义简单的类(只包含方法&#xff09;3. 创建对象4. self参数5. 类的初始化方法在初始化方法内部定义属性在初始化方法内部接收参数定义属性6. 类的内置方法使用__del__ 方法__str__ 方法7. 身份运算符is 与 区别&#xff1a;8. …...

LPDDR4x 的 学习总结(3) - SDRAM基本功能

上一节,我们重点介绍了array的存储结构。 本节介绍array周边的电路,对DDR的基本读写操作的相关功能模块的理解。 即通过哪些模块可以实现对ddr的基本读写。最简化的方式是把存储操作理解为行列选择&#xff0c;拆分为横竖两个纬度&#xff0c;最终实现对arrary进行读写。横向…...

设计模式(三)--适配器模式(Adapter Pattern)

将一个接口转换成客户希望的另一个接口&#xff0c;适配器模式使接口不兼容的那些类可以一起工作.比如我们日常开发中使用到的slf4j就使用了适配器模式&#xff0c;slf4j提供了一系列打日志的api,底层调用的是log4j或者logback来打日志&#xff0c;而作为调用者&#xff0c;不需…...

Web服务器基础介绍与Apache的简单介绍(LAMP架构与搭建论坛)

目录 Web服务器基础介绍 一.HTML是什么&#xff1f; 二.静态网页和动态网页 1.静态网页 2.动态网页 3.动态网页语言 三.HTTP协议 1.HTTP协议是什么&#xff1f; 2.HTTP方法 3.HTTP状态码 4.HTTP请求流程分析 4.1 请求报文 4.2 响应报文 Apache的简单介绍 一.Apa…...

Linux 进程:exec函数簇

目录&#xff08;1&#xff09;execl&#xff08;2&#xff09;execlp&#xff08;3&#xff09;execle&#xff08;4&#xff09;execv&#xff08;5&#xff09;execvp&#xff08;6&#xff09;execve在进程控制中提到&#xff0c;子进程的最大价值在于程序替换&#xff0c;…...

极简RSS订阅器Miniflux

什么是 Miniflux &#xff1f; Miniflux 是一个极简主义的 RSS 阅读器。它简单、快速、轻便且非常易于使用。Miniflux 是静态编译的单个二进制文件&#xff0c;没有使用任何复杂的框架&#xff0c;也没有外部依赖&#xff0c;简单、快速、轻巧且超级容易安装。支持 Atom、RSS 1…...

网络通信快速入门

&#x1f3e1;个人主页 &#xff1a; 守夜人st &#x1f680;系列专栏&#xff1a;Java …持续更新中敬请关注… &#x1f649;博主简介&#xff1a;软件工程专业&#xff0c;在校学生&#xff0c;写博客是为了总结回顾一些所学知识点 目录网络编程实现网络编程的三要素&#x…...

【阅读文档】Vue.js 2.0 之教程文档

系列文章目录 提示&#xff1a;阅读本章之前&#xff0c;请先阅读目录 文章目录系列文章目录前言兼容性Devtools开发版和生产版安装Vue-cli编译器Vue.js 是什么最基本的Vuev-bind 指令split 和 reverse 搭配v-model 双向绑定vue-component 定义组件v-bindObject.freeze箭头函数…...

Docker【基本使用】

1&#xff1a;启动Docker1.1&#xff1a;操作systemctl start docker.service1.2&#xff1a;常见问题【第一步】启动docker&#xff0c;提示启动失败&#xff0c;查询运行状态systemctl start docker.service【第二步】查询docker运行状态&#xff0c;提示不支持SELinux【第三…...

算法leetcode|39. 组合总和(rust重拳出击)

文章目录39. 组合总和&#xff1a;样例 1&#xff1a;样例 2&#xff1a;样例 3&#xff1a;提示&#xff1a;分析&#xff1a;题解&#xff1a;rustgoccpythonjava39. 组合总和&#xff1a; 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找…...

JavaSE学习笔记总结day18

今日内容 零、 复习昨日 一、作业 二、进程与线程 三、创建线程 四、线程的API 五、线程状态 六、线程同步 零、 复习昨日 晨考 一、作业 见答案 二、进程与线程[了解] 一个进程就是一个应用程序,进程包含线程 一个进程至少包含一个线程,大部分都是有多条线程在执行任务(多线…...

HybridFusion: LiDAR和视觉交叉源点云融合

一、基本信息 研究方向&#xff1a; 大场景点云配准 HybridFusion: 它可以在户外大型场景中从不同视角记录交叉源密集点云。 团队链接&#xff1a;http://www.adv-ci.com 视频链接&#xff1a; https://www.bilibili.com/video/BV1vM41147yD/?spm_id_from333.337.sear…...

走进JVM

JVM的位置 在操作系统之上&#xff0c;可以想象成一个软件&#xff0c;Java程序都运行在上面 JVM结构图 JVM调优的位置 99%的调优在堆中&#xff0c;极少数在方法区中 很多第三方插件都是在执行引擎那块地方做出修改而来&#xff0c;比如Lombook在程序运行时动态生成get/s…...

C语言-基础了解-15-C函数指针与回调函数

C函数指针与回调函数 一、函数指针 函数指针是指向函数的指针变量。 通常我们说的指针变量是指向一个整型、字符型或数组等变量&#xff0c;而函数指针是指向函数。 函数指针可以像一般函数一样&#xff0c;用于调用函数、传递参数。 函数指针变量的声明&#xff1a; type…...

react和vue在响应式上的不同理解

vue和react的区别总是被提及&#xff0c;关于这个问题最近也有了自己的想法。我认为它们之间最大的区别是对于响应数据变化的实现方式不同。 vue实现响应的方法是&#xff0c;首先收集依赖这个数据的副作用&#xff08;视图更新、计算属性等&#xff09;&#xff0c;当数据修改…...

多线程二 多线程了解与使用

文章目录synchronized 锁有两种synchronized异常捕获主线程和子线程volatile的作用notify是随机启动等待线程中的一个synchronized 锁有两种 类对象类的实例 第一种&#xff1a;锁类对象&#xff0c;有两种方式&#xff0c;如下&#xff1a; // 方法一&#xff1a;synchroni…...

嵌入式 Linux 的僵尸进程是什么?

目录 1、什么是僵尸进程&#xff1f; 2、僵尸进程的目的 3、怎么避免僵尸进程&#xff1f; 4、僵尸进程的处理方法 4.1 wait&#xff08;&#xff09;连接 4.2 waitpid&#xff08;&#xff09;函数 1、什么是僵尸进程&#xff1f; 首先内核会释放终止进程(调用了 exit …...

【刷题笔记】笔记一

1.自守数牛客链接解析&#xff1a;1.自守数的结尾肯定是 0&#xff0c;1&#xff0c;5&#xff0c;62.把数字转换为string类&#xff08;方便比较&#xff09;3.直接find在s2 里面 使用find查找另一个即可。#include <iostream> #include<string> using namespace …...

浏览器主页被hao123劫持的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。喜欢通过博客创作的方式对所学的知识进行总结与归纳,不仅形成深入且独到的理…...

华为OD机试题 - 热点网络统计(JavaScript)| 含代码编写思路

华为OD机试题 最近更新的博客使用说明本篇题解:热点网络统计题目输入输出描述示例一输入输出示例二输入输出Code解题思路华为OD其它语言版本最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...