how do I write a scliceable class?

E

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.

So what is there to do? Any suggestion?

Cheers.

Ernest
 
A

Alf P. Steinbach

* 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
 
D

Diez B. Roggisch

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
 
P

Peter Otten

Ernest said:
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
 
E

Ernest Adrogué

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 said:
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top