how do I write a scliceable class?

Discussion in 'Python' started by Ernest Adrogué, Feb 13, 2010.

  1. Hello everybody,

    I'm designing a container class that supports slicing.
    The problem is that I don't really know how to do it.

    class MyClass(object):
    def __init__(self, input_data):
    self._data = transform_input(input_data)
    def __getitem__(self, key):
    if isinstance(key, slice):
    # return a slice of self
    pass
    else:
    # return a scalar value
    return self._data[key]

    The question is how to return a slice of self.
    First I need to create a new instance... but how? I can't
    use MyClass(self._data[key]) because the __init__ method
    expects a different kind of input data.

    Another option is

    out = MyClass.__new__(MyClass)
    out._data = self._data[key]
    return out

    But then the __init__ method is not called, which is
    undesirable because subclasses of this class might need
    to set some custom settings in their __init__ method.

    So what is there to do? Any suggestion?

    Cheers.

    Ernest
    Ernest Adrogué, Feb 13, 2010
    #1
    1. Advertising

  2. * Ernest Adrogué:
    > Hello everybody,
    >
    > I'm designing a container class that supports slicing.
    > The problem is that I don't really know how to do it.
    >
    > class MyClass(object):
    > def __init__(self, input_data):
    > self._data = transform_input(input_data)
    > def __getitem__(self, key):
    > if isinstance(key, slice):
    > # return a slice of self
    > pass
    > else:
    > # return a scalar value
    > return self._data[key]
    >
    > The question is how to return a slice of self.
    > First I need to create a new instance... but how? I can't
    > use MyClass(self._data[key]) because the __init__ method
    > expects a different kind of input data.
    >
    > Another option is
    >
    > out = MyClass.__new__(MyClass)
    > out._data = self._data[key]
    > return out
    >
    > But then the __init__ method is not called, which is
    > undesirable because subclasses of this class might need
    > to set some custom settings in their __init__ method.


    I'd go for it anyway, because the programmer who subclasses needs to understand
    the base class. And if, say, that class has a custom _init method, and it's
    documented that that what's a subclass should override, then, OK. No problem.


    > So what is there to do? Any suggestion?


    An alternative can be to simply check for argument value None in the
    constructor, and if so, don't do anything.


    Cheers & hth.,

    - Alf
    Alf P. Steinbach, Feb 13, 2010
    #2
    1. Advertising

  3. Am 13.02.10 13:51, schrieb Ernest Adrogué:
    > Hello everybody,
    >
    > I'm designing a container class that supports slicing.
    > The problem is that I don't really know how to do it.
    >
    > class MyClass(object):
    > def __init__(self, input_data):
    > self._data = transform_input(input_data)
    > def __getitem__(self, key):
    > if isinstance(key, slice):
    > # return a slice of self
    > pass
    > else:
    > # return a scalar value
    > return self._data[key]
    >
    > The question is how to return a slice of self.
    > First I need to create a new instance... but how? I can't
    > use MyClass(self._data[key]) because the __init__ method
    > expects a different kind of input data.
    >
    > Another option is
    >
    > out = MyClass.__new__(MyClass)
    > out._data = self._data[key]
    > return out
    >
    > But then the __init__ method is not called, which is
    > undesirable because subclasses of this class might need
    > to set some custom settings in their __init__ method.


    I'd say you can't have your cake and eat it. Either let the constructors
    work with data to produce whatever state the instance really contains.
    If that's the case, go with your second solution. Potentially, you need
    to make self._data[key] some method-call that might be overridden,
    something along the lines of __getstate__, to make sure subclasses
    return all data that is relevant to them.

    But if you really have child-class code that needs to be run on *every*
    object construction, then you should make input_data optional, and pass
    the transformed input in for the slice-creation, bypassing the
    transform_input.

    The only other solution I can think of is to return a
    MyClassSlice-instance, which is just a view to MyClass instances, and
    restricts e.g. key-spaces.


    class MyClassSlice(object):

    def __init__(self, state, slice):
    self.state = state
    self.slice = slice


    def __getitem__(self, key):
    if isinstance(key, slice):
    # create subslice & return that
    return MyClassSlice(self.state, merged_slice(key, self.slice))
    elif self.key_in_slice(key):
    return self._state[key]
    raise IndexError

    def key_in_slice(self, key):
    # this of course depends on your key-domain.


    Diez
    Diez B. Roggisch, Feb 13, 2010
    #3
  4. Ernest Adrogué

    Peter Otten Guest

    Ernest Adrogué wrote:

    > I'm designing a container class that supports slicing.
    > The problem is that I don't really know how to do it.
    >
    > class MyClass(object):
    > def __init__(self, input_data):
    > self._data = transform_input(input_data)
    > def __getitem__(self, key):
    > if isinstance(key, slice):
    > # return a slice of self
    > pass
    > else:
    > # return a scalar value
    > return self._data[key]
    >
    > The question is how to return a slice of self.
    > First I need to create a new instance... but how? I can't
    > use MyClass(self._data[key]) because the __init__ method
    > expects a different kind of input data.
    >
    > Another option is
    >
    > out = MyClass.__new__(MyClass)
    > out._data = self._data[key]
    > return out
    >
    > But then the __init__ method is not called, which is
    > undesirable because subclasses of this class might need
    > to set some custom settings in their __init__ method.
    >
    > So what is there to do? Any suggestion?


    Either

    (1) make transform_input() idempotent, i. e. ensure that

    transform_input(transform_input(data)) == transform_input(data)

    and construct the slice with MyClass(self._data[key])

    or

    (2) require it to be invertible with

    inverse_transform_input(transform_input(data)) == data

    and make the slice with MyClass(inverse_transform_input(self._data[key]))

    Just stating the obvious...

    Peter
    Peter Otten, Feb 13, 2010
    #4
  5. Hi,

    Thanks a lot for your comments. I think I've got enough
    information to make a decision now.

    13/02/10 @ 15:16 (+0100), thus spake Peter Otten:
    > Ernest Adrogué wrote:
    >
    > > I'm designing a container class that supports slicing.
    > > The problem is that I don't really know how to do it.
    > >
    > > class MyClass(object):
    > > def __init__(self, input_data):
    > > self._data = transform_input(input_data)
    > > def __getitem__(self, key):
    > > if isinstance(key, slice):
    > > # return a slice of self
    > > pass
    > > else:
    > > # return a scalar value
    > > return self._data[key]
    > >
    > > The question is how to return a slice of self.
    > > First I need to create a new instance... but how? I can't
    > > use MyClass(self._data[key]) because the __init__ method
    > > expects a different kind of input data.
    > >
    > > Another option is
    > >
    > > out = MyClass.__new__(MyClass)
    > > out._data = self._data[key]
    > > return out
    > >
    > > But then the __init__ method is not called, which is
    > > undesirable because subclasses of this class might need
    > > to set some custom settings in their __init__ method.
    > >
    > > So what is there to do? Any suggestion?

    >
    > Either
    >
    > (1) make transform_input() idempotent, i. e. ensure that
    >
    > transform_input(transform_input(data)) == transform_input(data)
    >
    > and construct the slice with MyClass(self._data[key])
    >
    > or
    >
    > (2) require it to be invertible with
    >
    > inverse_transform_input(transform_input(data)) == data
    >
    > and make the slice with MyClass(inverse_transform_input(self._data[key]))
    >
    > Just stating the obvious...
    >
    > Peter
    > --
    > http://mail.python.org/mailman/listinfo/python-list
    Ernest Adrogué, Feb 13, 2010
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. E11
    Replies:
    1
    Views:
    4,736
    Thomas Weidenfeller
    Oct 12, 2005
  2. =?Utf-8?B?QWJlbCBDaGFu?=
    Replies:
    6
    Views:
    9,314
    Rahul
    May 3, 2006
  3. Peter Hansen
    Replies:
    1
    Views:
    380
    Joona I Palaste
    Aug 24, 2003
  4. christopher diggins
    Replies:
    16
    Views:
    745
    Pete Becker
    May 4, 2005
  5. Joseph Turian
    Replies:
    5
    Views:
    585
Loading...

Share This Page