Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Python (http://www.velocityreviews.com/forums/f43-python.html)
-   -   Hlelp clean up clumpsy code (http://www.velocityreviews.com/forums/t339920-hlelp-clean-up-clumpsy-code.html)

It's me 01-04-2005 06:38 AM

Hlelp clean up clumpsy code
 
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,




Steven Bethard 01-04-2005 09:45 AM

Re: Hlelp clean up clumpsy code
 
It's me wrote:
> 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

Nick Coghlan 01-04-2005 10:17 AM

Re: Hlelp clean up clumpsy code
 
It's me wrote:
> 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.

--
Nick Coghlan | ncoghlan@email.com | Brisbane, Australia
---------------------------------------------------------------
http://boredomandlaziness.skystorm.net

It's me 01-04-2005 03:12 PM

Re: Hlelp clean up clumpsy code
 
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" <ncoghlan@iinet.net.au> wrote in message
news:mailman.121.1104833878.22381.python-list@python.org...
> It's me wrote:
> > 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.
>
> --
> Nick Coghlan | ncoghlan@email.com | Brisbane, Australia
> ---------------------------------------------------------------
> http://boredomandlaziness.skystorm.net




Scott David Daniels 01-04-2005 04:18 PM

Re: Hlelp clean up clumpsy code
 
Nick Coghlan wrote:
> 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
Scott.Daniels@Acm.Org

Jeff Shannon 01-04-2005 07:33 PM

Re: Help clean up clumsy code
 
Scott David Daniels wrote:

> Nick Coghlan wrote:
>
>> 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


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


Pierre Quentel 01-04-2005 09:28 PM

Re: Hlelp clean up clumpsy code
 
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


All times are GMT. The time now is 06:51 AM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.