我有一个包含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的新手,但是我可以使用某种利用多态的类继承吗?
答案 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>