complex data structure - insert value

Nick Craig-Wood nick at craig-wood.com
Sun Oct 10 05:30:02 EDT 2004


Jeffrey Froman <jeffrey at fro.man> wrote:
>  spar wrote:
> 
> > Basically I'd like to do the same thing in Python. I've written a
> > convoluted test script where I can print an element:
> > 
> > print data[0]['CC'][1]
> > 
> > but I can't insert directly like I do in Perl (data[0]['CC'][1] =
> > $var).
> 
>  This should work fine, though in python this would result in assignment, not
>  insertion:
> 
> >>> #Assignment:
> >>> x=[{'foo':[1,2,3]}]
> >>> x[0]['foo'][1] = 'bar'
> >>> x
>  [{'foo': [1, 'bar', 3]}]
> 
> >>> #Insertion:
> >>> x[0]['foo'].insert(2, 'baz')
> >>> x
>  [{'foo': [1, 'bar', 'baz', 3]}]

The OP is relying on a perl feature called auto-vivification.  In perl
speak when  you do

  $data->[0]->{'CC'}->[1] = "spam";

The only thing that has to exist is the array reference in $data.
Perl will then auto-vivify the hash then the second arrayref.

Python doesn't do this in its standard list and dict objects.  So
either you create some subclasses which do (which isn't that hard -
need to overload __setitem__ in both cases and do the perl thing).

Alternatively you can do it the python way...  The original

foreach $bar_count(@bars) {
  foreach $el_count(@els) {
    $var = somefunc($bar_count,$el_count);
    $data[$bar_count]->{'CC'}->[$el_count] = $var;
  }
}

becomes

def somefunc(a,b): return 1
bars=[1, 2, 3, 4, 5]
els=[7, 8]

data = []
for bar_count in bars:
  for el_count in els:
    var = somefunc(bar_count, el_count)
    while bar_count >= len(data):
      data.append({})
    l = data[bar_count].setdefault('CC', [])
    while el_count >= len(l):
      l.append(None)
    l[el_count] = var


>>> pprint(data)
[{},
 {'CC': [None, None, None, None, None, None, None, 1, 1]},
 {'CC': [None, None, None, None, None, None, None, 1, 1]},
 {'CC': [None, None, None, None, None, None, None, 1, 1]},
 {'CC': [None, None, None, None, None, None, None, 1, 1]},
 {'CC': [None, None, None, None, None, None, None, 1, 1]}]


Or you may prefer this which you can't do in perl (because keys of
hashes can only be strings)

data = {}
for bar_count in bars:
  for el_count in els:
    var = somefunc(bar_count, el_count)
    data[(bar_count,'CC',el_count)] = var

>>> pprint(data)
{(1, 'CC', 7): 1,
 (1, 'CC', 8): 1,
 (2, 'CC', 7): 1,
 (2, 'CC', 8): 1,
 (3, 'CC', 7): 1,
 (3, 'CC', 8): 1,
 (4, 'CC', 7): 1,
 (4, 'CC', 8): 1,
 (5, 'CC', 7): 1,
 (5, 'CC', 8): 1}

It does't produce quite the same data structures but it may be useful
for you.

However one thing I've noticed from converting quite a few perl
programs to python is that constant 'CC' in a hash is indicative of
the fact that you really should be defining a class, of which CC
becomes an attribute or method.  In perl the way is to define complex
data structures of hashes and lists, but in python because its so much
easier to create a class when you want it, do that.

So whenever you see $hash->{constant_string} you should be thinking -
hmm shouldn't that be a class.  And when you've made it a class you'll
find that you have functions which should be methods of that class,
and before you know it you'll find your program looks a lot tidier!

-- 
Nick Craig-Wood <nick at craig-wood.com> -- http://www.craig-wood.com/nick



More information about the Python-list mailing list