简体   繁体   English

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

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

I recently read the questions that recommend against using switch-case statements in languages that do support it. 我最近阅读了建议不要在支持它的语言中使用switch-case语句的问题。 As far as Python goes, I've seen a number of switch case replacements, such as: 就Python而言,我已经看到了许多交换机案例替换,例如:

  1. Using a dictionary (Many variants) 使用字典(许多变体)
  2. Using a Tuple 使用元组
  3. Using a function decorator ( http://code.activestate.com/recipes/440499/ ) 使用函数装饰器( http://code.activestate.com/recipes/440499/
  4. Using Polymorphism (Recommended method instead of type checking objects) 使用多态(推荐的方法而不是类型检查对象)
  5. Using an if-elif-else ladder 使用if-elif-else梯形图
  6. Someone even recommended the Visitor pattern (Possibly Extrinsic) 有人甚至推荐了访客模式(可能是外在的)

Given the wide variety of options, I am having a bit of difficulty deciding what to do for a particular piece of code. 鉴于选择的种类繁多,我在确定如何处理特定代码时遇到了一些困难。 I would like to learn the criteria for selecting one of these methods over the other in general. 我想学习选择其中一种方法的标准。 In addition, I would appreciate advice on what to do in the specific cases where I am having trouble deciding (with an explanation of the choice). 另外,我很感谢在我遇到麻烦的具体情况下做什么的建议(对选择的解释)。

Here is the specific problem: 这是具体问题:
(1) (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))

This method is called by a qt-slot in response to choosing to draw a curve from a menu. 响应于选择从菜单绘制曲线,该方法由qt-slot调用。 The above method will contain a total of 4-7 curves once the application is complete. 一旦申请完成,上述方法将包含总共4-7条曲线。 Is it justified to use a throw away dictionary in this case? 在这种情况下使用丢弃词典是否合理? Since the most obvious way to do this is if-elif-else, should I stick with that? 由于最明显的方法是if-elif-else,我应该坚持下去吗? I have also consider using **kargs here (with a friends help) since all the curve classes use **kargs... 我也考虑在这里使用** kargs(有朋友的帮助)因为所有曲线类都使用** kargs ...

(2) (2)
This second piece of code is a qt-slot that is called when the user changes a property of a curve. 第二段代码是一个qt-slot ,当用户更改曲线的属性时调用该 Basically the slot takes the data from the gui (spinBox) and puts it in an instance variable of the appropriate curve class. 基本上,槽从gui(spinBox)获取数据并将其放入适当曲线类的实例变量中。 In this case, I again have the same question - should I use a dict? 在这种情况下,我再次有同样的问题 - 我应该使用dict吗?

Here is the aforementioned slot- 这是前面提到的插槽 -

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  

For reference, here is the code for connecting to the above slot - 供参考,以下是连接上述插槽的代码 -

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) 

Note - The self.endAngle etc. are initialized in the constructor. - self.endAngle等在构造函数中初始化。

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? 当我有100个案件或更多? 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? 如果我在函数之外为此目的构建一个dict,我应该检查它是否需要在哪里? What happens if it is not needed elsewhere? 如果在其他地方不需要它会发生什么?

My question is what is the best-practice if there is one? 我的问题是,如果有最佳实践,最佳做法是什么? What is the best/most elegant way to go about things? 什么是最好/最优雅的方式? Put in yet another way, when to use if-elif-else , when to use each of the other options? 换句话说,何时使用if-elif-else ,何时使用其他每个选项?

Sigh. 叹。 Too much hand-wringing over the wrong part of the problem. 对问题的错误部分过分绞尽脑汁。 The switch statement is not the issue. switch语句不是问题。 There are many ways of expressing "alternative" that don't add meaning . 表达“替代”的方法有很多,不能增加意义

The issue is meaning -- not technical statement choices. 问题是意义 - 而不是技术陈述的选择。

There are three common patterns. 有三种常见的模式。

  • Mapping a key to an object . 将键映射到对象 Use a dictionary if it is almost totally static and you have a mapping between a simple key and another more complex thing. 如果字典几乎完全是静态的,那么请使用字典,并且您可以在简单键和另一个更复杂的键之间进行映射。 Building a dictionary on the fly each time you need it is silly. 每次需要时动态构建字典都是愚蠢的。 You can use this if it's what you mean : your "conditions" are simple, static key values that map to objects. 如果它是你的意思,你可以使用它:你的“条件”是简单的,映射到对象的静态键值。

  • Variant behavior among subclasses . 子类之间的变体行为 Use Polymorphism instead of type checking objects. 使用多态而不是类型检查对象。 Correct. 正确。 If you have similar objects in multiple classes with variant behavior, they should be polymorphic. 如果在具有变体行为的多个类中有类似的对象,则它们应该是多态的。 Use this as often as possible. 尽可能经常使用它。

  • Other variant behavior . 其他变体行为 Use an if-elif-else ladder. 使用if-elif-else梯形图。 Use this when you don't have largely static key-to-value mapping. 如果没有很大的静态键值映射,请使用此选项。 Use this when the conditions are complex, or you mean procedures, not objects. 在条件复杂时使用此选项,或者您指的是过程,而不是对象。

Everything else is just tricky code that can achieve similar results. 其他一切都只是棘手的代码,可以达到类似的效果。

Using a Tuple. 使用元组。 This is just dictionary without the mapping. 这只是没有映射的字典。 This requires search, and search should be avoided whenever possible. 这需要搜索,应尽可能避免搜索。 Don't do this, it's inefficient. 不要这样做,效率低下。 Use a dictionary. 使用字典。

Using a function decorator ( http://code.activestate.com/recipes/440499/ ). 使用函数装饰器( http://code.activestate.com/recipes/440499/ )。 Icky. 恶心。 This conceals the if-elif-elif nature of the problem you're solving. 这隐瞒了你正在解决的问题的if-elif-elif性质。 Don't do this, it isn't obvious that the choices are exclusive . 不要这样做,选择是排他性的并不明显。 Use anything else. 使用其他任何东西

Someone even recommended the Visitor pattern. 有人甚至推荐了访客模式。 Use this when you have an object which follows the Composite design pattern. 如果有一个遵循Composite设计模式的对象,请使用此选项。 This depends on polymorphism to work, so it's not really a different solution. 这取决于多态性的工作,所以它并不是一个真正不同的解决方案。

In the first example I would certainly stick with the if-else statement. 在第一个例子中,我肯定会坚持使用if-else语句。 In fact I don't see a reason not to use if-else unless 事实上我没有理由不使用if-else除非

  1. You find (using eg the profile module) that the if statement is a bottleneck (very unlikely IMO unless you have a huge number of cases that do very little) 您发现(使用例如配置文件模块)if语句是一个瓶颈(非常不可能的IMO,除非您有大量的案例做得很少)

  2. The code using a dictionary is clearer / has less repetition. 使用字典的代码更清晰/重复次数更少。

Your second example I would actually rewrite 你的第二个例子我实际上会重写

setattr(self, name, value)

(probably adding an assert statement to catch invalid names). (可能添加一个断言语句来捕获无效名称)。

Considering that this is done in response to a user action (pickings something from a menu), and the number of choices you anticipate is very small, I'd definitely go with a simple if-elif-else ladder. 考虑到这是为了响应用户操作(从菜单中选择某些东西)而完成的,并且您预期的选择数量非常少,我肯定会选择一个简单的if-elif-else梯形图。

There's no point in optinizing for speed, since it only happens as fast as the user can make the selection anyway, this is not "inner loop of a raytracer"-territory. 选择速度是没有意义的,因为它的发生速度与用户无论如何都能进行选择一样快,这不是“光线跟踪器的内环” - 领域。 Sure, it matters to give the user quick feedback, but since the number of cases is so small, there is no danger of that either. 当然,给用户提供快速反馈很重要,但由于案例数量很少,因此也没有危险。

There's no point in optimizing for conciseness, since the (imo clearer, zero-readability-overhead) if-ladder will be so very short anyway. 优化简洁是没有意义的,因为(imo更清晰,零可读性开销)if-ladder无论如何都会非常短。

Regarding your dictionary questions: 关于你的字典问题:

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? 当我有100个案件或更多? 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? 如果我在函数之外为此目的构建一个dict,我应该检查它是否需要在哪里? What happens if it is not needed elsewhere? 如果在其他地方不需要它会发生什么?

  1. Another issue is maintainability. 另一个问题是可维护性。 Having the string->curveFunction dictionary allows you to data-drive the menu. 使用string-> curveFunction字典可以对菜单进行数据驱动。 Then adding another option is just a matter of putting another string->function entry in the dictionary (which lives in a part of the code dedicated to configuration. 然后添加另一个选项只是在字典中放入另一个字符串 - >函数条目(它存在于专用于配置的代码的一部分中)。

  2. Even if you have only a few entries, it "separates concerns"; 即使你只有几个条目,它也会“分开关注”; _setCurrentCurve is responsible for wiring up at run time, not defining the box of components. _setCurrentCurve负责在运行时进行连接,而不是定义组件盒。

  3. Build the dictionary and hold onto it. 构建字典并保持它。

  4. Even if it's not used elsewhere, you get the above benefits (locatability, maintainability). 即使它没有在别处使用,您也可以获得上述好处(可定位性,可维护性)。

My rule of thumb is to ask "What's going on here?" 我的经验法则是问“这里发生了什么?” for each component of my code. 对于我的代码的每个组件。 If the answer is of the form 如果答案是形式的

... and ... and ... ... 然后 ... 然后 ...

(as in "defining the library of functions and associating each with a value in the menu") then there are some concerns begging to be separated. (如“定义函数库并将每个函数菜单中的值相关联”)然后有一些问题需要分开。

Each of the exposed options fit well some scenarios: 每个暴露的选项都适合某些场景:

  1. if-elif-else: simplicity, clarity if-elif-else:简洁,清晰
  2. dictionary: useful when you configure it dynamically (imagine that you need a particular functionality to be executed on a branch) 字典:在动态配置它时很有用(想象一下,您需要在分支上执行特定功能)
  3. tuple: simplicity over if-else case for multiple choices per branch. 元组:简单优于if-else情况,每个分支有多个选项。
  4. polymorphism: automatic object oriented branching 多态性:自动面向对象的分支
  5. etc. 等等

Python is about readability and consistency and even if your decision will always be a subjective and it will depend on your style, you should always think about Python mantras. Python是关于可读性和一致性的,即使你的决定总是主观的,并且它将取决于你的风格,你应该总是考虑Python咒语。

./alex ./alex

I agree with df regarding the second example. 我同意df关于第二个例子。 The first example I would probably try to rewrite using a dictionary, particularly if all the curve constructors have the same type signature (perhaps using *args and/or **kwargs). 第一个例子我可能会尝试使用字典重写,特别是如果所有曲线构造函数都具有相同的类型签名(可能使用* args和/或** kwargs)。 Something like 就像是

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

or perhaps even 或者甚至是

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

In Python, don't event think about how to replace a switch statement. 在Python中,不要考虑如何替换switch语句。

Use classes and polymorphism instead. 请改用类和多态。 Try to keep the information about each availble choice and how to implement it in one place (ie the class that implements it). 尝试保留有关每个可用选择的信息以及如何在一个地方(即实现它的类)实现它。

Otherwise you will end up having lots of places that each contain a tiny fraction of each choice, and updating/extending will be a maintenance nightmare. 否则你最终将拥有许多地方,每个地方都包含每个选择的一小部分,更新/扩展将成为维护的噩梦。

This is exactly the kind of problem that OOD tries to solve by abstraction, information hiding, polymorphism and the lot. 这正是OOD试图通过抽象,信息隐藏,多态和批量来解决的问题。

Think about what classes of objects you have and their properties, then create an OO architecture around them. 考虑一下您拥有的对象类及其属性,然后围绕它们创建OO架构。 This way you will never ever have to worry about a missing "switch" statement again. 通过这种方式,您再也不用担心丢失“切换”语句了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM