9 ms·
Python idiom for taking the single item from a list
- teilo 16y agoExcellent. That one belongs in any Python style guide. Though technically it's not a style, it does lead to better readability, and reduces the propensity for unforseen consequences.
- jemfinch 16y agoThe reason it's not in the Python style guide is because it's a symptom of other problems in code. Lists are for holding multiple values of the same type. If you know that a list will always have one and only one value, it's not conceptually a list, it's some other type that's been encoded into a list for some reason, and you should fix that conceptual mismatch rather than papering over the issue with a style idiom.
- cturner 16y agoWhat you say sounds right, but it's not. Using some DB APIs you'll get back a list of results, and you'll know from the SQL that it will only have one result. Another example is if you know there is a single item in a data structure, and use list comprehension to extract it. You'll end up with a list of one item. These situations happen, and for good reasons.
- dochtman 16y agoWe also commonly use tuples as light-weight types (a kind of heterogeneous collection).
- njharman 16y agoLists use (not infrequently) logic like the following; If one element do this one element thing else do this more than one element thing
- iron_ball 16y agoBut in that case, either your code is evidently in a branch where the list will have one item -- as in your example -- in which case the context makes it clear. And if there's no context, such as in a nested function call, then at some point you should be passing that lone list item into a function which takes the item as an argument.
- teilo 16y agoThat sounds logical enough, except for two things: First: You are often working with someone-else's library which for various reasons you cannot change. Second: It is not uncommon to use a standard method that may well be able to return multiple items, but in your use case it should only return one. Case in point: a database call that returns the result of a query.
- baddox 16y agoDjango's ORM, the only Python one I've experience with, provides get() for those cases.
- StavrosK 16y agoVery good idea, I support this and will use it from now on (even though I don't like the (thing,) Python syntax very much), as it's the only way to say that the iterable should only have one element.
- tkeller 16y agoAs a commenter there noted, [thing] = stuff works too. I think I like that even better.
- StavrosK 16y agoOh, you're very much correct. Let me benchmark that... There doesn't appear to be any difference in speed between the two. Assigning the result to one variable (totally different to this) is about 6% faster, so this syntax is what I'll use, thank you!
- jrockway 16y agoAssigning the result to one variable (totally different to this) is about 6% faster, so this syntax is what I'll use, thank you! On your exact machine with your exact version of Python, today. Please don't let microbenchmarks dictate what code you write. If you need a 6% performance gain in a microbenchmark, you've chosen the wrong language to use. Python is about readability and maintainability, not syntax hacks to make some benchmark slightly faster.
- StavrosK 16y agoThe point of the above post was to say "this is nice syntax, and it isn't ten times slower, so it's good on that front too", not to say "use that because it's 6% faster". I'm never going to write this in a tight loop anyway, and even if I did, I'd benchmark the entire piece of code, not just this line.
- mikeklaas 16y agoThe brackets are optional; you can also write thing, = [stuff] though some people think that it's less legible since it has a completely different meaning without the comma. EDIT: you can even play with the whitespace and pretend that ",=" is a list->scalar conversion operator: x ,= [1]
- yangyang 16y agothing, = stuff works too.
- c-oreills 16y agoI think the comma's a lot easier to miss compared to (thing,) though.
- statictype 16y agoSo we have so if I was wrong in my original assumption that stuff has exactly one element, Python will shout at me before this will manifest itself as a hard-to-find bug someplace else in the program. and then later on, This method works even when stuff is a set or any other kind of collection. stuff[0] wouldn’t work on a set because set doesn’t support access by index number. The second argument is basically in favor of duck-typing which is the pythonic way of writing code, ie, not caring about the actual object but only if it responds to the given message. But what about the first argument? Couldn't you make the case that the pythonic way of handling it is to only care about if the object responds to __getitem__(0)? Which idiom you use would depend entirely on context, wouldn't it?
- cool-RR 16y agoDuck-typing means you don't assume that the object is from a certain type; Instead you assume that it's got the interfaces you need. (Either `.__iter__()` or `.__getitem__(0)` in this case.) `.__iter__()` is a more general and common interface than `.__getitem__(0)`, so it's preferable to assume that the object implements the former rather than the latter. But sometimes you do want to assume stuff about your object. In this case we want to assume that the list has exactly one item, and we want our code to break immediately if it isn't true.
- masklinn 16y ago> But what about the first argument? Couldn't you make the case that the pythonic way of handling it is to only care about if the object responds to __getitem__(0)? Mmm no? Here, what he cares about is that it's a single-element collection, he doesn't just want the collection's first item. If he did, foo[0] would do a better job. So the object responding to __getitem__(0) is not a sufficient condition. In fact, it's entirely wrong as sets do _not_ implement __getitem__. Worse, __getitem__(0) does a very different thing than unpacking on a dict. Unpacking is more ducky than __getitem__ for this case, because it expresses the following: the right-hand is a single-element iterable. Any iterable will work as long as it only has a single item, it doesn't have to be a sequence, a mapping, or even a collection (generators or callable_iterators will work just as well)
- digitallogic 16y ago
- Goladus 16y agoMy problem is that exact situation almost never comes up. Usually, I want to use the item in the list directly in an expression, not assign it to another variable first. For example: if 1==len(lst): return lst[0] else: return reduce(fn, lst) I don't really want to have to do: if 1==len(lst): (single,)=lst return single else: return reduce(fn, lst)
- cool-RR 16y agoI see what you mean. In this case it's indeed questionable whether this idiom would improve the code. The only improvement I can offer for this code is to fix the Yoda conditions :)
- masklinn 16y agoThat's very different than OP's piece of code: you're not trying to assert that the iterable yields a single element, you already know it. Furthermore, OP's assertion-unpacking will work not just on lists, but also on dicts (will return the only key, not the only value, and the key doesn't have to be ``0``), on sets (which don't implement __getitem__ at all), on arbitrary collections and even on arbitrary iterables (including callable_iterator and generators) Also, please don't put the constant on the left-hand of a comparison in Python, it's useless and ugly.
- Goladus 16y agoI agree it's cool that it works on lists and sets and dictionary keys. Upvoted, etc. Just noting that there are many situations where it won't replace the [0]. Also, I don't worry about order on simple equality expressions unless someone asks me to do it a certain way. That's a religious argument and a complete waste of time for me.
- jedbrown 16y agoreturn reduce(fn, lst) does the same thing.
- agentultra 16y agoSingle-element tuple unpacking? I've never found a case where I knew there would only be one element in a list, but I do use tuple unpacking all of the time. Pretty cool I guess; didn't know it wasn't a common idiom. Good to know. Thanks for sharing. :)
- marcinw 16y agohttp://docs.python.org/library/struct.html#struct.unpack http://docs.python.org/library/struct.html#struct.unpack """The result is a tuple even if it contains exactly one item.""" I run into this constantly using struct.unpack(), and I find the author's idiom to be ideal way to handle this. I will use this from now on.
- zokier 16y agoI think this style has one great drawback: it requires deeper knowledge of Python then the 'usual' thing = stuff[0].
- cool-RR 16y agoTherefore this post, so people will have this knowledge.
- jfager 16y agoIf someone's knowledge of Python is so shallow that they can't handle tuple unpacking, they should learn more Python. It's one of the basic foundations of the language, and it's hardly a difficult concept.
- masklinn 16y ago> If someone's knowledge of Python is so shallow that they can't handle tuple unpacking Did you know that "tuple unpacking" actually works on arbitrary iterables (in fact, the relevant function in Python's C code is called ``unpack_iterable``)? >>> def f(): yield 3 ... >>> b, = f() >>> b 3 you should be careful with such declarations.
- jfager 16y agoYes. Why is that surprising? f is a generator, generators are iterables, iterables can be unpacked. I've always thought that the tuple referred to by 'tuple unpacking' was the target containing the variables being assigned, rather than the thing being unpacked, because you frequently unpack things other than tuples.
- masklinn 16y agoYour comment is interesting at a number of levels. And by interesting, I mean that I can hardly fathom you wrote it: > Yes. Why is that surprising? because you called it "tuple unpacking". Furthermore, because in functional languages where this feature comes from you actually unpack tuples, and pattern-matching of other structures is not called "tuple unpacking". > I've always thought that the tuple referred to by 'tuple unpacking' was the target containing the variables being assigned That's what the "tuple" is unpacked into, it makes no sense that the name of the pattern would come from there. At best and stretching it, you'd have "into-a-tuple unpacking". Furthermore, you're not actually unpacking into a tuple (even less so in Python 3, `(1, * a)` isn't a valid expression... anywhere that I know of), you're unpacking into free variables. That's the point of unpacking. > because you frequently unpack things other than tuples. The only other thing frequently unpacked is a list, and Python's tuples are immutable lists, it does not stretch the imagination that a read-only feature of tuples would work on their mutable cousin as well. As for other structures, in 6 years of Python I'd say I've seen unpacking of arbitrary iterators thrice at best. You do not frequently unpack dicts or iterators. edit: fucking hell, yc's comment format sucks donkey balls.
- rbanffy 16y agoI may not be Dutch enough, but this certainly isn't the obvious way to do it.
- alrex021 16y agoThere you have it folks, Python supports destructuring assignment. :-)
- jerf 16y agoWhat's the smiley for? Python 2.6.5... Type "help", "copyright", "credits" or "license" for more information. >>> (a, (b, c, (d, e))) = (1, (2, 3, (4, 5))) >>> a, b, c, d, e (1, 2, 3, 4, 5) >>> (a, (b, c, (d, e))) = (1, (2, 3, (4, 5, 6))) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: too many values to unpack It isn't quite as flexible as functional languages and it's not as idiomatic as it is in functional languages, but it's not a hack or quirky edge-case either.
- deleted 16y ago[deleted]
- torrentabuse 16y agoProposed style guide: When you want to get the (n+1)th item from a list, do: item = stuff[n] To get the first item, do: item = stuff[0] Unless the list has one element, then do: (item, ) = stuff At least you'll never be accused of consistency.
- StavrosK 16y agoNot really. The first two are consistent, but the last one is "get the only item and assert that there is only one item to get. If you don't care about the assertion, use [0], as always. I like the latter, because there's added info for the reader ("this should return a one-item iterable"). It's not really inconsistent, because you aren't performing the same operation in the two cases.
- spoondan 16y agoI'm annoyed by the belief that consistency is ipso facto good. Consistency is a tool for attacking a particular type of problem. Consistency for its own sake will often make things worse. I see this fallacious reasoning all the time in design critiques: this is inconsistent with that. Well, yes, but so what? Why is consistency desirable in this instance?
- jemfinch 16y agoThe real solution is not to arbitrarily encode your types as lists of exactly one item. If you find yourself passing around such lists with enough regularity that you feel the need to develop an idiom for deconstructing them, you're doing something wrong.
- cool-RR 16y agoMore often than not you are receiving a list from some function, not constructing it manually. As an example: Say you have a GUI widget that can contain many entries, and you call `widget.get_entries()` which returns a list with all the entries. But if you know there must be only one entry, you can do `(entry,) = widget.get_entries()`.
- jemfinch 16y agoThen it should make a method which returns its known, sole entry. Returning a list of entries from a widget which will always contain only one entry is the logical equivalent to converting a function return value to a string and then expecting the client to convert it back from a string. That is to say, it's necessary in a general case (e.g., a Widget super class) but should not be exposed that way in some specific cases (e.g., your subclass wherein you know there will always be one entry). In reality, you shouldn't be mucking with the entries of a widget at all; you should tell the widget what to do and it should adjust its entries as necessary). Demeter's law and all.
- j_baker 16y agoI notice a lot of "shoulds" in this comment. Unfortunately, what should be and what is aren't always the same thing.
- jemfinch 16y agoNo doubt! But since we're on the subject of "how to make code like this better" it seems appropriate to discuss what's really wrong with the code, rather than just what sort of duct tape style guidelines we can use to patch over its flaws :)
- guns 16y agoSeems he would have more fun being a Rubyist.
- msbarnett 16y agoFunny, I was just thinking how much cleaner a supposedly-horrid monkey-patch would make his example in Ruby. thing = some_dict[my_object.get_foobar_handler()].get_only_element() self-documents the purpose of the operation and its assumption much more legibly than the typo-esque (thing,) = ... in my opinion.
- guns 16y agoWell, that's another way to do it. ;)
- DougBTX 16y agoI prefer array.single(), I think it's a rare enough use-case that spelling out what you are doing is worthwhile.
- j_baker 16y agoAre you talking about another language? 1) In python, arrays and lists are two very different things. 2) Neither the array module nor the array class have a single function.
- DougBTX 16y agoThe example came to mind via C#. I assumed that it could be added to Python via a mixin or whatever if you wanted to do it there.
- krosaen 16y ago[thing] = stuff also works and is more readable IMHO.
- j_baker 16y agoPersonally, I think this is a bit on the "clever" side. Plus, the error message you get isn't as easy to understand as if you used an assert statement. I'd probably just do something like this: def get_single(l): assert l and len(l) == 1 return l[0] Then you get the best of both worlds: readability and a concise one-liner.
- cool-RR 16y agoI'd avoid doing this, because people who'll read your code would have to check themselves what the `get_single` function does. Also, the performance here is probably much worse. (Although in many cases it would not matter.) But that's just my opinion, your suggestion is legitimate.
- jemfinch 16y ago> I'd avoid doing this, because people who'll read your code would have to check themselves what the `get_single` function does. This is an argument against using functions at all. If it works against get_single, it works against all functions. That is to say, it doesn't work at all.
- cool-RR 16y agoYes, but with most functions there's no choice, because most of the functions in your program do something (a) complex and/or (b) specific to your program. This `get_single` function falls into neither of these categories.
- jemfinch 16y agoFunctions should not be complex. Functions should be simple, and they should be composed by other simple functions to do more complex things.
- Scriptor 16y agoIt is certainly okay for some functions to be complex. Some may rely on simpler functions, but the one you'd use can still do a complex task in general. A function that squares a number is simple, one that computes the standard deviation is definitely more complex.
- necubi 16y agoThe same code works in ruby, though without the assertion behavior: > a = [0] > b, = a > b == 0 => true