面向对象的使用

news2024/11/16 8:41:40

目录

  • 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)
重写 父类方法有两种情况:

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

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

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

关于 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 中 属性的获取 存在一个 向上查找机制, 首先会在实例对象内部查找,如果没有找到就会向上查找类对象的属性
因此,要访问类属性有两种方式:

  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"
        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()

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Copula】考虑风光联合出力和相关性的Copula场景生成(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

文献资源最多的文献下载神器,99.99%的文献都可下载

用对工具事半功倍&#xff0c;查找下载文献用对工具能节约大量的时间和精力去做更多的事情。 文献党下载器&#xff08;wxdown.org&#xff09;&#xff0c;几乎整合了所有文献数据库资源&#xff0c;涵盖各种文献类型&#xff0c;包含全部学科。文献党下载器整合的资源如&…

Spring-Cloud-Gateway的过滤器的执行顺序问题

过滤器的种类 Spring-Cloud-Gateway中提供了3种类型的过滤器&#xff0c;分别是&#xff1a;路由过滤器、Default过滤器和Global过滤器。 路由过滤器和Default过滤器 路由过滤器和Default过滤器本质上是同一种过滤器&#xff0c;只不过作用范围不一样&#xff0c;路由过滤器…

【水下图像增强】Enhancing Underwater Imagery using Generative Adversarial Networks

原始题目Enhancing Underwater Imagery using Generative Adversarial Networks中文名称使用 GAN 增强水下图像发表时间2018年1月11日平台ICRA 2018来源University of Minnesota, Minneapolis MN文章链接https://arxiv.org/abs/1801.04011开源代码官方&#xff1a;https://gith…

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;…

Android动态权限获取官方实现之easypermission

Android动态权限获取官方实现之easypermission Android 6.0之后&#xff0c;基于用户隐私和安全考虑&#xff0c;敏感权限都开始采用动态运行时机制获取&#xff0c;于是就出现如果你不向用户申请权限&#xff08;弹窗&#xff0c;用户选择&#xff09;&#xff0c;有些功能就…

Flutter Android 打包保姆式全流程 2023 版

大家好&#xff0c;我是 17。 为什么要写这篇文章呢&#xff1f;对于一没有 android 开发经验&#xff0c;从未有过打包经历的新人来说&#xff0c;要想成功打包&#xff0c;是很困难的。因为受到的阻碍太多&#xff0c;是完全陌生的领域&#xff0c;几乎是寸步难行。如果有老…

hive真实表空间大小统计

1. 问题 如果是采用hdfs上传加载的表、或者是flume直接写hdfs的表空间通常看hive的属性是不准确的。 2. 思路 为了使结果更精确&#xff0c;我们直接使用linux下命令统计hive仓库目录下的每个表对应的文件夹目录占用空间的大小。 3. 解决方法 这里建立三层表结构 ods: 原始…

linux入门---粘滞位

为什么会有粘滞位 一台服务器有很多人使用&#xff0c;每个人在机器上都会有一个家目录&#xff0c;在家目录里可以实现自己想要的操作&#xff0c;但是有时候我们需要一个公共路径来完成一些操作&#xff0c;比如说资料分享产生临时文件的增删查改等等&#xff0c;这就好比我…

数据结构和算法学习记录——删除有序数组中的重复项、合并两个有序数组

去重删除有序数组中的重复项题目来自&#xff1a;https://leetcode.cn/problems/remove-duplicates-from-sorted-array/description/题目描述给你一个 升序排列 的数组 nums &#xff0c;请你原地删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数…

leetcode 71~80 学习经历

leetcode 71~80 学习经历71. 简化路径72. 编辑距离73. 矩阵置零74. 搜索二维矩阵75. 颜色分类76. 最小覆盖子串77. 组合78. 子集79. 单词搜索80. 删除有序数组中的重复项 II小结71. 简化路径 给你一个字符串 path &#xff0c;表示指向某一文件或目录的 Unix 风格 绝对路径 &am…

2023年再不会 IOC 源码,就要被淘汰了

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小黄&#xff0c;独角兽企业的Java开发工程师&#xff0c;CSDN博客专家&#xff0c;阿里云专家博主&#x1f4d5;系列专栏&#xff1a;Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙…

详细聊TTL电平、RS232接口和RS485接口

TTL电平 TTL&#xff08;Transistor-Transistor Logic&#xff09;是一种数字电平标准&#xff0c;常用于数字电子设备之间的数据传输。 TTL使用0V表示逻辑0&#xff0c;使用5V表示逻辑1&#xff0c;因此TTL信号的电平比较容易理解和处理。TTL信号传输距离较短&#xff0c;通…

文件服务设计

一、需求背景 文件的上传、下载功能是软件系统常见的功能&#xff0c;包括上传文件、下载文件、查看文件等。例如&#xff1a;电商系统中需要上传商品的图片、广告视频&#xff0c;办公系统中上传附件&#xff0c;社交类系统中上传用户头像等等。文件上传下载大致流程为&#…

【Java诊断工具】Arthas使用指南

文章目录一、简介二、解决什么问题 & 适用场景解决问题&#xff1a;适应场景&#xff1a;三、安装&启动四、相关命令五、来自天边的"海市蜃楼"六、结束一、简介 Arthas 是Alibaba开源的Java诊断工具。安装在系统所在服务器。通过全局视角实时查看应用 load、…

人工智能论文术语集39

Resume 简历&#xff08;但在AI领域一般指&#xff1a;复位&#xff09;Perform arithmetic operations 执行算数操作performance measurement and improvement techniques 性能测试和改进技术draw stuff with your mouse 用鼠标画东西the Color Palette 调色板high dynamic ra…

41 42Ping-Pong操作

提高电路吞吐率的结构——Ping-Pong操作 1.Ping-Pong操作原理 作用&#xff1a;为了让两个不匹配的模块进行对接&#xff0c;并且在对接的过程中让这两个模块能够同时工作&#xff0c;提高数据处理的吞吐率&#xff08;也称throughput效能&#xff09; 常见的不匹配&#xff1…

Semaphore 源码解读

一、Semaphore Semaphore 通过设置一个固定数值的信号量&#xff0c;并发时线程通过 acquire() 获取一个信号量&#xff0c;如果能成功获得则可以继续执行&#xff0c;否则将阻塞等待&#xff0c;当某个线程使用 release() 释放一个信号量时&#xff0c;被等待的线程如果可以成…

idea初学笔记

注:初学需安装idea专业版&#xff0c;方便学习使用idea运行内存配置从eclipse工具开发 转 idea工具开发&#xff0c;可设置idea快捷键同eclipse快捷键 file -> Settings -> Keymap -> 选择Eclipse -> OK设置idea项目整体编码格式file -> Settings -> Editor …

mysql五种索引类型---实操版本

背景 最近学习了Mysql的索引&#xff0c;索引对于Mysql的高效运行是非常重要的&#xff0c;正确的使用索引可以大大的提高MySql的检索速度。通过索引可以大大的提升查询的速度。不过也会带来一些问题。比如会降低更新表的速度&#xff08;因为不但要把保存数据还要保存一下索引…