Python: дизайн класса - список преобразований (критический анализ моего кода) - PullRequest
0 голосов
/ 03 декабря 2010

Я думал о классе, который может быть полезен для преобразования списка.Вот моя текущая реализация:

class ListTransform(object):
    """Specs: stores original list + transformations.
    Transformations are stored in a list.
    Every transformation is a func call, with
    one parameter, transformations are done in place.
    """
    def __init__(self, _list):
        self.orig_list = _list
        self.reset()
    def addtransform(self,t):
        self.transforms.append(t)
    def reset(self, ts = []):
        self.transforms = ts
    def getresult(self):
        li = self.orig_list[:] # start from a copy from the original
        # call all the in-place transform functions in order
        for transform in self.transforms:
            transform(li)
        return li

def pick_transform(pickindexes):
    """Only includes elements with specific indexes
    """    
    def pt(li):
        newli = []
        for idx in pickindexes:
            newli.append(li[idx])
        del li[:] # clear all the elements
        li.extend(newli)
    return pt

def map_transform(fn_for_every_element):
    """Creates a transformation, which will call a specific
    function for every element in a list
    """
    def mt(li):
        newli = map(fn_for_every_element, li)
        del li[:] # clear
        li.extend(newli)
    return mt
# example:

# the object which stores the original list and the transformations
li = ListTransform([0,10,20,30,40,50,60,70,80,90])

# transformations
li.addtransform(map_transform(lambda x: x + (x/10)))
li.addtransform(pick_transform([5,6,7]))

# getting result, prints 55, 66, 77
print li.getresult() 

Это хорошо работает, однако, чувство реализации чего-то некачественным образом беспокоит меня.

Какие функции Python вы бы использовали в этомреализацию я не использовал?Как бы вы улучшили общий дизайн / идеи этого класса?Как бы вы улучшили код?

Кроме того, с тех пор как изобретать колесо кажется неловким: какие стандартные инструменты заменяют этот класс?

Спасибо.

Ответы [ 3 ]

2 голосов
/ 03 декабря 2010

Имея общий охват, а не конкретный вариант использования, я бы посмотрел на это более «функционально»:

  • Не вносите изменения - верните новые списки. Так работают стандартные функции в функциональном программировании (а также map(), filter() и reduce() в Python).

  • Сконцентрируйтесь на преобразованиях, а не на данных. В частности, я бы вообще не создавал класс, подобный вашему ListTransform, а имел бы только какие-то объекты преобразования, которые можно объединить в цепочку.

Чтобы кодировать это, имея в виду функциональное программирование, преобразования просто будут функциями, как в вашем проекте. Все, что вам нужно, это какая-то композиция для преобразований:

def compose(f, g):
    return lambda lst: f(g(lst))

(Для простоты в данной реализации есть только два параметра вместо произвольного числа.) Ваш пример теперь будет очень простым:

from functools import partial
map_transform = partial(map, lambda x: x + (x/10))
pick_transform = lambda lst: [lst[i] for i in (5,6,7)]
transform = compose(pick_transform, map_transform)
print transform([0,10,20,30,40,50,60,70,80,90])
# [55, 66, 77]

Альтернативой может быть реализация преобразований в виде классов вместо функций.

2 голосов
/ 03 декабря 2010

Не используйте пустой список в качестве аргумента по умолчанию.Используйте None и проверьте его:

def some_method(self, arg=None):
    if arg is None:
        arg = []
    do_your_thing_with(arg)

Я - известная ошибка начинающего Python.

0 голосов
/ 04 декабря 2010

Вы могли бы расширить сам класс list и применять преобразования лениво по мере необходимости.Вот краткая реализация - она ​​не позволяет манипулировать индексами для преобразований, но вы можете применить любое преобразование отображения в стеке.

class ListTransform(list):
    def __init__(self, *args):
        list.__init__(self, *args)
        self.transforms = []
    def __getitem__(self, index):
        return reduce(lambda item, t: t(item), self.transforms, list.__getitem__(self, index))
    def __iter__(self):
        for index in xrange(len(self)):
            yield self[index]
    def __repr__(self):
        return "'[%s]'" % ", ".join(repr(item) for item in self)
    __str__ = lambda s: repr(s).strip("'")

И вы готовы к работе:

>>> a = ListTransform( range(10))
>>> a
'[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]'
>>> a.transforms.append(lambda x: 2 * x)>>> a
'[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]'
>>> a.transforms.append(lambda x: x + 5)
>>> a
'[5, 7, 9, 11, 13, 15, 17, 19, 21, 23]'
>>> a.append(0)
>>> a
'[5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 5]'

Хорошо - возможно, я перехитрил вызов "Reduce" в методе getitem - но это забавная часть.:-) Не стесняйтесь переписать его в несколько строк для удобства чтения:

def __getitem__(self, index):
   item = list.__getitem__(self, index)
   for t in self.transforms:
       item = t(item)
   return item

Если вам понравилась идея, вы можете включить элемент «filter» для создания функций фильтрации для элементов и проверить количествопараметров в преобразованиях, чтобы они могли работать с индексами и даже достигать других элементов списка.

...