appending to a list via properties

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Lonnie Princehouse

    appending to a list via properties

    Here's a curious hack I want to put up for discussion. I'm thinking of
    writing a PEP for it.

    Observation
    -----------------
    I found myself using this construct for assembling multiple lists:

    foo = []
    qux = []

    while some_condition:
    a, b = calculate_somet hing()
    foo.append(a)
    qux.append(b)

    Not elegant! It requires temporary variables a and b, which are only
    used to populate the lists.


    Suggestion
    ----------------

    class better_list (list):
    tail = property(None, list.append)

    foo = better_list()
    qux = better_list()

    while some_condition:
    foo.tail, qux.tail = calculate_somet hing()

    Granted, the name tail might not be the best, but you get the idea.


    Alternatives
    ------------------

    1. Use "append" instead, preserving original list.append behavior.

    class better_list (list):
    append = property(lambda l: lambda x: list.append(l,x ),
    list.append)

    2. Use an external wrapper, similar to operator.*gette r

    class propertize (object):
    def __init__(self, target):
    self.__target__ = target
    def __setattr__(sel f, attribute, value):
    if attribute.start swith('_'): return
    object.__setatt r__(self, attribute, value)
    else: getattr(self.__ target__, attribute)(valu e)

    propertize(foo) .append, propertize(qux) .append =
    calculate_somet hing()


    Well?

  • Larry Bates

    #2
    Re: appending to a list via properties

    Lonnie Princehouse wrote:[color=blue]
    > Here's a curious hack I want to put up for discussion. I'm thinking of
    > writing a PEP for it.
    >
    > Observation
    > -----------------
    > I found myself using this construct for assembling multiple lists:
    >
    > foo = []
    > qux = []
    >
    > while some_condition:
    > a, b = calculate_somet hing()
    > foo.append(a)
    > qux.append(b)
    >
    > Not elegant! It requires temporary variables a and b, which are only
    > used to populate the lists.
    >
    >
    > Suggestion
    > ----------------
    >
    > class better_list (list):
    > tail = property(None, list.append)
    >
    > foo = better_list()
    > qux = better_list()
    >
    > while some_condition:
    > foo.tail, qux.tail = calculate_somet hing()
    >
    > Granted, the name tail might not be the best, but you get the idea.
    >
    >
    > Alternatives
    > ------------------
    >
    > 1. Use "append" instead, preserving original list.append behavior.
    >
    > class better_list (list):
    > append = property(lambda l: lambda x: list.append(l,x ),
    > list.append)
    >
    > 2. Use an external wrapper, similar to operator.*gette r
    >
    > class propertize (object):
    > def __init__(self, target):
    > self.__target__ = target
    > def __setattr__(sel f, attribute, value):
    > if attribute.start swith('_'): return
    > object.__setatt r__(self, attribute, value)
    > else: getattr(self.__ target__, attribute)(valu e)
    >
    > propertize(foo) .append, propertize(qux) .append =
    > calculate_somet hing()
    >
    >
    > Well?
    >[/color]
    Not entirely sure I understand, but here goes. I normally
    use list comprehensions. I'm assuming that calculate_somet hing()
    is acting on a list of items. If not then maybe some underlying
    data refactoring is in order. If so you write:

    results=[calculate_somet hing(x) for x in list_of_items]
    foo=result[x[0] for x in results]
    qux=result[x[1] for x in results]

    Without knowing more about what calculate_somet hing() does
    and what condition terminates the loop it is hard to say if
    this will work for you.

    -Larry Bates

    Comment

    • Carl Banks

      #3
      Re: appending to a list via properties

      Lonnie Princehouse wrote:[color=blue]
      > Here's a curious hack I want to put up for discussion. I'm thinking of
      > writing a PEP for it.[/color]

      A minor library change wouldn' t need a PEP.
      [color=blue]
      > Observation
      > -----------------
      > I found myself using this construct for assembling multiple lists:
      >
      > foo = []
      > qux = []
      >
      > while some_condition:
      > a, b = calculate_somet hing()
      > foo.append(a)
      > qux.append(b)
      >
      > Not elegant![/color]

      I don't agree; however:
      [color=blue]
      > class better_list (list):
      > tail = property(None, list.append)[/color]

      This is an impressive, spiffy little class.
      [color=blue]
      > Well?[/color]

      I suspect it's too sneaky and not commonly useful enough to get serious
      consideration for the standard library. But definitely submit it to
      Python Cookbook:


      Carl Banks


      P.S. to get rid of temporary variables while using regular lists:

      growing_lists = foo,qux
      while some_condition:
      for (s,x) in zip(growing_lis t,calculate_som ething()):
      list.append(s,x )

      No I don't really recommend it.

      Comment

      • Alex Martelli

        #4
        Re: appending to a list via properties

        Carl Banks <invalidemail@a erojockey.com> wrote:
        ...[color=blue][color=green]
        > > class better_list (list):
        > > tail = property(None, list.append)[/color]
        >
        > This is an impressive, spiffy little class.[/color]

        Yes, nice use of property.
        [color=blue]
        > growing_lists = foo,qux
        > while some_condition:
        > for (s,x) in zip(growing_lis t,calculate_som ething()):
        > list.append(s,x )
        >
        > No I don't really recommend it.[/color]

        Why not? Seems OK. Maybe simplified to:

        while some_condition:
        for alist, anitem in zip((foo, qux), calculate_somet hing()):
        alist.append(an item)

        If you want to hoist for performance, you can hoist more:

        appenders = foo.append, qux.append
        while some_condition:
        for appender, anitem in zip(appenders, calculate_somet hing()):
        appender(anitem )


        Alex

        Comment

        • Carl Banks

          #5
          Re: appending to a list via properties


          Alex Martelli wrote:[color=blue]
          > Carl Banks <invalidemail@a erojockey.com> wrote:
          > ...[color=green][color=darkred]
          > > > class better_list (list):
          > > > tail = property(None, list.append)[/color]
          > >
          > > This is an impressive, spiffy little class.[/color]
          >
          > Yes, nice use of property.
          >[color=green]
          > > growing_lists = foo,qux
          > > while some_condition:
          > > for (s,x) in zip(growing_lis t,calculate_som ething()):
          > > list.append(s,x )
          > >
          > > No I don't really recommend it.[/color]
          >
          > Why not? Seems OK. Maybe simplified to:[/color]

          Well, for this simple case I guess. If you have a lot of lists, or
          you're trying to write a general multiple list appender, it would be
          fine.

          Carl Banks

          Comment

          • Xavier Morel

            #6
            Re: appending to a list via properties

            Alex Martelli wrote:[color=blue]
            > Carl Banks <invalidemail@a erojockey.com> wrote:
            > ...[color=green][color=darkred]
            >>> class better_list (list):
            >>> tail = property(None, list.append)[/color]
            >> This is an impressive, spiffy little class.[/color]
            >
            > Yes, nice use of property.
            >
            > Alex[/color]

            I don't know, I usually see people considering that properties are
            "cool" as long as they don't have side effects, as long as they're
            virtual members.

            The tail property doesn't behave like member data at all, the semantics
            are strange, counter-intuitive (tail would usually be the end of the
            list, either [-1] or [1:], in this case it's some magic position at the
            end of the list). And it has one hell of a side effect.

            Comment

            • Alex Martelli

              #7
              Re: appending to a list via properties

              Xavier Morel <xavier.morel@m asklinn.net> wrote:
              [color=blue]
              > Alex Martelli wrote:[color=green]
              > > Carl Banks <invalidemail@a erojockey.com> wrote:
              > > ...[color=darkred]
              > >>> class better_list (list):
              > >>> tail = property(None, list.append)
              > >> This is an impressive, spiffy little class.[/color]
              > >
              > > Yes, nice use of property.
              > >
              > > Alex[/color]
              >
              > I don't know, I usually see people considering that properties are
              > "cool" as long as they don't have side effects, as long as they're
              > virtual members.[/color]

              If a property didn't have any side effects, there would be no reason to
              define it as a property. I do fully expect x.y=z to have "side effects"
              on z when y is an assignable property.
              [color=blue]
              > The tail property doesn't behave like member data at all, the semantics
              > are strange, counter-intuitive (tail would usually be the end of the
              > list, either [-1] or [1:], in this case it's some magic position at the
              > end of the list). And it has one hell of a side effect.[/color]

              The name 'tail' may not be ideal (for people coming from languages where
              'head' is the first element and 'tail' is all the others, in
              particular). But the side effect doesn't look hellish at all to me.


              Alex

              Comment

              • Peter Otten

                #8
                Re: appending to a list via properties

                [Alex Martelli]
                [color=blue]
                > If you want to hoist for performance, you can hoist more:
                >
                > appenders = foo.append, qux.append
                > while some_condition:
                > for appender, anitem in zip(appenders, calculate_somet hing()):
                > appender(anitem )[/color]

                You are of course claiming a performance improvement over Carl's variant,
                but looking back into the initial post

                [Lonnie Princehouse]
                [color=blue]
                > foo = []
                > qux = []
                >
                > while some_condition:
                > a, b = calculate_somet hing()
                > foo.append(a)
                > qux.append(b)[/color]

                the original code looks like yours with the inner loop unrolled - a classic
                measure for performance improvement.

                $ python -m timeit -s'ext = [].extend, [].extend' -s'def calc(): return (),
                ()' 'for ix, i in zip(ext, calc()): ix(i)'
                100000 loops, best of 3: 2.79 usec per loop

                $ python -m timeit -s'ax = [].extend; bx = [].extend' -s'def calc(): return
                (), ()' 'a, b = calc(); ax(a); bx(b)'
                1000000 loops, best of 3: 0.975 usec per loop

                (I used extend instead of append() to avoid the effects of memory hogging)

                I find the faster code more readable than yours - and Lonnie's cool hack -
                so I'd leave it at that.

                Peter

                Comment

                Working...