在Python中如何使用反射来模拟多态性?
那天在公司茶水间,正啃着面包,小王突然问我一句——“哥,Python 能不能实现多态?不靠继承那种,有点像 Java 接口实现的那种写法。”我当时脑子有点懵,牙还没咽下去,就回了句:“啊?你说的是那种……反射搞多态?”
他点点头,说他在写一个策略模式的东西,用一堆 if elif 写得脑袋都大了。
想象一下啊,你现在要实现一个支付系统,有微信支付、支付宝支付、银行卡支付……常规写法是不是就开始这么搞:
defpay(method, amount):
if method == 'wechat':
wechat_pay(amount)
elif method == 'alipay':
alipay_pay(amount)
elif method == 'bank':
bank_pay(amount)
写起来简单,但加个 PayPal、加个 Apple Pay 是不是就得动这个函数?这不就违反开闭原则了吗,对吧?
我说你这样,干脆搞一组类,每种支付写一个类,类名里统一点,比如都叫 XXXPay。然后你搞个注册器,不管多少个类,全给它注册进去。用的时候,通过方法名或者类名字符串,反射调用。
classWechatPay:
defpay(self, amount):
print(f'用微信支付:¥{amount}')
classAlipay:
defpay(self, amount):
print(f'用支付宝支付:¥{amount}')
然后来一个超级简单的“工厂”,这名字我都懒得起了:
defget_payment_instance(method):
cls_name = method.capitalize() + 'Pay'
cls = globals().get(cls_name)
if cls:
return cls()
else:
raise ValueError(f'未知的支付方式: {method}')
看清楚没?这里用的是 globals(),直接把字符串变成类,再实例化——这就是最基础的反射玩法了。
然后你就可以这样用:
p = get_payment_instance('wechat')
p.pay(100)
p2 = get_payment_instance('alipay')
p2.pay(88)
我说你可以把类名和函数名都字符串化,比如你还可以在类里写多个方法,不光 pay,还有 refund 啥的,然后你根据用户输入动态调用:
defcall_method(obj, method_name, *args, **kwargs):
method = getattr(obj, method_name, None)
if method and callable(method):
return method(*args, **kwargs)
raise AttributeError(f'{obj} 没有方法 {method_name}')
然后用起来更灵活:
p = get_payment_instance('wechat')
call_method(p, 'pay', 100)
甚至可以这么玩:
method_name = input('请输入操作(pay/refund):')
amount = float(input('请输入金额:'))
call_method(p, method_name, amount)
反射这玩意其实就像 Python 留的一条“后门”,可以动态获取类、属性、方法。你说它是不是多态?严格意义上说它是“鸭子类型 + 动态调度”的超集,跟传统面向对象那种“继承 + 重写”比,更自由,但也更容易出锅。
所以我跟小王说,你这逻辑没问题,但你得小心:
类名一定要规范,不然反射不到 方法名记得加容错 如果规模大了,建议搞注册表或装饰器自动收集,不要全靠 globals()到处查
他听完以后直点头,还说要试试搞个插件系统也用这套逻辑……
后来我就回工位继续干活了,结果刚坐下,产品经理敲我背,说微信退款失败了,问我接口是不是又写错了……
-END-
我为大家打造了一份RPA教程,完全免费:https://www.songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB,点击下方公众号回复关键字 python 全部免费领