views:

63

answers:

3

Is there an article or forum discussion or something somewhere that explains why lists use append/extend but sets and dicts use add/update.

I frequently find myself converting lists into sets and this difference makes that quite tedious so for my personal sanity I'd like to know what the rationalization is.

The need to convert between these occurs regularly as we iterate on development. Over time as the structure of the program morphs various structures gain and lose requirements like ordering and duplicates.

For example something that starts out as an unordered bunch of stuff in a list might pick up the the requirement that there be no duplicates and so need to be converted to a set.

All such changes require finding and changing all places where the relevant structure is added/appended and extended/updated.

So I'm curious to see the original discussion that led to this language choice, unfortunately I've had no luck googling for it.

+1  A: 

set and dict are unordered. "Append" and "extend" conceptually only apply to ordered types.

Ignacio Vazquez-Abrams
I'm fairly sure that when you said add you meant append
tolomea
+1  A: 

It's written that way to annoy you.

Seriously. It's designed so that one can't simply convert one into the other easily. Historically, sets are based off dicts, so the two share naming conventions. While you could easily write a set wrapper to add these methods ...

class ListlikeSet(set):
    def append(self, x):
        self.add(x)

    def extend(self, xs):
        self.update(xs)

... the greater question is why you find yourself converting lists to sets with such regularity. They represent substantially different models of a collection of objects; if you have to convert between the two a lot, it suggests you may not have a very good handle on the conceptual architecture of your program.

Chris B.
+2  A: 

append has a popular definition of "add to the very end", and extend can be read similarly (in the nuance where it means "...beyond a certain point"); sets have no "end", nor any way to specify some "point" within them or "at their boundaries" (because there are no "boundaries"!), so it would be highly misleading to suggest that these operations could be performed.

x.append(y) always increases len(x) by exactly one (whether y was already in list x or not); no such assertion holds for s.add(z) (s's length may increase or stay the same). Moreover, in these snippets, y can have any value (i.e., the append operation never fails [except for the anomalous case in which you've run out of memory]) -- again no such assertion holds about z (which must be hashable, otherwise the add operation fails and raises an exception). Similar differences apply to extend vs update. Using the same name for operations with such drastically different semantics would be very misleading indeed.

it seems pythonic to just use a list on the first pass and deal with the performance on a later iteration

Performance is the least of it! lists support duplicate items, ordering, and any item type -- sets guarantee item uniqueness, have no concept of order, and demand item hashability. There is nothing Pythonic in using a list (plus goofy checks against duplicates, etc) to stand for a set -- performance or not, "say what you mean!" is the Pythonic Way;-). (In languages such as Fortran or C, where all you get as a built-in container type are arrays, you might have to perform such "mental mapping" if you need to avoid using add-on libraries; in Python, there is no such need).

Edit: the OP asserts in a comment that they don't know from the start (e.g.) that duplicates are disallowed in a certain algorithm (strange, but, whatever) -- they're looking for a painless way to make a list into a set once they do discover duplicates are bad there (and, I'll add: order doesn't matter, items are hashable, indexing/slicing unneeded, etc). To get exactly the same effect one would have if Python's sets had "synonyms" for the two methods in question:

class somewhatlistlikeset(set):
    def append(self, x): self.add(x)
    def extend(self, x): self.update(x)

Of course, if the only change is at the set creation (which used to be list creation), the code may be much more challenging to follow, having lost the useful clarity whereby using add vs append allows anybody reading the code to know "locally" whether the object is a set vs a list... but this, too, is part of the "exactly the same effect" above-mentioned!-)

Alex Martelli
Where did I imply I was using "goofy checks" for duplicates? Something picking up the requirement that it can't have duplicates would be a prime example of a time to convert a list into a set.Of course that means I have to find and change every bit of code that touches it. Actually now that you've suggested it adding goofy checks would be easier than finding all the appends and extends.
tolomea
Alex Martelli