在Python中选择不同的switch-cases替换 - 字典或if-elif-else?

时间:2009-02-27 11:47:51

标签: python switch-statement

我最近阅读了建议不要在支持它的语言中使用switch-case语句的问题。就Python而言,我已经看到了许多交换机案例替换,例如:

  1. 使用字典(多种变体)
  2. 使用元组
  3. 使用功能装饰器(http://code.activestate.com/recipes/440499/
  4. 使用多态(推荐方法而不是类型检查对象)
  5. 使用if-elif-else阶梯
  6. 有人甚至推荐了访客模式(可能是外在的)
  7. 鉴于各种各样的选项,我在确定如何处理特定代码时遇到了一些困难。我想学习选择其中一种方法的标准。此外,如果我在决定遇到问题的具体情况下(对选择的解释),我会很感激建议如何做。

    以下是具体问题:
    (1)

    def _setCurrentCurve(self, curve):
            if curve == "sine":
                self.currentCurve =  SineCurve(startAngle = 0, endAngle = 14,
                lineColor = (0.0, 0.0, 0.0), expansionFactor = 1,
                centerPos = (0.0, 0.0))
            elif curve == "quadratic":
                self.currentCurve = QuadraticCurve(lineColor = (0.0, 0.0, 0.0))
    

    此方法由 qt-slot 调用,以响应从菜单中选择绘制曲线。一旦申请完成,上述方法将包含总共4-7条曲线。在这种情况下使用丢弃词典是否合理?由于最明显的方法是if-elif-else,我应该坚持下去吗?我也考虑在这里使用** kargs(有朋友的帮助),因为所有曲线类都使用** kargs ......

    (2)
    第二段代码是 qt-slot ,当用户更改曲线的属性时会调用该代码。基本上,槽从gui(spinBox)获取数据并将其放入适当曲线类的实例变量中。在这种情况下,我再次有同样的问题 - 我应该使用词典吗?

    这是前面提到的插槽 -

    def propertyChanged(self, name, value):
        """A Qt slot, to react to changes of SineCurve's properties."""
        if name == "amplitude":
            self.amplitude = value
        elif name == "expansionFactor":
            self.expansionFactor = value
        elif name == "startAngle":
            self.startAngle = value
        elif name == "endAngle":
            self.endAngle = value  
    

    供参考,以下是连接上述插槽的代码 -

    def _connectToPage(self, page):
        for connectionData in page.getConnectibles():
            self.connect(connectionData["object"],
                        SIGNAL(connectionData["signal"]),
                        lambda value, name = connectionData["property"]:\
                            self.currentCurve.propertyChanged(name, value))
            self.connect(connectionData["object"],
                        SIGNAL(connectionData["signal"]),
                        self.hackedDisplayArea.update) 
    

    注意 - self.endAngle等在构造函数中初始化。

    据我所知,选择字典的原因是快速查找。什么时候有必要?当我有100个案件或更多?每次调用函数时,继续构建和丢弃字典是一个好主意吗?如果我在函数之外为此目的构建一个dict,我应该检查它是否需要在哪里?如果在其他地方不需要它会发生什么?

    我的问题是,如果有最佳实践,最佳做法是什么?什么是最好/最优雅的方式?换句话说,何时使用 if-elif-else ,何时使用其他每个选项?

7 个答案:

答案 0 :(得分:24)

叹息。对问题的错误部分过分绞尽脑汁。 switch语句不是问题。表达“替代”的方法有很多,不能添加含义

问题是含义 - 不是技术声明选择。

有三种常见的模式。

  • 将键映射到对象。如果字典几乎完全是静态的,那么请使用字典,并且您可以在简单键和另一个更复杂的键之间进行映射。每次需要时动态构建字典都是愚蠢的。你可以使用它,如果它是意思:你的“条件”是简单的,映射到对象的静态键值。

  • 子类之间的变体行为。使用多态而不是类型检查对象。正确。如果在具有变体行为的多个类中有类似的对象,则它们应该是多态的。尽可能经常使用它。

  • 其他变体行为。使用if-elif-else梯形图。如果没有很大的静态键值映射,请使用此选项。当条件复杂时,或者表示程序而不是对象时使用此项。

其他一切都只是棘手的代码,可以达到类似的效果。

使用元组。这只是没有映射的字典。这需要搜索,应尽可能避免搜索。不要这样做,效率低下。使用字典。

使用函数装饰器(http://code.activestate.com/recipes/440499/)。恶心。这隐瞒了你正在解决的问题的if-elif-elif性质。不要这样做,选择独占并不明显。使用其他任何东西。

有人甚至推荐了访客模式。如果有一个遵循 Composite 设计模式的对象,请使用此选项。这取决于多态性的工作,所以它并不是一个真正不同的解决方案。

答案 1 :(得分:8)

在第一个例子中,我肯定会坚持使用if-else语句。事实上,除非

,否则我没有理由不使用if-else
  1. 你发现(使用例如个人资料模块)if语句是一个瓶颈(非常不可能的IMO,除非你有大量的案例做得很少)

  2. 使用字典的代码更清晰/重复次数更少。

  3. 你的第二个例子我实际上会重写

    setattr(self, name, value)
    

    (可能添加一个断言语句来捕获无效名称)。

答案 2 :(得分:2)

考虑到这是为了响应用户操作(从菜单中选择某些东西)而完成的,并且您预期的选择数量非常少,我肯定会使用简单的if-elif-else梯形图。

选择速度是没有意义的,因为它的发生速度与用户无论如何都能进行选择一样快,这不是“光线跟踪器的内环” - 领域。当然,给用户提供快速反馈很重要,但由于案例数量很少,所以也没有危险。

优化简洁没有意义,因为(imo更清晰,零可读性开销)if-ladder无论如何都会非常短。

答案 3 :(得分:2)

关于字典问题:

  

据我所知,选择字典的原因是快速查找。什么时候有必要?当我有100个案件或更多?每次调用函数时,继续构建和丢弃字典是一个好主意吗?如果我在函数之外为此目的构建一个dict,我应该检查它是否需要在哪里?如果在其他地方不需要它会发生什么?

  1. 另一个问题是可维护性。使用string-> curveFunction字典可以让您对菜单进行数据驱动。然后添加另一个选项只是在字典中放入另一个string->函数条目(它存在于专用于配置的代码的一部分中。

  2. 即使你只有几个条目,它也会“分开关注”; _setCurrentCurve负责在运行时进行连接,而不是定义组件框。

  3. 构建字典并按住它。

  4. 即使它没有在别处使用,您也可以获得上述好处(可定位性,可维护性)。

  5. 我的经验法则是问“这里发生了什么事?”对于我的代码的每个组件。如果答案是

    的形式
      

    ... ... ...

    (如“定义函数库将每个函数与菜单中的值相关联”)然后有一些问题需要分开。

答案 4 :(得分:1)

每个公开的选项都适合某些情况:

  1. if-elif-else:简洁,清晰
  2. 字典:动态配置时很有用(想象一下你需要在分支上执行特定功能)
  3. tuple:if-else case的简单性,每个分支有多个选择。
  4. polymorphism:面向对象的自动分支
  5. Python是关于可读性和一致性的,即使你的决定总是主观的,并且它将取决于你的风格,你应该总是考虑Python咒语。

    ./亚历

答案 5 :(得分:1)

我同意df关于第二个例子。第一个例子我可能会尝试使用字典重写,特别是如果所有曲线构造函数都具有相同的类型签名(可能使用* args和/或** kwargs)。像

这样的东西
def _setCurrentCurve(self, new_curve):
    self.currentCurve = self.preset_curves[new_curve](options_here)

或者甚至

def _setCurrentCurve(self, new_curve):
    self.currentCurve = self.preset_curves[new_curve](**preset_curve_defaults[new_curve])

答案 6 :(得分:1)

在Python中,不要考虑如何替换switch语句。

使用类和多态。尝试保留有关每个可用选择的信息以及如何在一个地方(即实现它的类)实现它。

否则你最终将拥有许多地方,每个地方都包含每个选择的一小部分,更新/扩展将成为维护的噩梦。

这正是OOD试图通过抽象,信息隐藏,多态和批量解决的问题。

考虑您拥有的对象类及其属性,然后围绕它们创建OO架构。通过这种方式,您再也不用担心丢失“切换”语句了。