关于在Python中选择不同的switch-case替换:在Python中使用不同的switch-cases替换 – 字典或if-elif-else?

Choosing between different switch-case replacements in Python - dictionary or if-elif-else?

我最近读了一些建议不要在支持它的语言中使用switch case语句的问题。至于python,我已经看到了许多交换盒替换,例如:

  • 使用字典(许多变体)
  • 使用元组
  • 使用函数修饰器(http://code.activestate.com/recipes/440499/)
  • 使用多态性(推荐的方法而不是类型检查对象)
  • 使用if-elif-else阶梯
  • 有人甚至推荐了访客模式(可能是外在的)
  • 考虑到各种各样的选项,我很难决定要为特定的代码段做什么。我想学习选择这些方法中的一种的标准,而不是一般的其他方法。此外,对于我在决定问题时遇到的具体情况,我将非常感谢您的建议(对选择进行解释)。

    具体问题如下:(1)

    1
    2
    3
    4
    5
    6
    7
    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槽调用,以响应从菜单中绘制曲线的选择。一旦应用完成,上述方法将包含总共4-7条曲线。在这种情况下,使用扔掉的词典是否合理?既然最明显的方法是如果Elif还有别的,我应该坚持吗?我还考虑在这里使用**kargs(在朋友的帮助下),因为所有曲线类都使用**kargs…

    (2)第二段代码是qt槽,当用户更改曲线的属性时调用。基本上,插槽从GUI(spinbox)获取数据,并将其放入适当曲线类的实例变量中。在这种情况下,我还有同样的问题——我应该用听写吗?

    这是前面提到的插槽-

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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,我应该检查它是否需要elswhere吗?如果其他地方不需要,会发生什么?

    我的问题是,如果有的话,最佳实践是什么?做事情的最佳/最优雅的方式是什么?换一种方式,如果是elif,何时使用其他选项?


    叹息。对问题的错误部分绞尽脑汁。switch语句不是问题所在。有很多表达"替代"的方法没有增加意义。

    这个问题是有意义的,而不是技术声明的选择。

    有三种常见的模式。

    • 将键映射到对象。如果字典几乎完全是静态的,并且您有一个简单键和另一个更复杂的键之间的映射,那么就使用字典。每次你需要的时候都能快速地编一本字典是很愚蠢的。如果您的意思是:您的"条件"是映射到对象的简单静态键值,则可以使用它。

    • 子类间的变异行为。使用多态性而不是类型检查对象。对的。如果在多个具有不同行为的类中有类似的对象,那么它们应该是多态的。尽可能经常使用。

    • 其他变异行为。使用if-elif-else阶梯。当您没有基本上静态的键到值映射时,可以使用这个方法。当条件复杂时,或者您指的是过程,而不是对象时,请使用此选项。

    其他的一切都是复杂的代码,可以实现类似的结果。

    使用元组。这只是没有映射的字典。这需要搜索,并且应尽可能避免搜索。别这么做,效率很低。使用字典。

    使用函数修饰器(http://code.activestate.com/recipes/440499/)。艾奇。这掩盖了你正在解决的问题的if-elif-elif性质。不要这样做,选择是排他的并不明显。使用其他任何东西。

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


    在第一个例子中,我当然会坚持if else语句。事实上,我看不出一个不使用if-else的理由,除非

  • 您发现(例如使用概要文件模块)if语句是一个瓶颈(非常不可能是imo,除非您有大量几乎不起作用的案例)

  • 使用字典的代码更清晰/重复性更低。

  • 你的第二个例子,我会重写

    1
    setattr(self, name, value)

    (可能添加了assert语句来捕获无效的名称)。


    关于字典问题:

    As far as I know, the reasons for choosing a dict is for fast lookup. When is that warranted? when I have 100 cases or more? Is it a good idea to keep building and throwing away a dictionary each time the function is called? If I build a dict for this purpose outside a function, should I check If it is needed elswhere? What happens if it is not needed elsewhere?

  • 另一个问题是可维护性。有了字符串->曲线函数字典,您就可以用数据驱动菜单了。然后添加另一个选项仅仅是在字典中放入另一个字符串->函数条目(它位于专门用于配置的代码的一部分中)。

  • 即使只有几个条目,它也"分离关注点";_setCurrentCurve负责在运行时连接,而不是定义组件框。

  • 建立字典并保存它。

  • 即使它没有在其他地方使用,您也会得到上面的好处(可定位性、可维护性)。

  • 我的经验法则是问"这里发生了什么?"对于我的代码的每个组件。如果答案是这样的话

    ... and ... and ...

    (如"定义函数库并将每个函数与菜单中的值相关联"),则需要分离一些关注点。


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

    选择速度是没有意义的,因为它只发生在用户可以做出选择的速度一样快的情况下,这不是"光线跟踪器的内环"领域。当然,给用户快速反馈很重要,但是由于案例数量太少,所以也没有这种危险。

    优化简洁性是没有意义的,因为(IMO更清晰,零可读性开销)如果阶梯会如此短。


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

    而是使用类和多态性。尝试将关于每个可用选项的信息以及如何在一个位置(即实现它的类)实现它。

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

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

    考虑您拥有的对象类及其属性,然后围绕它们创建一个OO体系结构。这样你就再也不用担心丢失的"switch"语句了。


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

    1
    2
    def _setCurrentCurve(self, new_curve):
        self.currentCurve = self.preset_curves[new_curve](options_here)

    或者甚至

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


    每个公开的选项都很适合某些场景:

  • 如果是elif else:简单,清晰
  • 字典:在动态配置时很有用(假设您需要在分支上执行特定的功能)
  • tuple:简单性优于if-else,适用于每个分支的多个选项。
  • 多态性:自动面向对象分支
  • 等。
  • python是关于可读性和一致性的,即使您的决定总是主观的,并且取决于您的风格,您也应该始终考虑python咒语。

    /亚历克斯