Hlelp clean up clumpsy code

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • It's me

    #1

    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

    #2
    Re: Hlelp clean up clumpsy code

    It's me wrote:[color=blue]
    > 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?[/color]

    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_it em, (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

    Comment

    • Nick Coghlan

      #3
      Re: Hlelp clean up clumpsy code

      It's me wrote:[color=blue]
      > 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?[/color]

      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(flatt en(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(flatt en(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
      ---------------------------------------------------------------

      Comment

      • It's me

        #4
        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.12 1.1104833878.22 381.python-list@python.org ...[color=blue]
        > It's me wrote:[color=green]
        > > 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?[/color]
        >
        > 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[/color]
        which[color=blue]
        > 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(flatt en(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[/color]
        which[color=blue]
        > 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.[/color]
        Don't[color=blue]
        > try to flatten a circular structure, though :)
        >
        > You may not even need to write the generator, since if you have Tkinter,[/color]
        that[color=blue]
        > 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(flatt en(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[/color]


        Comment

        • Scott David Daniels

          #5
          Re: Hlelp clean up clumpsy code

          Nick Coghlan wrote:[color=blue]
          > 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[/color]

          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@A cm.Org

          Comment

          • Jeff Shannon

            #6
            Re: Help clean up clumsy code

            Scott David Daniels wrote:
            [color=blue]
            > Nick Coghlan wrote:
            >[color=green]
            >> 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[/color]
            >
            >
            > Avoiding LBYL gives you:
            > def flatten(seq):
            > for x in seq:
            > try:
            > for y in flatten(x):
            > yield y
            > except TypeError:
            > yield x[/color]

            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

            Comment

            • Pierre Quentel

              #7
              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

              Comment

              Working...