Newbie question about class operator overloading

  • Thread starter Rory Campbell-Lange
  • Start date
R

Rory Campbell-Lange

Hi. I'm just starting to use python.

I am anxious about how best to set and access items one level down in a
data structure if I am using __setitem__ and __getitem__.

At the moment I can do

for a data structure Data:

object.Data = { 'one' : [1, 2, {}, 4],
'two' : [5, 6, {}, 8]}

I can use normal __setitem__ and __getitem__ to address Data keys very
easily

However, if I wish to access object.Data['one'][0] for instance, I am
using the following:

object['three'] = [0, 'val0'] # set
x = object['three'][0] # get

Is this advisable? I'm worried the syntax is very odd.

Extract from an example class:

def __setitem__ (self,key,value):
if type(value) == list and type(value[0]) == int:
if key not in self.data:
self.data[key] = {}
self.data[key][value[0]] = value[1]
else:
self.data[key] = value

def __getitem__ (self,key,value=None):
if not value==None:
return self.data[key][value]
else:
return self.data[key]
 
S

Steven Bethard

Rory said:
Hi. I'm just starting to use python.

I am anxious about how best to set and access items one level down in a
data structure if I am using __setitem__ and __getitem__.

At the moment I can do

for a data structure Data:

object.Data = { 'one' : [1, 2, {}, 4],
'two' : [5, 6, {}, 8]}

What is the goal of this data structure? I have a feeling this needs to
be refactored...
However, if I wish to access object.Data['one'][0] for instance, I am
using the following:

object['three'] = [0, 'val0'] # set
x = object['three'][0] # get

Seems like you'd do much better with names for the items rather than an
index. Maybe something like:

py> class Record(object):
.... def __init__(self, foo, bar, dict, baz):
.... self.foo = foo
.... self.bar = bar
.... self.dict = dict
.... self.baz = baz
.... def __repr__(self):
.... return 'Record(%r, %r, %r, %r)' % (

.... self.foo, self.bar, self.dict, self.baz)
....
py> data = dict(one=Record(1, 2, {}, 4),
.... two=Record(5, 6, {}, 8))
py> data
{'two': Record(5, 6, {}, 8), 'one': Record(1, 2, {}, 4)}
py> data['one'].foo = 'val0'
py> data
{'two': Record(5, 6, {}, 8), 'one': Record('val0', 2, {}, 4)}
py> data['one'].foo
'val0'
Is this advisable? I'm worried the syntax is very odd.

Yes it is odd. If you want to be more consistent with other Python
syntax, but you don't want to use a Record, check for keys as tuples:

py> class C(object):
.... def __init__(self):
.... self.data = {}
.... def __setitem__(self, x, value):
.... try:
.... name, index = x
.... self.data.setdefault(name, {})[index] = value
.... except ValueError:
.... self.data[x] = value
.... def __getitem__(self, x):
.... try:
.... name, index = x
.... return self.data[name][index]
.... except ValueError:
.... return self.data[x]
....
py> c = C()
py> c.data
{}
py> c['one', 0] = 1
py> c['one', 3] = 4
py> c['two', 1] = 6
py> c['two', 2] = {}
py> c.data
{'two': {1: 6, 2: {}}, 'one': {0: 1, 3: 4}}
py>

As you can see, Python has builin syntax support to allow tuples to be
used as dict keys. (The parentheses can be omitted.)

Still, I think the class-based solution is much better than the
__getitem__/__setitem__ one.

STeVe
 
R

Rory Campbell-Lange

Hi Steve

I've been playing around with your two suggestions.

The Record class is an elegant solution. It doesn't however help in the
case where the class has the following general data structure (something
I should have stated originally):

class.config1 = param
class.config2 = param
class.data = {
'this' : []
'that' : []
...
}

The __setitem__ and __getitem__ methods allow the class.data data
structure to be dealt with easily as self.data[key] = val
without worrying about getting involved with other variables such as
config1 and config2 (because potentially a data key could be called
'config1' for example.

So the __getitem__ and __setitem__ give data hiding properties when I
inherit from this class.

I think!
Anyway, I'm very grateful for your help.

Rory

Rory Campbell-Lange wrote:
I am anxious about how best to set and access items one level down in a
data structure if I am using __setitem__ and __getitem__.
....
object['three'] = [0, 'val0'] # set
x = object['three'][0] # get
....

py> data['one'].foo = 'val0'
py> data
{'two': Record(5, 6, {}, 8), 'one': Record('val0', 2, {}, 4)}
py> data['one'].foo
'val0' ....
... def __setitem__(self, x, value):
... try:
... name, index = x
... self.data.setdefault(name, {})[index] = value
... except ValueError:
... self.data[x] = value
....
py> c['one', 0] = 1

This does seem a lot more logical than my object['three'] = [0, 'val0'].
Thanks for this (and using try against a possible ValueError).

....
 
S

Steven Bethard

Rory said:
Hi Steve

I've been playing around with your two suggestions.

The Record class is an elegant solution. It doesn't however help in the
case where the class has the following general data structure (something
I should have stated originally):

class.config1 = param
class.config2 = param
class.data = {
'this' : []
'that' : []
...
}

The __setitem__ and __getitem__ methods allow the class.data data
structure to be dealt with easily as self.data[key] = val
without worrying about getting involved with other variables such as
config1 and config2 (because potentially a data key could be called
'config1' for example.

Yeah, if the keys of your data dict might collide with the attributes in
your object, you can't make _data_ a Record type. On the other hand,
you can make the items _within_ data Record types, which is what my
original suggestion said. For example:

py> class Record(object):
.... def __init__(self, foo, bar, dict, baz):
.... self.foo = foo
.... self.bar = bar
.... self.dict = dict
.... self.baz = baz
.... def __repr__(self):
.... return 'Record(%r, %r, %r, %r)' % (
.... self.foo, self.bar, self.dict, self.baz)
....
py> class C(object):
.... def __init__(self, config1, config2):
.... self.config1 = config1
.... self.config2 = config2
.... self.data = dict(this=Record(1, 2, {}, 4),
.... that=Record(5, 6, {}, 8))
.... def __getitem__(self, item):
.... return self.data[item]
....
py> c = C(True, False)
py> c['this']
Record(1, 2, {}, 4)
py> c['that']
Record(5, 6, {}, 8)
py> c['this'].foo
1
py> c['that'].bar
6
py> c['that'].baz = 42
py> c['that']
Record(5, 6, {}, 42)
... def __setitem__(self, x, value):
... try:
... name, index = x
... self.data.setdefault(name, {})[index] = value
... except ValueError:
... self.data[x] = value ...

py> c['one', 0] = 1

This does seem a lot more logical than my object['three'] = [0, 'val0'].
Thanks for this (and using try against a possible ValueError).

No prob. Especially if you expect mostly to get items of the form
(name, index), the try/except is the way to go.

STeVe
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top