Python evangelists will say the reason Python doesn't have a switch statement is because it has dictionaries. So... how can I use a dictionary to solve this problem here? The problem is that all values are being evaluated some and raising exceptions depending on the input.
This is just a dumb example of a class that stores a number or a list of numbers and provides multiplication.
class MyClass(object):
def __init__(self, value):
self._value = value
def __mul__(self, other):
return {
(False, False): self._value * other._value ,
(False, True ): [self._value * o for o in other._value] ,
(True , False): [v * other._value for v in self._value] ,
(True , True ): [v * o for v, o in zip(self._value, other._value)],
}[(isinstance(self._value, (tuple, list)), isinstance(other._value, (tuple, list)))]
def __str__(self):
return repr(self._value)
__repr__ = __str__
>>> x = MyClass(2.0)
>>> y = MyClass([3.0, 4.0, 5.0])
>>> print x
2.0
>>> print y
[3.0, 4.0, 5.0]
>>> print x * y
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
开发者_Go百科 File "<stdin>", line 8, in __mul__
TypeError: can't multiply sequence by non-int of type 'float'
One way that I could solve it would be to prefix each value with "lambda : " and at after the dictionary lookup call the lambda function .... "}(isinsta ...)"
Is there a better way?
Yes, define small lambdas for these different options:
def __mul__(self, other):
scalar_times_scalar = lambda x,y: x*y
scalar_times_seq = lambda x,y: [x*y_i for y_i in y]
seq_times_scalar = lambda x,y: scalar_times_seq(y,x)
seq_times_seq = lambda x,y: [x_i*y_i for x_i,y_i in zip(x,y)]
self_is_seq, other_is_seq = (isinstance(ob._value,(tuple, list))
for ob in (self, other))
fn = {
(False, False): scalar_times_scalar,
(False, True ): scalar_times_seq,
(True , False): seq_times_scalar,
(True , True ): seq_times_seq,
}[(self_is_seq, other_is_seq)]
return fn(self._value, other._value)
Ideally, of course, you would define these lambdas only once at class or module scope. I've just shown them in the __mul__
method here for ease of reference.
I can think of two approaches here:
Some
if
statements. For just four combinations ofTrue
andFalse
, it's not that bad. Sequences ofif
...elif
...elif
... clauses are, from what I've seen, not uncommon in Python code.Creating the dict once (as a class field, rather than an instance field), and storing (lambda) functions inside it. This scales better than the previous approach and is faster for many options (although I don't know the value of "many").
I think the main point here is readability.
A dictionary lookup as the one you showed is definitely difficult to read, and therefore to maintain.
In my opinion, main goal while writing software should be readability; for this reason, I would go for a set of if/elif explicitely comparing the two values (instead of having the mapping for the types); then if measurements shows performance concerns, other solutions (like a dictionary lookup with functions) could be explored.
精彩评论