简体   繁体   中英

Python: Class design - List Transformations (critique my code)

I have been thinking about a class which could be useful for list transformations. Here is my current implementation:

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() 

This works well, however, the feeling of implementing something in a substandard manner bothers me.

What Python features would you use in this implementation, I haven't used? How would you improve the overall design/ideas behind this class? How would you improve the code?

Also, since reinventing the wheel feels awkward: what are the standard tools replacing this class?

Thanks.

Do not use an empty list as default argument. Use None and test for it:

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

I's a well known Python's beginner pitfall.

Having a general scope and not a particular use case in mind, I would look at this in a more "functional" way:

  • Don't make the tranformations in place -- rather return new lists. This is how standard functions in functional programming work (and also map() , filter() and reduce() in Python).

  • Concentrate on the transformations rather than on the data. In particular, I would not create a class like your ListTransform at all, but rather only have some kind of transformation objects that can be chained.

To code this having functional programming in mind, the transforms would simply be functions, just like in your design. All you would need in addition is some kind of composition for the transforms:

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

(For the sake of simplicity the given implementation has only two parameters instead of an arbitrary number.) Your example would now be very simple:

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]

An alternative would be to implement the transforms as classes instead of functions.

You could extend the list class itself, and apply the transforms lazily as the elements are needed. Here is a short implementation - it does not allow for index manipulation on the transforms, but you can apply any mapping transform in a stack.

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("'")

And you are ready to go:

>>> 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]'

Ok - I may have overreached with the "reduce" call in the getitem method - but that is the fun part. :-) Feel free to rewrite it in more lines for readability:

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

If you like the idea, you could include a "filter" member to create filtering functions for the items, and check for the number of parameters on the transforms to allow them to work with indexes, and even reach other list items.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM