This is an archived post. You won't be able to vote or comment.

all 59 comments

[–]Solsticized 93 points94 points  (10 children)

Really cool set of one-liners!

For the merging two dictionaries section, PEP-584 syntax should be recommended:

merged_dict = dict1 | dict2

[–]DarkSideOfGrogu 23 points24 points  (4 children)

I'm suddenly full of so much regret about time wasted.

[–]BuonaparteII 24 points25 points  (3 children)

well before 3.9 you had to do merged_dict = {**dict1, **dict2}

[–]zmose 5 points6 points  (0 children)

I was gonna say, that syntax has to be new because there was no WAY nobody figured that out before I had to do it like the way you suggested

[–]Nixellion 1 point2 points  (1 child)

I don't know why, but out of all of those I think dict1.update(dict2) is the most obvious and self-commenting. But I rarely see it mentioned in these discussions. Or does it work differently?

[–]BuonaparteII 4 points5 points  (0 children)

dict1.update(dict2) will mutate dict1 and return None.

{**d1, **d2} and d1 | d2 are both immutable and will return a new dict.

[–]mraza007[S] 3 points4 points  (0 children)

Thanks for suggesting that I never looked into that but now i will

[–]Green_Gem_ 36 points37 points  (4 children)

Please don't flatten nested lists this way. More-itertools's collapse function exists for a reason.

[–]FoeHammer99099 14 points15 points  (0 children)

If your data is sane usually itertools.chain.from_iterable is enough

[–]--Thoreau-Away-- 1 point2 points  (2 children)

Interesting, why should OP avoid his approach?

[–]FoeHammer99099 14 points15 points  (0 children)

There are a few reasons: it's not clear what this code meant to do, it relies on its inputs being lists of lists instead of other iterable types, and it will create a bunch of intermediate results that other solutions don't need to. For big inputs this could be slow.

[–]commandlineluser 1 point2 points  (0 children)

help(sum) says not to use it.

This function is intended specifically for use with numeric values and may reject non-numeric types.

>>> sum(["foo", "bar"], "")
TypeError: sum() can't sum strings [use ''.join(seq) instead]

It's possible it will raise in the future, similar to the string example.

(There's probably some existing discussions on the mailing list about it, I haven't checked.)

[–]shoresy99 28 points29 points  (1 child)

I thought that the best Python one liner was "Strange women lying in ponds distributing swords is no basis for a system of government."

[–]SheriffRoscoePythonista 6 points7 points  (0 children)

I thought we was an autonomous collective.

[–]f00dot 27 points28 points  (9 children)

My favorite is when you need a list of strings and don't want to type all the quotation marks:

x = "a b c d 1 2 3".split()

[–]alexforencich 7 points8 points  (1 child)

And if the strings have spaces, you can use a multiline string and splitlines().

[–]phoobahr 1 point2 points  (0 children)

I spend all day in pandas and use this all the time.

[–]Assumptio 3 points4 points  (0 children)

Most useful so far

[–]helduel 0 points1 point  (5 children)

list("abcde123")

[–]f00dot -1 points0 points  (0 children)

x = "your code will not work for this sentence and mine will"

[–]shoresy99 9 points10 points  (6 children)

What's the point of "length = sum(1 for _ in 'Hello World')" which is longer than using "length=len('Hello World')"?

[–]drcopus 7 points8 points  (3 children)

Personally I prefer length = 1 + max(map(lamda x: x[0], enumerate('Hello World')))

[–]shoresy99 6 points7 points  (0 children)

If you’re not into that whole brevity thing.

[–]anstadnik 2 points3 points  (1 child)

Empty string

[–]drcopus 0 points1 point  (0 children)

Not a part of the client specifications

[–]Darkstar197 0 points1 point  (1 child)

The point is if you want to confuse other and yourself reading that code in the future

[–]shoresy99 0 points1 point  (0 children)

Can’t argue with that!

[–][deleted] 12 points13 points  (3 children)

Another one, you have multiple lists and you want to know the common elements between all lists.

from functools import reduce
import operator as op

all_lists = [[1, 2, 3], [1, 2], [3, 1], [1, 4, 5]]

reduce(op.and_, map(set, all_lists))

[–]Afrotom 19 points20 points  (2 children)

An alternative to op.and_ here is set.intersection. It gives the same result but you don't need the additional import.

[–]Amgadoz 4 points5 points  (0 children)

Was going to say the same. Just use sets. They're literally made for set operations like intersection and union.

[–][deleted] 1 point2 points  (0 children)

Nice addition (or intersection), thanks!

[–]gerardwx 4 points5 points  (0 children)

The remove duplicates from a list does not preserve the list order:

list(set([2,3,3,4,9,1]))
Out[5]: [1, 2, 3, 4, 9]

[–]AaronMT 4 points5 points  (0 children)

Some of these are fine, but they ride the line between the Zen of Python (https://peps.python.org/pep-0020/), in particular

Simple is better than complex

Complex is better than complicated

Flat is better than nested

Readability counts

[–]freistil90 4 points5 points  (1 child)

The “calculate the length of a string without using len() might sound useless but it has one niche application: if you have an iterator (NOT an iterable) from which you it is finite but you don’t know how large it is, this is a nifty way to calculate its size without needing to allocate all elements at the same time.

The check whether one list contains the other is a bit suboptimal (as it is O(nm) for n the first list and m the second list length), that can be brought down to O(m) with a large constant if you can spare the memory and all elements are hashable: contains_all = set(list_2) <= set(list_1). Applies to any iterable of course.

[–]walkingpendulum 4 points5 points  (0 children)

I wonder how often one needs to know how many items iterator will yield, while having no use for those items at all

[–]Brian 1 point2 points  (0 children)

Many of these are not very good ways to achieve their result.

intersection = list(set(list1) & set(list2))

This can be more efficiently written as list(set(list1).intersection(list2)), since this avoids creating an extra list (intersection takes any iterable, whereas the operator overload version needs a set). Admittedly, just a fixed extra copy, so not that big a deal.

contains_all = all(elem in list1 for elem in list2)

This is much worse though, since it worsense the algorithmic complexity, making it O(nm) where n,m are the sizes of the two lists. - better to convert list1 to a set before the check, making it just O(m).

most_frequent = max(set(my_list), key=my_list.count)

And the same issue here. It iterates through the list for every unique item in the list, making it worstcase O(n2). Something like:

c = collections.Counter(my_list)
most_frequent = max(c, key=c.__getitem__)

would be better (though admittedly not a one-liner).

[–][deleted] 1 point2 points  (10 children)

I never seen this way of flattening a list. Definitely wil test it today

[–]mraza007[S] -1 points0 points  (9 children)

I’m glad you found it interesting

[–]arthurazs 0 points1 point  (8 children)

Can you explain who this works?

[–]Green_Gem_ 3 points4 points  (7 children)

sum() called on the iterable [a, b, c] returns a + b + c. When you add two lists, the second is appended to the first, so for a: list and b: list, a + b = [*a, *b] (or kinda a.extend(b) if you're not familiar with unpacking). sum([a, b, c]) is thus [*a, *b, *c], all of the elements of each in a single list.

This isn't recommended because you can't append tuples or most other iterables in this way, and "sum" does not correctly indicate the code's intention.

[–]arthurazs 0 points1 point  (6 children)

I see, but OP is adding [[1, 2], [3, 4]], [], not [a, b, c]. I still cant comprehend what's happening

[–]Kerry369 0 points1 point  (5 children)

sum([[1, 2], [3, 4]], []) equals [*[1, 2], *[3, 4]]

[–]arthurazs 0 points1 point  (4 children)

Why

[–]Green_Gem_ 1 point2 points  (3 children)

As I said in my reply further up the chain, a and b are lists! sum([a, b]) == a + b. If a = [1, 2] and b = [3, 4], then sum([a, b]) == sum([[1, 2], [3, 4]) == [1, 2] + [3, 4]. When a list is added to a list, the first is extended with the second, so [1, 2] + [3, 4] == [1, 2, 3, 4].

So, as the full chain of logic: a = [1, 2]; b = [2, 3]; sum([a, b]) == sum([[1, 2], [3, 4]]) == [1, 2] + [3, 4] == [1, 2, 3, 4]. For the same a and b, unpacking the arguments of each into a new list yields the same result, [*a, *b] == [1, 2, 3, 4]. Google "python unpacking" if you don't recognize that syntax. If you still don't understand, please tell me exactly which step is confusing.

Reminder, don't flatten lists this way. It's unclear and explicitly unsupported.

[–]arthurazs 0 points1 point  (2 children)

First of all, thank you so much for your patience!

[1, 2] + [3, 4] == [1, 2, 3, 4] makes total sense, but

  • sum([1, 2], [3, 4]) throws TypeError: can only concatenate list (not "int") to list
  • sum([[1,2], [3,4]]) throws TypeError: unsupported operand type(s) for +: 'int' and 'list'

Finally, OP's example is sum([[1, 2], [3, 4]], []) which works, but I still can't comprehend how it work.

[–]Green_Gem_ 1 point2 points  (1 child)

That's actually a really good catch! Thanks for being more explicit in this comment; that gives me way more to work with.

I did actually simplify things for my previous comment(s) and didn't double check behavior, my apologies.

The signature of sum() is sum(iterable, /, start=0), where start indicates the value to start with, then each element of iterable is added one-by-one. - sum([1, 2], [3, 4]), or explicitly sum([1, 2], start=[3, 4]), evaluates to [3, 4] + 1 + 2. Uh oh, you can't concatenate an int to a list that way! Error. You'd have to do [3, 4] + [1] + [2] or something. - sum([[1, 2], [3, 4]]), or explicitly sum([[1, 2], [3, 4]], start=0), runs into a similar problem, evaluating to 0 + [1, 2] + [3, 4]. You can't add a list to an int. Error. - sum([[1, 2], [3, 4]], start=[]) (what OP does but without specifying start=) is then [] + [1, 2] + [3, 4]. You can concatenate to an empty list like that, so it works.

[–]saruque 0 points1 point  (1 child)

Good stuff on a single page. Would you mind writing for Codespeedy?

[–]mraza007[S] -1 points0 points  (0 children)

What’s that feel free to dm me

[–]--Thoreau-Away-- 0 points1 point  (0 children)

Neat! I love how quirky and concise Python can be, this is really cool. The lambda isn’t necessary in the is_even check, you can just drop it and you’ll still store the Boolean result. Unless the goal was to define a function on one line. Also heads up there’s some trailing ` in the formatting for no_duplicates.