我有一些函数,它们的实现细节取决于传递给它们的对象类型(具体来说,是选择链接Django模型以生成QuerySet的正确方法)。以下两个选项中的哪一个是更Pythonic的实现方式?
def do_something(thing: SuperClass) -> "QuerySet[SomethingElse]":
if isinstance(thing, SubClassA):
return thing.property_set.all()
if isinstance(thing, SubClassB):
return thing.method()
if isinstance(thing, SubClassC):
return a_function(thing)
if isinstance(thing, SubClassD):
return SomethingElse.objects.filter(some_property__in=thing.another_property_set.all())
return SomethingElse.objects.none()
def do_something(thing: SuperClass) -> "QuerySet[SomethingElse]":
return {
SubClassA: thing.property_set.all(),
SubClassB: thing.method(),
SubClassC: a_function(thing),
SubClassD: SomethingElse.objects.filter(some_property__in=thing.another_property_set.all()),
}.get(type(thing), SomethingElse.objects.none())
[字典选项具有较少的重复代码和较少的行,但是if梯子使PyCharm和MyPy更快乐(尤其是使用类型检查)。
我假设两者之间的任何性能差异都可以忽略不计,除非它位于经常调用的例程的内部循环中(如>> 1请求/秒)。
这正是多态性旨在解决的问题的类型。遵循“封装变化的内容”的概念,我建议创建所有类都实现的基本“接口”,然后在所有类上调用同名的方法。
我在引号中加上了“接口”,因为Python并没有真正的接口,因为它们在OOP中是众所周知的。因此,您必须使用子类,并手动执行方法签名(即要小心)。
演示:
class SuperClass:
# define the method signature here (mostly for documentation purposes)
def do_something(self):
pass
class SubClassA(SuperClass):
# Be careful to override this method with the same signature as shown in
# SuperClass. (In this case, there aren't any arguments.)
def do_something(self):
print("Override A")
class SubClassB(SuperClass):
def do_something(self):
print("Override B")
if __name__ == '__main__':
import random
a = SubClassA()
b = SubClassB()
chosen = random.choice([a, b])
# We don't have to worry about which subclass was chosen, because they
# share the same interface. That is, we _know_ there will be a
# `do_something` method on it that takes no arguments.
chosen.do_something()