How to get an item from a simple set?

Discussion in 'Python' started by Pete Forman, Nov 24, 2004.

  1. Pete Forman

    Pete Forman Guest

    I have a set that contains one item. What is the best way of getting
    at that item? Using pop() empties the set. Here is what I've tried.

    Python 2.3.4 (#1, Jun 13 2004, 11:21:03)
    [GCC 3.3.1 (cygming special)] on cygwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from sets import Set
    >>> s = Set(['foo'])
    >>> s.copy().pop()

    'foo'
    >>> [x for x in s][0]

    'foo'
    >>> s[0]

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    TypeError: unindexable object

    --
    Pete Forman -./\.- Disclaimer: This post is originated
    WesternGeco -./\.- by myself and does not represent
    -./\.- opinion of Schlumberger, Baker
    http://petef.port5.com -./\.- Hughes or their divisions.
     
    Pete Forman, Nov 24, 2004
    #1
    1. Advertising

  2. Pete Forman wrote:
    > I have a set that contains one item. What is the best way of getting
    > at that item? Using pop() empties the set. Here is what I've tried.


    This is what tuple unpacking is for:

    >>> s = set(['foo'])
    >>> item, = s
    >>> item

    'foo'
    >>> [item] = s
    >>> item

    'foo'

    It's up to you whether you like the tuple or list syntax better. =)

    Steve
     
    Steven Bethard, Nov 24, 2004
    #2
    1. Advertising

  3. Pete> I have a set that contains one item. What is the best way of
    Pete> getting at that item? Using pop() empties the set.

    Do you want to enumerate all the items in the set? If so:

    for elt in s:
    print elt

    If you just want to grab one arbitrary (though not random) item from the
    set, try:

    elt = iter(s).next()

    Note that repeating this operation will always return the same item:

    >>> s

    set(['jkl', 'foo', 'abc', 'def', 'ghi'])
    >>> iter(s).next()

    'jkl'
    >>> iter(s).next()

    'jkl'
    >>> iter(s).next()

    'jkl'
    >>> iter(s).next()

    'jkl'

    Skip
     
    Skip Montanaro, Nov 24, 2004
    #3
  4. Pete Forman

    Pete Forman Guest

    Skip Montanaro <> writes:

    > Pete> I have a set that contains one item. What is the best way of
    > Pete> getting at that item? Using pop() empties the set.
    >
    > If you just want to grab one arbitrary (though not random) item from the
    > set, try:
    >
    > elt = iter(s).next()


    I actually wanted to append the single item to a string, Steven's
    solutions work for assignment.

    So this looks like my best bet. I'll probably use join instead of +=
    in my code.

    >>> line = 'bar '
    >>> line += iter(s).next()
    >>> line

    'bar foo'

    --
    Pete Forman -./\.- Disclaimer: This post is originated
    WesternGeco -./\.- by myself and does not represent
    -./\.- opinion of Schlumberger, Baker
    http://petef.port5.com -./\.- Hughes or their divisions.
     
    Pete Forman, Nov 24, 2004
    #4
  5. Pete Forman wrote:
    > I actually wanted to append the single item to a string, Steven's
    > solutions work for assignment.

    [snip]
    >>>>line = 'bar '
    >>>>line += iter(s).next()
    >>>>line

    > 'bar foo'


    Yeah, using the assignment's an extra line:

    >>> line_list = ['bar ']
    >>> item, = s
    >>> line_list.append(item)
    >>> ''.join(line_list)

    'bar foo'

    I still tend to write the extra line in cases like this -- it guarantees
    that the set is really the size that I think it is, where the
    iter(s).next() solution will not raise an exception if the set is
    actually larger.

    Steve
     
    Steven Bethard, Nov 24, 2004
    #5
  6. Pete Forman

    Pete Forman Guest

    Steven Bethard <> writes:

    > I still tend to write the extra line in cases like this -- it
    > guarantees that the set is really the size that I think it is, where
    > the iter(s).next() solution will not raise an exception if the set
    > is actually larger.


    The preceding line in my code is
    if len(s) == 1:

    :)
    --
    Pete Forman -./\.- Disclaimer: This post is originated
    WesternGeco -./\.- by myself and does not represent
    -./\.- opinion of Schlumberger, Baker
    http://petef.port5.com -./\.- Hughes or their divisions.
     
    Pete Forman, Nov 24, 2004
    #6
  7. On Wed, 24 Nov 2004 09:46:50 -0600, Skip Montanaro <> wrote:

    >
    > Pete> I have a set that contains one item. What is the best way of
    > Pete> getting at that item? Using pop() empties the set.
    >
    >Do you want to enumerate all the items in the set? If so:
    >
    > for elt in s:
    > print elt
    >
    >If you just want to grab one arbitrary (though not random) item from the
    >set, try:
    >
    > elt = iter(s).next()
    >
    >Note that repeating this operation will always return the same item:
    >
    > >>> s

    > set(['jkl', 'foo', 'abc', 'def', 'ghi'])
    > >>> iter(s).next()

    > 'jkl'
    > >>> iter(s).next()

    > 'jkl'
    > >>> iter(s).next()

    > 'jkl'
    > >>> iter(s).next()

    > 'jkl'
    >

    Lest someone else not realize that the operation you are repeating includes creating
    a fresh initialized iterator each time, and you're just doing it as a way to grab one element:

    >>> s = set(['jkl', 'foo', 'abc', 'def', 'ghi'])
    >>> it = iter(s)
    >>> it.next()

    'jkl'
    >>> it.next()

    'foo'
    >>> it.next()

    'abc'
    >>> it.next()

    'def'
    >>> it.next()

    'ghi'
    >>> it.next()

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    StopIteration

    Regards,
    Bengt Richter
     
    Bengt Richter, Nov 24, 2004
    #7
  8. On Wed, 24 Nov 2004 15:40:30 GMT, Steven Bethard <> wrote:

    >Pete Forman wrote:
    >> I have a set that contains one item. What is the best way of getting
    >> at that item? Using pop() empties the set. Here is what I've tried.

    >
    >This is what tuple unpacking is for:
    >
    > >>> s = set(['foo'])
    > >>> item, = s
    > >>> item

    >'foo'
    > >>> [item] = s
    > >>> item

    >'foo'
    >
    >It's up to you whether you like the tuple or list syntax better. =)
    >

    Thanks. I didn't realize a list format could be used to specify target names
    like that. My intial reaction is a bendy feeling in my list expression syntax
    recognizer, though. I'm not sure I like that on the left hand side.
    It feels too much like __setitem__ on some implied object. The tuple syntax
    on the left hand side is only for unpacking (unless you want to imagine invoking
    an implied unnamed function, but that's a stretch IMO), so it doesn't trigger
    that near-miss syntax recognition feeling.

    Regards,
    Bengt Richter
     
    Bengt Richter, Nov 24, 2004
    #8
  9. Pete Forman wrote:
    > Steven Bethard <> writes:
    >
    >
    >>I still tend to write the extra line in cases like this -- it
    >>guarantees that the set is really the size that I think it is, where
    >>the iter(s).next() solution will not raise an exception if the set
    >>is actually larger.

    >
    >
    > The preceding line in my code is
    > if len(s) == 1:
    >


    So is this just one branch of a case statement? What do you do in the
    case that len(s) != 1? And which one happens more often?

    If I have two possible unpackings of an iterable and I know one is much
    more common than the other, I often do something like:

    try:
    x, y = s # more common unpacking
    except ValueError:
    [x], y = s, None # less common ('exceptional') unpacking

    This is a reasonable pattern if your code really does favor one branch
    substantially over the other. But dont' take my word for it. ;) Here's
    what timeit says:

    ----- test.py ----
    def test_cond(*args):
    if len(args) == 1:
    [x], y = args, None
    elif len(args) == 2:
    x, y = args
    else:
    raise ValueError('wrong number of arguments')

    def test_try(*args):
    try:
    x, y = args
    except ValueError:
    [x], y = args, None

    def test(fn, single_times, double_times):
    for _ in range(single_times):
    fn(1)
    for _ in range(double_times):
    fn(0, 1)


    ---- command prompt ----
    >python -m timeit -s "import test" "test.test(test.test_cond, 10, 10)"

    10000 loops, best of 3: 26.7 usec per loop

    >python -m timeit -s "import test" "test.test(test.test_try, 10, 10)"

    10000 loops, best of 3: 116 usec per loop

    >python -m timeit -s "import test" "test.test(test.test_cond, 1, 100)"

    10000 loops, best of 3: 132 usec per loop

    >python -m timeit -s "import test" "test.test(test.test_try, 1, 100)"

    10000 loops, best of 3: 99.8 usec per loop


    As you can see, when the try/except block is slower when the two
    branches get traversed approximately equally, but faster when one branch
    is substantially favored over the other.

    Steve
     
    Steven Bethard, Nov 24, 2004
    #9
  10. Bengt Richter wrote:
    > On Wed, 24 Nov 2004 15:40:30 GMT, Steven Bethard <> wrote:
    >>
    >>>>>[item] = s
    >>>>>item

    >>
    >>'foo'
    >>
    >>It's up to you whether you like the tuple or list syntax better. =)
    >>

    >
    > Thanks. I didn't realize a list format could be used to specify target names
    > like that. My intial reaction is a bendy feeling in my list expression syntax
    > recognizer, though. I'm not sure I like that on the left hand side.
    > It feels too much like __setitem__ on some implied object. The tuple syntax
    > on the left hand side is only for unpacking (unless you want to imagine invoking
    > an implied unnamed function, but that's a stretch IMO), so it doesn't trigger
    > that near-miss syntax recognition feeling.


    Yeah, I almost always prefer the tuple (comma) syntax, but occasionally
    I find the list syntax clearer, if, for example, I'm unpacking a nested
    single-item list:

    >>> t

    [['abcd'], 1, 2]
    >>> (x,), y, z = t
    >>> x, y, z

    ('abcd', 1, 2)

    The ,), in the tuple-only unpacking makes me uncomfortable for some
    reason. I feel marginally more comfortable with:

    >>> [x], y, z = t
    >>> x, y, z

    ('abcd', 1, 2)

    Of course, I generally feel uncomfortable if I have a weird unpacking
    thing like this anyway. It pretty much only comes up for me when I want
    to assign some default values in one branch of a try/except or if/else
    statement, e.g.

    try:
    x, y = s
    except ValueError:
    [x], y = s, None

    Steve
     
    Steven Bethard, Nov 24, 2004
    #10
  11. Pete Forman

    Pete Forman Guest

    Steven Bethard <> writes:

    > So is this just one branch of a case statement? What do you do in
    > the case that len(s) != 1? And which one happens more often?


    My s contains a set of possible values that are being refined. I'm
    printing either a single resolved value or "many" to indicate there is
    still some way to go. len(s) > 1 is not exceptional though len(s) ==
    0 would be. Over the run of the program one and many will happen
    equally often. Performance is less of an issue than clarity of the
    code.
    --
    Pete Forman -./\.- Disclaimer: This post is originated
    WesternGeco -./\.- by myself and does not represent
    -./\.- opinion of Schlumberger, Baker
    http://petef.port5.com -./\.- Hughes or their divisions.
     
    Pete Forman, Nov 26, 2004
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. ssoss

    Display Related Item or Add Item

    ssoss, Sep 17, 2003, in forum: ASP .Net
    Replies:
    2
    Views:
    503
    ssoss
    Sep 19, 2003
  2. Replies:
    2
    Views:
    631
  3. Johannes Zellner
    Replies:
    1
    Views:
    508
    Alex Martelli
    Jan 17, 2006
  4. bilgekhan
    Replies:
    18
    Views:
    498
    bilgekhan
    May 28, 2008
  5. Johannes Bauer

    Get item from set

    Johannes Bauer, Apr 26, 2009, in forum: Python
    Replies:
    11
    Views:
    642
Loading...

Share This Page