Hlelp clean up clumpsy code

I

It's me

Another newbie question.

There must be a cleaner way to do this in Python:

#### section of C looking Python code ####
a = [[1,5,2], 8, 4]
a_list = {}
i = 0
for x in a:
if isinstance(x, (int, long)):
x = [x,]
for w in [y for y in x]:
i = i + 1
a_list[w] = i
print a_list
#####

The code prints what I want but it looks so "C-like". How can I make it
more Python like?

Thanks,
 
S

Steven Bethard

It's me said:
Another newbie question.

There must be a cleaner way to do this in Python:

#### section of C looking Python code ####
a = [[1,5,2], 8, 4]
a_list = {}
i = 0
for x in a:
if isinstance(x, (int, long)):
x = [x,]
for w in [y for y in x]:
i = i + 1
a_list[w] = i
print a_list
#####

The code prints what I want but it looks so "C-like". How can I make it
more Python like?

Don't know what version of Python you're using, but if you're using 2.4
(or with a few slight modifications, with 2.3), you can write:

py> dict((item, i+1)
.... for i, item in enumerate(
.... a_sub_item
.... for a_item in a
.... for a_sub_item
.... in isinstance(a_item, (int, long)) and [a_item] or a_item))
{8: 4, 1: 1, 2: 3, 4: 5, 5: 2}

Basically, I use a generator expression to flatten your list, and then
use enumerate to count the indices instead of keeping the i variable.

Steve
 
N

Nick Coghlan

It's me said:
Another newbie question.

There must be a cleaner way to do this in Python:

#### section of C looking Python code ####
a = [[1,5,2], 8, 4]
a_list = {}
i = 0
for x in a:
if isinstance(x, (int, long)):
x = [x,]
for w in [y for y in x]:
i = i + 1
a_list[w] = i
print a_list
#####

The code prints what I want but it looks so "C-like". How can I make it
more Python like?

Firstly, calling your dictionary "a_list" is evil. . .

Secondly, explaining what you want the code to do in English is handy when
asking for help cleaning up code (since we then know which features are
deliberate, and which are accidental implementation artificacts).

If I'm reading the code correctly, you want to flatten a data structure which
may contain either substructures or actual elements.

A custom generator will do nicely:

Py> def flatten(seq):
.... for x in seq:
.... if hasattr(x, "__iter__"):
.... for y in flatten(x):
.... yield y
.... else:
.... yield x
....
Py> data = [[1,5,2],8,4]
Py> val_to_pos = {}
Py> for i, x in enumerate(flatten(data)):
.... val_to_pos[x] = i + 1
....
Py> print val_to_pos
{8: 4, 1: 1, 2: 3, 4: 5, 5: 2}

Not any shorter, but this version works correctly for any leaf elements which
don't supply __iter__ (e.g. strings), and internal elements which do (e.g.
tuples) and the depth is limited only by the maximum level of recursion. Don't
try to flatten a circular structure, though :)

You may not even need to write the generator, since if you have Tkinter, that
already supplies a near-equivalent function:

Py> from Tkinter import _flatten as flatten
Py> data = [[1,5,2],8,4]
Py> val_to_pos = {}
Py> for i, x in enumerate(flatten(data)):
.... val_to_pos[x] = i + 1
....
Py> print val_to_pos
{8: 4, 1: 1, 2: 3, 4: 5, 5: 2}

It even works with strings as leaf elements:

Py> data = [["abc","def",2],8,"xyz"]
Py> flatten(data)
('abc', 'def', 2, 8, 'xyz')

Cheers,
Nick.
 
I

It's me

Thanks, Steve and Nick.

Yes, that's what I need to do. I didn't know it's call "flattening" a list
structure but that's precisely what I needed to do.

Steve,

I am using 2.3 and so I will go with Nick's version.

Thanks to both for helping.


Nick Coghlan said:
It's me said:
Another newbie question.

There must be a cleaner way to do this in Python:

#### section of C looking Python code ####
a = [[1,5,2], 8, 4]
a_list = {}
i = 0
for x in a:
if isinstance(x, (int, long)):
x = [x,]
for w in [y for y in x]:
i = i + 1
a_list[w] = i
print a_list
#####

The code prints what I want but it looks so "C-like". How can I make it
more Python like?

Firstly, calling your dictionary "a_list" is evil. . .

Secondly, explaining what you want the code to do in English is handy when
asking for help cleaning up code (since we then know which features are
deliberate, and which are accidental implementation artificacts).

If I'm reading the code correctly, you want to flatten a data structure which
may contain either substructures or actual elements.

A custom generator will do nicely:

Py> def flatten(seq):
... for x in seq:
... if hasattr(x, "__iter__"):
... for y in flatten(x):
... yield y
... else:
... yield x
...
Py> data = [[1,5,2],8,4]
Py> val_to_pos = {}
Py> for i, x in enumerate(flatten(data)):
... val_to_pos[x] = i + 1
...
Py> print val_to_pos
{8: 4, 1: 1, 2: 3, 4: 5, 5: 2}

Not any shorter, but this version works correctly for any leaf elements which
don't supply __iter__ (e.g. strings), and internal elements which do (e.g.
tuples) and the depth is limited only by the maximum level of recursion. Don't
try to flatten a circular structure, though :)

You may not even need to write the generator, since if you have Tkinter, that
already supplies a near-equivalent function:

Py> from Tkinter import _flatten as flatten
Py> data = [[1,5,2],8,4]
Py> val_to_pos = {}
Py> for i, x in enumerate(flatten(data)):
... val_to_pos[x] = i + 1
...
Py> print val_to_pos
{8: 4, 1: 1, 2: 3, 4: 5, 5: 2}

It even works with strings as leaf elements:

Py> data = [["abc","def",2],8,"xyz"]
Py> flatten(data)
('abc', 'def', 2, 8, 'xyz')

Cheers,
Nick.
 
S

Scott David Daniels

Nick said:
A custom generator will do nicely:

Py> def flatten(seq):
... for x in seq:
... if hasattr(x, "__iter__"):
... for y in flatten(x):
... yield y
... else:
... yield x

Avoiding LBYL gives you:
def flatten(seq):
for x in seq:
try:
for y in flatten(x):
yield y
except TypeError:
yield x

--Scott David Daniels
(e-mail address removed)
 
J

Jeff Shannon

Scott said:
Avoiding LBYL gives you:
def flatten(seq):
for x in seq:
try:
for y in flatten(x):
yield y
except TypeError:
yield x

If I'm not mistaken, this will result in infinite recursion on
strings. 'for x in aString' will iterate over the characters in the
string, even if the string is only a single character, so "for y in
flatten('a'):" will not give a type error. You'd need to add
special-case tests to watch for this condition (and try not to be too
special-case and allow unicode objects to pass).

Nick's version works on strings (and unicode objects) because they
lack an __iter__() method, even though they follow the (older)
sequence protocol.

Jeff Shannon
Technician/Programmer
Credit International
 
P

Pierre Quentel

You can also do it in a more pythonic way but without generators :

# a = [[1,5,2], 8, 4]
# l = []
# for item in a:
# if isinstance(item, (int, long)):
# l.append(item)
# else:
# l+=item
# print dict([(item,i+1) for (i,item) in enumerate(l)])

It works in the same conditions as your original code (no nested lists)

A few other things :
- you don't have to type a comma for one-item lists : x = [x] works -
you probably confused with tuples where you must do x=(x,)
- instead of
# for w in [y for y in x]:
just do
# for w in x:
- for "i = i+1" there is a shortcut : i+=1 (see "l+=item" above)

Regards,
Pierre
 

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,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top