ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python面向对象:多态特性与鸭子类型技术详解

Python面向对象:多态特性与鸭子类型技术详解 Python面向对象多态特性与鸭子类型技术详解一、开篇同一个接口不同的行为多态Polymorphism是OOP的四大特性之一。“poly”多“morph”形态——同一段代码可以作用于不同类型的对象只要它们有相同的接口。⌨️ Python中多态无处不在# len()函数对不同类型都有效——这就是多态print(len(hello))# 5 —— 字符串print(len([1,2,3]))# 3 —— 列表print(len({a:1}))# 1 —— 字典print(len(range(100)))# 100 —— range对象# len()不关心你是什么类型——只要你实现了__len__方法二、两种多态实现方式2.1 继承多态# 通过继承实现多态——经典的OOP方式classAnimal:defspeak(self):raiseNotImplementedErrorclassDog(Animal):defspeak(self):return汪汪classCat(Animal):defspeak(self):return喵喵classDuck(Animal):defspeak(self):return嘎嘎# 同一个函数——处理不同类型的动物defanimal_chorus(animals):动物合唱团——不关心具体是什么动物foranimalinanimals:print(animal.speak())animal_chorus([Dog(),Cat(),Duck(),Dog()])# 汪汪 喵喵 嘎嘎 汪汪2.2 鸭子类型——Python的真·多态# 鸭子类型如果它走路像鸭子叫起来像鸭子——那它就是鸭子# 不关心对象的类型只关心它有没有需要的方法/属性classDog:defspeak(self):return汪汪classCat:defspeak(self):return喵喵classRobot:机器人——不继承任何动物类但有speak方法defspeak(self):return哔哔你好classPerson:人——同样不继承动物类defspeak(self):return大家好# animal_chorus对它们都有效# 不需要继承自Animal——只要有speak()方法就行animal_chorus([Dog(),Cat(),Robot(),Person()])# 汪汪 喵喵 哔哔你好 大家好# 这就是鸭子类型的精髓# Python不检查类型——只检查方法是否存在# 这比严格的继承多态更灵活三、鸭子类型的实际应用3.1 文件类对象# Python中许多API接受file-like object——任何有read/write方法的对象# 真正的文件defprocess_data(source):处理数据源——不关心是文件、字符串还是网络流datasource.read()print(f读取了{len(data)}字节)returndata.upper()# 文件对象# with open(data.txt) as f:# result process_data(f)# StringIO——在内存中模拟文件fromioimportStringIO fake_fileStringIO(hello world)resultprocess_data(fake_file)print(result)# HELLO WORLD# 任何有read()方法的对象都可以classMockSource:模拟数据源——测试用defread(self):returnmock dataresultprocess_data(MockSource())print(result)# MOCK DATA3.2 迭代器协议# for循环不要求你继承特定类——只需要实现__iter__和__next__classCountdown:倒计时——实现了迭代器协议def__init__(self,start):self.countstartdef__iter__(self):returnselfdef__next__(self):ifself.count0:raiseStopIteration self.count-1returnself.count1# for循环对这个自定义类同样有效forninCountdown(5):print(n,end )# 5 4 3 2 1print()# 列表、字典、生成器、文件...都实现了迭代器协议# for循环通过鸭子类型支持所有这些类型3.3 isinstance vs 鸭子类型# 两种风格对比classPaymentProcessor:支付处理器defprocess(self,payment_method,amount):处理支付# 风格一类型检查不推荐# if not isinstance(payment_method, (CreditCard, WeChatPay, Alipay)):# raise TypeError(不支持的支付方式)# 风格二鸭子类型推荐# 不检查类型——直接调用不行就报错try:payment_method.pay(amount)return支付成功exceptAttributeError:raiseTypeError(支付方式必须有pay()方法)exceptExceptionase:returnf支付失败:{e}# 各支付方式——不需要继承基类classCreditCard:defpay(self,amount):print(f信用卡支付¥{amount})classWeChatPay:defpay(self,amount):print(f微信支付¥{amount})classAlipay:defpay(self,amount):print(f支付宝支付¥{amount})processorPaymentProcessor()processor.process(CreditCard(),100)processor.process(WeChatPay(),200)# 新增支付方式——不需要修改PaymentProcessorclassBitcoinPay:defpay(self,amount):print(f比特币支付¥{amount})processor.process(BitcoinPay(),500)# 直接能用四、ABC抽象基类在鸭子类型和类型检查间平衡fromabcimportABC,abstractmethod# ABC提供了可选的类型约束# 它不像Java的interface那样强制——但能提供更清晰的错误信息classPaymentMethod(ABC):支付方式——抽象基类abstractmethoddefpay(self,amount):passabstractmethoddefrefund(self,amount):pass# 继承ABC——如果缺少抽象方法实例化时会报错classCreditCard(PaymentMethod):defpay(self,amount):print(f信用卡支付¥{amount})defrefund(self,amount):print(f退款¥{amount}到信用卡)# 不继承ABC的类——仍然可以通过鸭子类型使用classWeChatPay:# 没有继承PaymentMethoddefpay(self,amount):print(f微信支付¥{amount})defrefund(self,amount):print(f退款¥{amount}到微信)# 两者都能用formethodin[CreditCard(),WeChatPay()]:method.pay(100)method.refund(50)五、总结多态让代码灵活而通用。Python通过鸭子类型将多态发挥到了极致——不需要继承不需要接口只要对象有对应的方法就能工作。核心要点继承多态子类重写父类方法——经典的OOP方式鸭子类型只看方法不看类型——Python的默认做法“如果它走路像鸭子”——有read()就是文件有__iter__就能遍历EAFP求原谅比求许可容易try用方法不行就报错而不是提前检查isinstance✅Python多态的精髓写代码时不要问你是什么类型“而要问你能做什么”。
返回列表