13.Python 类

目录

    • 1. 类的基础
    • 2. 类的生命周期
    • 3. 类的成员
      • 3.1 属性和方法
      • 3.2 方法装饰器
      • 3.3 属性装饰器
      • 3.4 构造属性
      • 3.5 内置成员
    • 4.类的特性
      • 4.1 封装
      • 4.2 继承
      • 4.3 组合
      • 4.4 扩展
      • 4.5 多态
    • 5. 迭代器

面向过程就是分析解决问题所需要的步骤,然后用函数把这些步骤一步一步地实现,当使用时依次调用即可;面向过程通过函数紧密关联。

面向对象就是把构成问题的事务分门别类进行抽象,再实例化为具体的对象,最后通过对象的行为来解决问题。面向对象是通过类(class)和对象(object)等概念得以体现。

所有内容都被视为对象,类是对象的模型,拥有继承、封装和多态等基本特性。

1. 类的基础

使用class语句定义类,语法格式如下:

class <Name>:
		<statement>

类结构由首行和代码块两部分组成:

  • 首行以class关键字开头,空格之后,跟随类名(Name),以冒号结束。类名一般首写字母大写。在类名后面可以添加小括号(),在小括号种包含零个或多个可选的基类。
  • 代码块是类的主体部分,类主体由各种类成员构成。代码块一般换行、缩进显示。如果是单句,可以与首行并行显示,不需要缩进。

class 语句与其他语句一样,都是实时执行的,没有独立的编译时间。当运行时,将创建一个类对象,并赋值给类名的变量。

与函数结构一样,在类的主体代码块第一行可以使用’’'或”””添加注释,用来指定类的帮助信息,在创建类实例时,可以提示帮助信息。

class No: # 空类
		pass 
# 或者
class No:pass

class Student: # 类
    name = '简单的类' # 类属性
    def hi(self): # 实例方法
        return 'Hi,Python'

与函数一样,使用小括号可以调用类,返回一个实例对象。语法和示例如下:

obj = Name()

class Student: # 类
    name = '张三' # 类属性
    def who(self): # 实例方法
        return self.name
obj = Student() # 实例类
print(obj.who()) # 调用实例方法,输出 张三

在类结构中,包含self参数的函数称为实例方法,实例方法的第一个参数指向实例对象,通过类或实例对象可以访问类的成员。self代表类的实例,而不是类自身。通过self.__class__可以访问类。

2. 类的生命周期

在类的生命周期的不同时间点和应用环境被自动调用,俗称类的魔术方法。灵活使用魔术方法,可以增强类的功能。

构造函数

__init__()表示构造函数,用于初始化类,当使用小括号语法实例化类时被自动调用。构造函数主要用于完成类的初始化配置,如设置初始值、配置运行环境等。

'''
设计一个圆类,该类可以显示圆的位置和大小,
并能够计算圆的面积和周长,同时允许对圆的位置和半径进行修改。
在构造函数中,初始化圆的坐标和半径。
'''

class Circle: # 定义圆类
    def __init__(self,x,y,r): # 初始化函数
        self.x = x # x轴坐标
        self.y = y # y轴坐标
        self.r = r # 半径
    def get_position(self): # 获取圆位置函数
        return (self.x,self.y) # 位置信息以元组方式返回
    def set_position(self,x,y): # 设置圆位置函数
        self.x = x # 设置x轴
        self.y = y # 设置y轴
    def get_area(self): # 计算圆面积方法
        return 3.14*self.r**2
    def get_circumference(self): # 计算圆周长方法
        return 2*3.14*self.r
    
circle = Circle(2,4,4) # 实例化圆类
area = circle.get_area()
circumference = circle.get_circumference()
print('圆的面积:%d'%area) # 圆的面积:50
print('圆的周长:%d'%circumference) # 圆的周长:25
print('圆的初始位置:',circle.get_position()) # 圆的初始位置: (2, 4)
circle.set_position(3,4)
print('修改后圆的位置:',circle.get_position()) # 修改后圆的位置: (3, 4)

实例化函数

__new__()表示实例化函数,该函数在类实例化时被自动调用。与__init__()函数比较如下:

  • 功能不同:__new__()函数负责设计实例对象,而__init__()函数负责完善实例对象的信息。
  • 执行顺序不同:类通过__new__()函数返回实例对象,在__new__()函数内再调用__init__()函数,初始化实例,因此,实例化的过程要早于初始化的过程,即先执行__new__()函数,再执行__init__()函数。
  • 第一个参数不同:__new__()第一个参数为cls,指向当前类,属于类级别的方法:__init__()第一个参数为self,指向实例对象,属于实例级别的方法。
  • 返回值不同:__new__()函数返回实例对象,而__init__()函数返回None。注意,__new__()函数不能直接返回当前类的实例,否则会造成死循环,应返回父类(supper())或根类(object)的实例,也可以时其他类的实例。
class One(object): # 定义单例类
    instance = None # 定义一个类属性
    def __new__(cls,*arg,**kwargs): # 重写new方法
        if cls.instance is None: # 判断类属性是否为空,如空,则调用父类的new方法开辟空间
            cls.instance = super().__new__(cls) # new 是静态方法,在调用时需要主动传递cls参数
            return cls.instance
        else: # 否则返回原实例对象
            return cls.instance
        
obj1 = One() # 实例化
print(obj1) # <__main__.One object at 0x7fe988505790>
obj2 = One() # 实例化
print(obj2) # <__main__.One object at 0x7fe988505790>

如果__new__()没有返回当前类的实例,那么当前类的__init__()函数是不会被调用的。如果__new__()返回其他类的实例,那么只调用那个类的构造函数。

class Test:
    def __init__(self): # 初始化函数
        print('__init__')
    def __new__(cls): # 实例化函数
        print('__new__')
        # return super().__new__(cls)
        return object.__new__(cls)

Test()
'''
__init__ 
重写__new__()函数,导致__init__()函数不能被自动执行
'''

class Test:
    def __init__(self): # 初始化函数
        print('__init__')
    def __new__(cls): # 实例化函数
        print('__new__')
        # return super().__new__(cls)
        return object.__new__(cls)

Test()
'''
__new__
__init__
返回当前类的实例,可以使用supper()和object。
'''

调用实例函数

使用小括号调用实例对象时,将触发__call__()函数。

class Add: # 加法器类
    def __init__(self,*args): # 构造函数
        self.__sum = 0 # 初始化本地变量
    def __call__(self,*args): # 当调用对象时,可以传入多个值
        for i in args: # 迭代参数列表
            if(isinstance(i,(int,float))): # 检测参数值是否为数字
                self.__sum += i # 叠加数字
        return self.__sum # 最后返回数字之和
add = Add() # 实例加法器类
print(add(3,4,5)) # 执行求和运算,输出 12

析构函数

__del__()表示析构函数,与__init__()作用相反,用于销毁实例对象。当类的实例在内存中被释放时,自动触发析构函数。

由于Python能够自动管理内存,用户不用关心内存的分配和释放。Python解释器自动执行,当解释器在进行垃圾时也自动执行__del__()函数。

class Test: # 测试类
    def __del__(self): # 析构函数
        print('__del__')

test = Test() # 实例化
del test # 删除实例对象
print('程序结束') # 程序结束标志
'''
__del__
程序结束
'''

Python 采用自动引用计数的方式实现垃圾回收,只有当一个Python对象的计数器值为0时,才自动调用__del__()函数回收实例对象。

class Test: # 测试类
    def __del__(self): # 析构函数
        print('__del__')

test = Test() # 实例化
d = test
del test # 删除实例对象
print('程序结束') # 程序结束标志
'''
程序结束
'''

3. 类的成员

3.1 属性和方法

属性主要用于存储值,可以分为类属性和实例属性。类属性位于类结构的顶层域中,不是在 def 域中;而实例属性位于类内的def域中。

通过赋值语句可以定义属性,语法如下:

class 类名:
		类属性名 = 属性值  # 类属性
		...
		def 函数名(self): # 类的函数
				self.实例属性名 = 属性值  # 实例属性
				...

在类结构中,使用等号运算符为变量赋值,该变量就是类变量,即类的属性。如果把变量放在def中,使用点语法附加在self(第一个形参,指实例对象)上,则表示该变量为本地变量,即实列属性。

类属性和实例属性有很多不同,比较如下:

  • 命名空间:实例属性属于实例对象,类属性属于类对象。
  • 访问方式:实例属性必须通过实例对象来访问;类属性通过类直接访问,也可以通过实例对象来访问。
  • 存储方式:实例属性在每个实例对象中都保存一份,类属性在内存中仅保存一份。
  • 加载方式:实例属性只在实例化类的时候创建,类属性在类的代码被加载时创建。
  • 应用场景:如果在每个实例对象中读取的值都不相同,那么可以使用实例属性;如果在每个实例对象中读取的值都相同,那么可以使用类属性。
'''
定义一个员工类,包含员工姓名、部门、年龄等信息,
并添加统计员工总人数的功能
'''
class Employee: # 定义员工类
    count = 0  # 类属性,统计员工数量
    def __init__(self,name,age,department): # 构造函数
        self.name = name # 实例属性
        self.age = age
        self.department = department
        Employee.count += 1 # 员工人数递增
# 实例化类
emp1 = Employee('zhangsan',19,'A')
emp2 = Employee('lisi',20,'B')
emp3 = Employee('wangwu',22,'A')
emp4 = Employee('zhaoliu',18,'C')
# 打印员工人数
print('总共创建%d个员工对象'%Employee.count) # 总共创建4个员工对象

方法表示行为或动作,用于完成特定的任务,解决具体的问题。

class 类名:
		def 方法名(self):
				# 执行任务
			  ...

在类结构中,方法就是一个普通函数,Python允许类对象或实例对象都可以调用方法,但不建议使用类对象直接调用实例方法。

调用对象不同,则方法的行为不同,简单说明如下:

  • 当实例对象调用函数时,函数就是实例方法,实例方法的第一个实参被自动设为实例对象,习惯用self作为第一个形参,也可以使用其他名称。
  • 当类对象调用函数时,该函数只是类的一个行为属性,此时函数不会自动设置实参。如果已经定义了形参self,则必须传入一个实参,否则将抛出异常,且self也不会指代类对象。

在实例方法中,可以通过self访问类属性,也可以访问实例属性,如果重名,则实例属性优先级高于类属性

# 定义简单的类,实现加减乘除四则运算
class MyMath: 
    def __init__(self,a,b): # 初始化类
        self.a = a
        self.b = b
    def addition(self): # 加法运算
        return self.a + self.b
    def subtraction(self): # 减法运算
        return self.a - self.b
    def multiplication(self): # 乘法运算
        return self.a*self.b
    def division(self): # 除法运算
        if self.b == 0:
            print('除数不能为0')
        else:
            return self.a/self.b
        
while True: # 无限次使用计算器
    a = int(input('参数a:'))
    b = int(input('参数b:'))
    mymath = MyMath(a,b)
    print('加法结果:',mymath.addition())
    print('加法结果:',mymath.subtraction())
    print('加法结果:',mymath.multiplication())
    if mymath.division() != None: # 除数不为0时,返回不为None
        print('加法结果:',mymath.division())
    flag = input('是否退出运算[y/n]:')
    if flag == 'y':
        break

3.2 方法装饰器

Python 内置了两个装饰器函数,使用它们可以把类中函数转换为专用类方法或静态方法。

  • classmethod:装饰为类方法。对于类方法来说,习惯上使用cls设置第一个形参,也可以使用其他名称。
  • staticmethod:装饰为静态方法。无默认实参,如果要在静态方法中访问类的属性,只能通过引用类对象来实现。

简单比较实例方法、类方法和静态方法:

  • 实例对象可以调用3种方法,类对象只能调用类方法和静态方法。
  • 实例方法的上下文运行环境是实例对象,而类方法的上下文运行环境是类对象。
  • 调用实例方法时,可以访问实例的属性,也可以只读类的属性;调用类方法时,可以访问类的属性;调用静态方法时,只能通过参数或者类对象间接访问类的属性。
# 使用类方法修改类属性的值
class People(): 
    country = '中国'
    # 类方法,使用classmethod进行修饰
    @classmethod
    def get(cls):
        return cls.country
    @classmethod
    def set(cls,county):
        cls.country = county
p = People() # 实例化类
print(p.get()) # 通过实例对象引用
print(People.get()) # 通过类对象引用
p.set('美国') # 通过实例对象,调用类方法,修改类属性
print(p.get()) # 通过实例对象引用类方法

# 在静态方法中如何使用类对象访问类属性
class People(): 
    country = '中国'
    @staticmethod # 静态方法
    def get():
        return People.country # 通过类对象访问类属性
p = People() # 实例化类
print(People.get()) # 使用类对象调用静态方法
print(p.get()) # 通过实例对象调用静态方法

3.3 属性装饰器

静态属性存在一个缺陷:无法对用户的访问进行监控。

property 装饰器函数,可以把一个普通函数转换为函数式属性,这样通过函数的行为对用户的访问进行监控,避免乱操作。

属性的访问包括:读、写、删,对应的装饰器为:@property、@方法名.setter、@方法名.deleter。

class Goods(object):
    def __init__(self,price,discount=1): # 初始化函数
        self.orig_price = price # 原价
        self.discount = discount # 折扣

    @property
    def price(self): # 读取属性函数
        new_price = self.orig_price*self.discount
        return new_price
    
    @price.setter # 写入属性函数
    def price(self,value):
        self.orig_price = value
    
    @price.deleter # 删除属性函数
    def price(self):
        del self.orig_price

obj = Goods(120,0.7) # 实例化类
print(obj.price) # 获取商品价格 84.0
obj.price = 200 # 修改商品原价 
print(obj.price) # 获取商品价格 140
del obj.price # 删除商品原价
print(obj.price) # 不存砸,抛出异常

3.4 构造属性

属性装饰器用法比较繁琐,使用property()函数构造属性可以快速封装,前3个参数分别对应获取属性、设置属性以及删除属性的方法。该函数返回一个属性,定义的属性与使用@property装饰器定义的属性具有相同功能。

class Goods(object):
    def __init__(self,price,discount=1):
        self.orig_price = price
        self.discount = discount

    def get_price(self):
        new_price = self.orig_price*self.discount
        return new_price
    
    def set_price(self,value):
        self.orig_price = value

    def del_price(self):
        del self.orig_price

    # 构造属性
    price = property(get_price,set_price,del_price,"可读、可写、可删除属性:商品价格")

obj = Goods(120,0.7) # 实例化类
print(obj.price) # 获取商品价格 84.0
obj.price = 200 # 修改商品原价 
print(obj.price) # 获取商品价格 140
del obj.price # 删除商品原价
print(obj.price) # 不存砸,抛出异常

3.5 内置成员

Python 内置了一组变量和函数,习惯上称为魔法变量和魔法方法。为了方便识别,这些内置变量和函数名称首尾都带有双下划线。

内置变量,又称内置属性:

  • __doc__:当前类的描述信息,定义在类的第一行注释,通过类对象直接访问。
  • __module__:当前对象属于哪个模块。
  • __class__:当前对象属于哪个类。
  • __dict__:当前类对象或当前实例对象包含的所有成员。
  • __base__:当前类的基类。如果是单继承,使用__base__可以获取父类。
  • __bases__:当前类的基类。如果是多继承,使用__bases__可以获取所有父类,并以元组类型返回。
  • __name__:当前类的名字(字符串)。
class Student:
    def __init__(self,name,age):
        self.name = name 
        self.age = age

student = Student('张三',19)
# 实例对象访问
print(student.__class__) # 
print(student.__module__) # __main__
# 类对象访问
print(Student.__class__) # 
print(Student.__module__) # __main__

__file__:返回当前文件的绝对路径。在终端直接运行时,则返回文件本身。因此,如果要使用绝对路径,推荐使用os.path.abspath(__file__),确保始终返回绝对路径。
__name__:返回当前模块的名称。如果为main,表示顶层模块,能够调用其他非main的模块,并且可以运行;如果为非main,则无法运行,只能用来导入使用。

内置函数可以分为两大类:

  • 运算符重载:大部分运算符都可以重载,通过对应的魔术方法可以重新设计指定运算符在当前类型下的运算逻辑。
  • 类操作:魔术方法,在类的生命周期的不同时间节点被自动执行,用来增强类的功能。另外还提供了一组与不同类、不同行为相绑定的魔术方法,当特定行为发生时,被自动执行。
  • __getattr__:当访问不存在的属性时,将执行__getattr__()方法,参数为属性,函数返回值为不存在属性的值。默认行为时抛出异常。
  • __getattr__:设置属性值时,会调用该方法,参数为属性和属性值。注意,重写该方法的行为时,不要重复调用属性,这样会造成死循环,可以通过self.__dict__[key]=value方式写入。
  • __delattr__:删除属性时,调用该方法,参数为属性。
class Student:
    def __init__(self,name,age):
        self.name = name
        self.age = age 
    def __getattr__(self,item):
        return '属性 %s 不存在'%item
    def __setattr__(self,key,value):
        if str(key) == 'age':
            self.__dict__[key]=str(value)+'岁' 
        else:
            self.__dict__[key]=value
    def __delattr__(self,item):
        self.__dict__.pop(item)
        print('属性 %s 已删除'%item)

student = Student('张三',24)
print(student.age) # 24岁
del student.age # 属性 age 已删除
print(student.age) # 属性 age 不存在

4.类的特性

4.1 封装

封装就是信息隐藏,将类的使用和实现分开,只保留有限的接口(方法)与外部联系。对于开发人员,只要知道类的使用即可,不用关心类的实现过程和技术细节。也可以让开发人员把更多的精力集中应用层面开发,同时也避免了程序之间的依赖和耦合。

Python 只有模块、类和函数产生作用域。类的成员只能借助点语法,通过类对象、实例对象进行访问。

类对象.属性名
实例对象.属性名
类对象.方法名()
实例对象.方法名()

类的所有成员都有两种形式:

  • 公有成员:在任何地方都能访问。
  • 私有成员:只能在类的内部访问。

私有成员和公有成员的定义方式不同:私有成员命名时,前两个字符必须时下划线,内置成员除外,如__init____call____str__等魔术方法或魔法变量。

class Test:
    def __init__(self): # 初始化函数
        self.a = '公有属性'
        self.__b = '私有属性'
    def get(self): # 公共方法
        return self.__b # 返回私有属性的值
test = Test() # 实例化类 
print(test.a) # 公有属性
print(test.get()) # 私有属性
print(test.__b) # 直接访问私有属性,将抛出异常

如果要访问私有属性,也可以通过如下方式访问:

对象._类__属性名

print(test._Test__b) # 强制访问私有属性,输出 私有属性

4.2 继承

不同类之间可能存在代码重叠,如果不想重写代码,可以利用继承机制快速实现代码复制。继承机制简化了类的创建,提高了代码的可重用性,还可以构建类与类之间的关系。

新建类可以继承一个或多个类,被继承的类称为父类或基类,新建类称为子类或派生类。

class 子类(基类1,基类2,...,基类N):
		类主体

基类可以时一个或多个,基类之间通过逗号分隔。如果不指定基类,则将继承Python对象系统的根类object,如果只有一个父类,则称为单继承;如果有多个父类,则称为多继承。

class Rectangle: # 定义矩形类
    def __init__(self,width=10,height=10): # 初始化类
        self.width = width 
        self.height = height 
    def area(self): # 定义面积方法
        return self.width*self.height
    def perimeter(self): # 定义周长方法
        return 2*(self.width+self.height)
    
class PlanRectangle(Rectangle): # 定义有位置参数的矩形类
    def __init__(self,width,height,startX,startY): # 初始化类
        super().__init__(width,height) # 调用父类构造函数,初始化宽度和高度
        self.startX = startX
        self.startY = startY
    def isInside(self,x,y): # 定义与矩形位置方法
        # 点在矩形的条件
        if (x>=self.startX and x<=(self.startX+self.width)) and (y>=self.startX and y<=(self.startY+self.height)):
            return True
        else:
            return False

plainrectangle = PlanRectangle(10,5,10,10) # 实例化类
print('矩形的面积:',plainrectangle.area()) # 调用面积方法,矩形的面积: 50
print('矩形的周长:',plainrectangle.perimeter()) # 调用周长方法,矩形的周长: 30
if plainrectangle.isInside(15,11): # 判断点是否在矩形内,点在矩形内
    print('点在矩形内')
else:
    print('点不在矩形内')

4.3 组合

代码重用有两种方式:继承和组合。组合是指在一个类中使用另一个类的对象作为数据属性。

from math import pi
class Circle: # 圆形类
    def __init__(self,radius): # 初始化类
        self.radius = radius
    def area(self): # 计算圆的面积
        return pi*self.radius*self.radius
    def perimeter(self): # 计算圆的周长
        return 2*pi*self.radius
    
circle = Circle(10) # 实例化一个圆
area1 = circle.area() # 计算圆面积
per1 = circle.perimeter() # 计算圆周长
print(area1,per1) # 打印面积和周长:314.1592653589793 62.83185307179586

class Ring:  # 圆环类
    def __init__(self,radius_outside,radius_inside): 
        self.outsid_circle = Circle(radius_outside) # 组合外圆实例
        self.inside_circle = Circle(radius_inside) # 组合内圆实例
    def area(self): # 计算圆环的面积
        return self.outsid_circle.area()-self.inside_circle.area()
    def perimeter(self): # 计算圆环的周长
        return self.outsid_circle.perimeter()+self.inside_circle.perimeter()
    
ring = Ring(10,5)
print(ring.perimeter()) # 圆环的周长 94.24777960769379
print(ring.area()) # 圆环的面积 235.61944901923448

组合与继承都能够有效利用已有类的资源,实现代码重用,但是二者使用方法不同:

  • 通过继承建立派生类与基类之间的关系,这是一种从属关系。
  • 使用组合建立类与组合类之间的关系,这是一种从属关系。

4.4 扩展

基类的成员都会被派生类继承,当基类中的某个方法不完全适应派生类时,就需要在派生类中进行重写。实现方法:在子类中定义同名方法,那么该方法将覆盖从基类继承来的方法;如果不是同名方法,则能够增强基类的功能,实现类的扩展。

class Bird: # Bird类,基类
    def eat(self): # eat()方法
        print('Bird,吃东西')

class SongBird(Bird): # SongBird()类,派生类
    def eat(self): # 重写基类eat()方法
        print('SongBird,吃东西')
    def song(self): # 扩展song()方法
        print('SongBrid,唱歌')

bird = Bird()
songBird = SongBird()
bird.eat() # 输出 Bird,吃东西
songBird.eat() # 输出 SongBird,吃东西
songBird.song() # SongBrid,唱歌

4.5 多态

多态是指不同的类型,拥有相同的接口,可以有不同的实现。Python 支持多态,多态不关注是否符合类型,仅关注是否有可执行的接口,如果有就可以执行。

class Bike: # 定义自行车类
    def __init__(self,brand,color): # 初始化函数
        self.oral_brand = brand
        self.oral_color = color
    @property # 属性
    def brand(self):
        return self.oral_brand # 返回属性值
    @brand.setter 
    def brand(self,b): # 设置品牌属性值
        self.oral_brand = b 
    @property 
    def color(self): 
        return self.oral_color
    @color.setter
    def color(self,c): # 设置颜色属性值
        self.oral_color = c
    def riding(self): # 定义骑行方法
        print('自行车可以骑行')

class Folding_Bike(Bike): # 定义折叠自行车类
    def __init__(self,brand,color): # 初始化函数
        super().__init__(brand,color) # 调用父类方法
    def riding(self): # 重写父类方法
        print('折叠自行车:{}{}可以折叠'.format(self.color,self.brand)) 
class Electric_Bike(Bike): # 定义电动车类
    def __init__(self,brand,color,battery): # 初始化函数
        super().__init__(brand,color) # 调用父类
        self.oral_battery = battery
    @property
    def battery(self):
        return self.oral_battery
    @battery.setter
    def battery(self,b):
        self.oral_battery = b
    def riding(self): # 重写父类方法
        print('电动车:{}{}使用{}电池'.format(self.color,self.brand,self.battery))
f_bike = Folding_Bike('捷安特','白色') # 实例化折叠自行车类
f_bike.riding() # 折叠自行车:白色捷安特可以折叠
f_bike.color = '黑色' # 设置属性值
f_bike.riding() # 输出: 折叠自行车:黑色捷安特可以折叠
e_bike = Electric_Bike('小刀','蓝色','55V20AH') # 实例化电动车类
e_bike.riding() # 输出: 电动车:蓝色小刀使用55V20AH电池
e_bike.battery = '60V20AH' # 设置属性值
e_bike.riding() # 输出: 电动车:蓝色小刀使用60V20AH电池

5. 迭代器

可迭代对象:能够使用for语句遍历,如序列(字符串、列表、元组)、非序列(字典、文件)、自定义类(实现了__iter__()__getitem__()方法)、迭代器和生成器。

for循环与__iter__()方法的关系:调用可迭代对象的__iter__()方法返回一个迭代器对象(iterator),不断调用迭代器的__next__()方法返回元素,知道遇到StopIteration异常,才结束调用__next__()方法,并停止迭代。

迭代器(iterator)是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,知道所有元素访问完才结束,迭代器只能往前不会后退。

使用collections模块的IterableIterator类型可以验证对象是否可迭代对象或迭代器。

from collections.abc import Iterable
from collections.abc import Iterator
list = [1,2,3,4]
it = iter(list)
print(isinstance(list,Iterable)) # True
print(isinstance(it,Iterable)) # True
print(isinstance(list,Iterator)) # False
print(isinstance(it,Iterator)) # True

如果一个类实现了下面2个方法,那么它的实例就是一个迭代器(iterator):

  • __iter__:返回self,即迭代器自身。
  • __next__:返回下一个可用的元素。当没有元素时抛出StopIteration异常。
class MyList(object): # 可迭代对象
    def __init__(self,num): # 初始化
        self.data = num # 设置可迭代的上边界
    def __iter__(self): # 迭代器
        return MyListIterator(self.data) # 返回可迭代对象的迭代器类的实例

class MyListIterator(object): # 迭代器类,供Mylist可迭代器类的实例
    def __init__(self,data):
        self.data = data # 初始化可迭代的上边界
        self.now = 0 # 当前迭代值,初始为0
    def __iter__(self):
        return self # 返回迭代器类的实例,因为自己就是迭代器,所以返回self
    def __next__(self): # 迭代器必须实现的方法,获取下一个元素
        while self.now < self.data:
            self.now += 1
            return self.now - 1 # 返回当前迭代值
        raise StopIteration # 超出上边界,抛出异常
    

my_list = MyList(5) # 创建一个可迭代的对象
print(type(my_list))  # 
my_list_iter = iter(my_list) # 获取对象的迭代器
print(type(my_list_iter)) # 
for i in my_list:
    print(i) # 输出 0 1 2 3 4


class data:
    list:list = [1,2,3,4,5,6] # 内部列表
    index = 0 # 计数器
    def __call__(self,*args,**kwargs): # 回调函数
        item = self.list[self.index] # 获取每个元素的值
        self.index += 1 # 递增变量
        return item # 放回元素的值
    def __iter__(self): # 迭代器函数
        self.i = iter(self.list) # 把列表对象转换为迭代器对象
        return self.i # 返回可迭代的列表对象
# 每次迭代都会调用一次__call__方法,当__call__方法的返回值等于3时停止迭代
for item in iter(data(),3):
    print(item) # 输出 1 2

你可能感兴趣的:(Python,python,开发语言,笔记)