代码气味 - if / else构造

时间:2013-12-10 14:59:18

标签: python oop code-duplication

我有一个包含9个元素的列表。前三个代表位置,下一个速度,下一个力量。

有时我需要来自阵列的力量,其他时间速度和其他时间位置。

所以我写了一个函数如下:

def Extractor(alist,quantity):

    if quantity=='positions':
        x = alist[0]
        y = alist[1]
        z = alist[2]
        return (x,y,z)

    elif quantity=='velocities':
        vx = alist[3]
        vy = alist[4]
        vz = alist[5]
        tot_v = np.sqrt(vx**2 + vy**2 + vz**2)
        return (vx,vy,vz,tot_v)

    elif quantity=='forces':
        fx = alist[6]
        fy = alist[7]
        fz = alist[8]
        tot_f = np.sqrt(fx**2 + fy**2 + fz**2)
        return (fx,fy,fz,tot_f)
    else:
        print "Do not recognise quantity: not one of positions, velocities, force"

然而,由于代码重复,这似乎是一个巨大的代码味道。是否有更好,更pythonic的方式来做到这一点?我是OOP的新手,但是我可以使用某种利用多态的类继承吗?

4 个答案:

答案 0 :(得分:14)

您的方法违反了Single Responsibility Principle。考虑将它拆分为:

def positionExtractor(alist):
    return tuple(alist[0:3])

def velocityExtractor(alist):
    velocity = tuple(alist[3:6])
    return velocity + (np.sqrt(sum(x**2 for x in velocity)),)

def forcesExtractor(alist):
    forces = tuple(alist[6:9])
    return forces + (np.sqrt(sum(x**2 for x in forces)),)

你可以将它们放在字典中:

extractors = {
    'position' : positionExtractor,
    'velocity' : velocityExtractor,
    'forces' : forcesExtractor}

并使用:

result = extractors[quantity](alist)

以下是继承的示例。但是,对于这么简单的任务来说似乎过度工程了:

import numpy as np

class Extractor:
    def extract(self, alist):
        raise NotImplementedError()

class IndexRangeExtractor(Extractor):
    def __init__(self, fromIndex, toIndex):
        self.fromIndex = fromIndex
        self.toIndex = toIndex

    def extract(self, alist):
        return tuple(alist[self.fromIndex:self.toIndex])

class EuclideanDistanceExtractorDecorator(Extractor):
    def __init__(self, innerExtractor):
        self.innerExtractor = innerExtractor

    def extract(self, alist):
        innerResult = self.innerExtractor.extract(alist)
        distance = np.sqrt(sum(x**2 for x in innerResult))

        return innerResult + (distance,)

#... 

class ExtractorFactory:
    def __init__(self):
        self.extractors = {
            'position':IndexRangeExtractor(0, 3),
            'velocity':EuclideanDistanceExtractorDecorator(
                IndexRangeExtractor(3, 6)),
            'forces':EuclideanDistanceExtractorDecorator(
                IndexRangeExtractor(6, 9))}

    def createExtractor(self, quantity):
        return self.extractors[quantity]


alist = [1,2,3,4,5,6,7,8,9]
ef = ExtractorFactory()
e1 = ef.createExtractor('position')
e2 = ef.createExtractor('velocity')
e3 = ef.createExtractor('forces')

print e1.extract(alist)
print e2.extract(alist)
print e3.extract(alist)

答案 1 :(得分:4)

您可以先使用偏移来挑选元素;除了positions以外,所有人都需要使用公式:

_slices = {'positions': slice(3), 'velocities': slice(3, 6), 'forces': slice(6, 9)}

def Extractor(alist, quantity):
    try:
        a, b, c = alist[_slices[quantity]]
        tot = np.sqrt(a**2 + b**2 + c**2)
        return a, b, c, tot
    except KeyError:
         raise ValueError(
             "Do not recognise quantity: "
             "not one of {}".format(', '.join(_slices)))        

返回一致个值;如果无法计算positions的平方根,我会返回0.0总数:

tot = np.sqrt(a**2 + b**2 + c**2) if quantity != 'positions' else 0.0

答案 2 :(得分:2)

为什么不尝试这样的事情:

    def extract_position(x,y,z):
        return (x, y, z)

    def extract_velocities(x,y,z):
        return (x, y, z, np.sqrt(x**2 + y**2 + z**2))

    def extract_forces(x,y,z):
        return (x, y, z, np.sqrt(x**2 + y**2 + z**2))

    extractor = { 'positions': extract_position,
                  'velocities': extract_velocities,
                  'forces': extract_forces }

    try:

        print extractor['positions'](1,2,3)

        print extractor['unknown'](4,5,6)

    except KeyError:
        print "Do not recognise quantity: not one of positions, velocities, force"

我更喜欢使用函数指针将数据绑定到任意计算。此外,字典取代了开关样式语法,因此至少感觉与您所寻找的类似。

你也可以用同样的方法来确定速度和力量,所以你也可以压缩它。

答案 3 :(得分:1)

可能是这样的:

from operator import itemgetter

def extract(sequence, quantity):
    try:
        a, b, c = {
            'positions': itemgetter(0, 1, 2),
            'velocities': itemgetter(3, 4, 5),
            'forces': itemgetter(6, 7, 8)
        }[quantity](sequence)
        return a, b, c, np.sqrt(a**2 + b**2, c**2)
    except KeyError as e:
        pass # handle no suitable quantity found here

请注意,总是执行计算...保持返回值一致为4元组...除非它是真正昂贵的计算,否则这不应成为问题。< / p>