Python ленивый словарь оценки - PullRequest
3 голосов
/ 12 августа 2010

Евангелисты Python скажут, что причина, по которой у Python нет оператора switch, заключается в том, что у него есть словари.Итак ... как я могу использовать словарь, чтобы решить эту проблему здесь?Проблема состоит в том, что все значения оцениваются по некоторым значениям и вызывают исключения в зависимости от ввода.

Это просто тупой пример класса, который хранит число или список чисел и обеспечивает умножение.

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>
  File "<stdin>", line 8, in __mul__
TypeError: can't multiply sequence by non-int of type 'float'

Один из способов, который я мог бы решить, - это префикс каждого значения с помощью "lambda:", а после поиска в словаре вызвать функцию lambda .... "} (isinsta ...)"

Есть ли лучший способ?

Ответы [ 3 ]

4 голосов
/ 13 августа 2010

Да, определите маленькие лямбды для этих разных опций:

    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)

В идеале, конечно, вы должны определять эти лямбды только один раз в области видимости класса или модуля. Я только что показал их в методе __mul__ здесь для простоты использования.

1 голос
/ 13 августа 2010

Я думаю, что главное здесь - удобочитаемость.
Поисковый словарь, который вы показывали, определенно труден для чтения, и поэтому его нужно поддерживать.

На мой взгляд, основной целью при написании программного обеспечения должна быть читаемость; по этой причине я бы пошел для набора if / elif, явно сравнивая два значения (вместо того, чтобы иметь отображение для типов); затем, если измерения показывают проблемы с производительностью, можно найти другие решения (например, поиск в словаре с функциями).

1 голос
/ 12 августа 2010

Я могу думать о двух подходах здесь:

  • Некоторые if заявления. Всего четыре комбинации True и False не так уж и плохи. Последовательности if ... elif ... elif ..., как я видел, нередки в коде Python.

  • Создание dict один раз (как поле класса, а не как поле экземпляра) и сохранение (лямбда) функций внутри него. Это масштабируется лучше, чем предыдущий подход, и быстрее для многих вариантов (хотя я не знаю значения «многие»).

...