我最近阅读了建议不要在支持它的语言中使用switch-case语句的问题。就Python而言,我已经看到了许多交换机案例替换,例如:
鉴于各种各样的选项,我在确定如何处理特定代码时遇到了一些困难。我想学习选择其中一种方法的标准。此外,如果我在决定遇到问题的具体情况下(对选择的解释),我会很感激建议如何做。
以下是具体问题:
(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 ,何时使用其他每个选项?
答案 0 :(得分:24)
叹息。对问题的错误部分过分绞尽脑汁。 switch语句不是问题。表达“替代”的方法有很多,不能添加含义。
问题是含义 - 不是技术声明选择。
有三种常见的模式。
将键映射到对象。如果字典几乎完全是静态的,那么请使用字典,并且您可以在简单键和另一个更复杂的键之间进行映射。每次需要时动态构建字典都是愚蠢的。你可以使用它,如果它是意思:你的“条件”是简单的,映射到对象的静态键值。
子类之间的变体行为。使用多态而不是类型检查对象。正确。如果在具有变体行为的多个类中有类似的对象,则它们应该是多态的。尽可能经常使用它。
其他变体行为。使用if-elif-else梯形图。如果没有很大的静态键值映射,请使用此选项。当条件复杂时,或者表示程序而不是对象时使用此项。
其他一切都只是棘手的代码,可以达到类似的效果。
使用元组。这只是没有映射的字典。这需要搜索,应尽可能避免搜索。不要这样做,效率低下。使用字典。
使用函数装饰器(http://code.activestate.com/recipes/440499/)。恶心。这隐瞒了你正在解决的问题的if-elif-elif性质。不要这样做,选择独占并不明显。使用其他任何东西。
有人甚至推荐了访客模式。如果有一个遵循 Composite 设计模式的对象,请使用此选项。这取决于多态性的工作,所以它并不是一个真正不同的解决方案。
答案 1 :(得分:8)
在第一个例子中,我肯定会坚持使用if-else语句。事实上,除非
,否则我没有理由不使用if-else你发现(使用例如个人资料模块)if语句是一个瓶颈(非常不可能的IMO,除非你有大量的案例做得很少)
使用字典的代码更清晰/重复次数更少。
你的第二个例子我实际上会重写
setattr(self, name, value)
(可能添加一个断言语句来捕获无效名称)。
答案 2 :(得分:2)
考虑到这是为了响应用户操作(从菜单中选择某些东西)而完成的,并且您预期的选择数量非常少,我肯定会使用简单的if-elif-else梯形图。
选择速度是没有意义的,因为它的发生速度与用户无论如何都能进行选择一样快,这不是“光线跟踪器的内环” - 领域。当然,给用户提供快速反馈很重要,但由于案例数量很少,所以也没有危险。
优化简洁没有意义,因为(imo更清晰,零可读性开销)if-ladder无论如何都会非常短。
答案 3 :(得分:2)
关于字典问题:
据我所知,选择字典的原因是快速查找。什么时候有必要?当我有100个案件或更多?每次调用函数时,继续构建和丢弃字典是一个好主意吗?如果我在函数之外为此目的构建一个dict,我应该检查它是否需要在哪里?如果在其他地方不需要它会发生什么?
另一个问题是可维护性。使用string-> curveFunction字典可以让您对菜单进行数据驱动。然后添加另一个选项只是在字典中放入另一个string->函数条目(它存在于专用于配置的代码的一部分中。
即使你只有几个条目,它也会“分开关注”; _setCurrentCurve
负责在运行时进行连接,而不是定义组件框。
构建字典并按住它。
即使它没有在别处使用,您也可以获得上述好处(可定位性,可维护性)。
我的经验法则是问“这里发生了什么事?”对于我的代码的每个组件。如果答案是
的形式... 和 ... 和 ...
(如“定义函数库和将每个函数与菜单中的值相关联”)然后有一些问题需要分开。
答案 4 :(得分:1)
每个公开的选项都适合某些情况:
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架构。通过这种方式,您再也不用担心丢失“切换”语句了。