什么是多态多态Polymorphism是指同一操作如方法调用作用于不同对象时可以有不同的实现产生不同的结果。例如len()函数可以用于字符串、列表、元组等不同类型但都能正确返回长度。鸭子类型的定义鸭子类型的名称来源于一句谚语“如果它走起路来像鸭子、叫起来也像鸭子那么它就是鸭子。”在编程中这意味着我们不关心一个对象是什么类型类只关心它有没有我们需要的方法或属性。只要对象实现了某个方法就可以被调用而不需要显式继承自某个基类。简单举例def make_sound(animal): animal.quack() # 调用对象的 quack 方法class Duck: def quack(self): print(嘎嘎嘎) class Person: def quack(self): print(我模仿鸭子叫) class Dog: def bark(self): print(汪汪汪)make_sound(Duck()) # 输出嘎嘎嘎 make_sound(Person()) # 输出我模仿鸭子叫 # make_sound(Dog()) # 报错Dog 对象没有 quack 方法Duck和Person都有quack()方法所以它们都可以传给make_sound即使它们之间没有继承关系。这就是鸭子类型 ——只要会“叫”我就当它是鸭子。常见的鸭子类型应用Python 中有很多内置的“协议”它们都是鸭子类型的体现迭代器协议只要对象实现了__iter__()和__next__()方法就可以用于for循环。上下文管理器协议只要对象实现了__enter__()和__exit__()方法就可以用于with语句。序列协议只要对象实现了__getitem__()和__len__()就可以像列表一样被索引和切片。1、迭代器协议class Counter: 一个简单的计数器从 start 数到 end包含 end def __init__(self, start, end): self.current start # 当前值 self.end end # 结束值 def __iter__(self): # __iter__ 返回迭代器对象本身因为自身已经实现了 __next__ return self def __next__(self): if self.current self.end: raise StopIteration # 没有更多元素时抛出异常 else: # 返回当前值并将 current 加 1为下一次迭代做准备 value self.current self.current 1 return value # 使用 for 循环遍历 Counter 对象 for num in Counter(1, 5): print(num)工作原理详解创建迭代器Counter(1, 5)创建了一个对象其内部属性current初始为 1end为 5。进入for循环Python 首先调用iter(Counter(1,5))即对象的__iter__方法该方法返回自身因为自身就是迭代器。反复获取值循环内部不断调用迭代器的__next__()方法每次返回current的当前值并递增直到current end时抛出StopIteration。停止循环for捕获到StopIteration后自动退出循环。2、上下文管理器协议class MyContext: def __enter__(self): print(进入上下文) return self # 可以返回任何对象通过 as 绑定 def __exit__(self, exc_type, exc_val, exc_tb): print(退出上下文) # 如果返回 True则抑制异常不向外抛出 # 如果返回 False 或不返回默认 None异常会继续传播 return False with MyContext() as mc: print(在上下文中执行) # 如果这里发生异常__exit__ 会被调用除了自定义类Python 还提供了contextlib模块可以用更简洁的方式创建上下文管理器例如contextmanager装饰器。上下文管理器协议由两个特殊方法组成__enter__(self)在进入with代码块时被调用。它的返回值可以通过as关键字绑定到with语句中指定的变量。__exit__(self, exc_type, exc_val, exc_tb)在退出with代码块时被调用无论是否发生异常。该方法可以用于清理资源如关闭文件、释放锁等并且可以通过返回值来控制异常是否被传播。3、序列协议序列协议是指对象通过实现特定的方法使其能够像内置序列如列表、元组一样支持索引访问、切片、迭代等操作。核心的两个方法是__getitem__(self, key)定义通过索引key获取元素的行为。key可以是整数也可以是切片对象slice。__len__(self)返回序列中元素的个数用于支持len()函数和某些隐式长度判断。实现了这两个方法后对象就可以使用obj[i]进行索引访问使用obj[start:stop:step]进行切片被for循环迭代被in运算符用于成员检测被reversed()等函数处理如果实现得当。class MyRange: def __init__(self, start, end): self.start start self.end end def __len__(self): # 返回序列长度 return max(0, self.end - self.start) def __getitem__(self, key): # 处理索引和切片 if isinstance(key, int): # 检查索引是否越界 if key 0 or key len(self): raise IndexError(索引超出范围) return self.start key elif isinstance(key, slice): # 处理切片获取切片的起始、停止、步长 start, stop, step key.indices(len(self)) # 根据切片参数生成新的 MyRange 对象 new_start self.start start new_end self.start stop # 注意这里为了简化假设步长为1实际应支持任意步长 if step ! 1: # 简单的实现返回一个列表或者可以扩展 MyRange 支持步长 return [self.start i for i in range(start, stop, step)] return MyRange(new_start, new_end) else: raise TypeError(索引必须是整数或切片) # 测试 r MyRange(2, 8) # 代表 2,3,4,5,6,7 # 索引访问 print(r[0]) # 输出 2 print(r[3]) # 输出 5 # print(r[10]) # 会抛出 IndexError # 切片 print(r[1:5]) # 输出 MyRange(3,7) (包含 3,4,5,6) print(r[1:5:2]) # 输出 [3,5] (因为步长不是1返回了列表) # 支持 len() print(len(r)) # 输出 6 # 支持迭代 for num in r: print(num, end ) # 输出 2 3 4 5 6 7 # 支持 in print(4 in r) # True print(10 in r) # False说明__len__返回序列长度使len(r)正常工作。__getitem__当key是整数时计算并返回对应元素并进行边界检查。当key是切片时使用slice.indices(len(self))将切片参数规范化到有效范围内然后根据需要返回新的序列或列表。本例中当步长为1时返回新的MyRange对象否则返回列表以展示灵活性。其他类型抛出TypeError。python的多态与JAVA多态的区别在 Java、C 等静态类型语言中要实现多态通常需要继承同一个父类或实现同一个接口。编译器强制检查类型。而在 Python 中我们只需要保证对象有对应的方法即可无需继承。这带来了极大的灵活性但也要求开发者遵循约定即确保对象真的有该方法否则会在运行时抛出AttributeError。