Christopher Barker writes:
But again, not worth any more of my time that I have already spent.
Feel free not to read or respond. (I mean that literally, I don't want to take up more of your time just because I responded. I'd be interested in your response if you care to make one. I do think that there are some general principles of software design here, and also some issues of how they should be applied to the Python language.)
if they required something set like, they would require something in set that was not in all iterables.
That's the opposite of duck-typing, though. Duck-typing is about *sufficient* conditions for treating a object as an instance of the *argument's* intended type. Whether a object is sufficiently set-like depends on what you're going to do with the object, not on all the irrelevant attributes that actual sets have.
Do we need to provide near duplicate methods that coerce iterables to set?
I don't think anyone is asking fort that
I think I've failed to convey my meaning, because the whole point is that the named methods are the same operations as the dunders except that they also allow general iterables, which is possible by forgetting that iterables have an order (and then reimposing an implementation-dependent order) and forgetting that they may contain duplicate values. It's the loss of the two latter characteristics that I mean by "coerce to set".
But here's a (n abstract) use case:
def some_fun(a_set, some_args): ... a_set.union(something_iterable) ...
Of course it's easy to construct hypotheticals which fail if people decide to apply them to views instead of builtin sets. My question is whether the convenience of these functions is genuine, or whether it's an attractive nuisance. My argument here is like the claim the loose behavior of builtin zip with respect to unequal-length iterables is an attractive nuisance. As I see it, the language design questions are: (1) "Does consenting adults apply here?" (2) "Is the convenience that great?" I had a couple more that have to do with issues of implementing the non-dunder methods on all classes that derive from Set, but I'm pretty sure those are solved by providing generic concrete methods in the Set ABC like def union(self, it): return self.__or__(set(it))
So then I need to either wrap something_Iterable in set(), and use the | operator,
Which is what I would do: x.union(y) x | set(y) I agree that ".union(y)" may be easier for many people to type (you don't have to do a mental gear change to an operator notation which is different from conventional set theory notation), but as you can see the number of characters is the same. I don't see a huge cost to this as people can get used to the explicit conversion, and for other set operations the operator versions are shorter. The counterargument is that as long as builtin set supports the named versions, people will use them and expect them on all Sets. Now that I realize the Set ABC can provide all the non-dunder methods in reasonably efficient generic implementations, that argument is a lot stronger than I thought. But I'm still enough of a curmudgeon to think it would be a better idea to use explicit conversion and the operator forms. ;-) (Don't argue back, I know that's not going to convince anyone. :-)
I guess I don't like the inconsistency between the "prototype" builtin and the ABC -- seems strange to me.
I don't either. I'm just willing to bear it in the face of "Explicit is better than implicit" and "In the face of ambiguity, refuse the temptation to guess" (and for backward compatibility's sake on the builtin set). In this case, is someSet.union(someList) what I really want, or is it someList.extend(someSet)? If you want to argue "look, .extend also takes any Iterable including sets that are arbitrarily ordered, I think they're symmetrical", then I disagree but I'm not going to convince you.[1]
And there is nothing in the docs that discourages use of the methods in favor of the operators. In fact, there is a section that describes why the methods are there, and how they can be useful.
Are you referring to Note, the non-operator versions of union(), intersection(), difference(), and symmetric_difference(), issubset(), and issuperset() methods will accept any iterable as an argument. In contrast, their operator based counterparts require their arguments to be sets. This precludes error-prone constructions like set('abc') & 'cbs' in favor of the more readable set('abc').intersection('cbs'). I don't see anything there about why they're useful, only that if you want to intersect with something not a set (which is mathematically undefined! :-), they're more readable. And, of course "set('abc') & set('cbs')" is *shorter*. The tutorial doesn't mention the non-operator versions at all in the obvious place (section 5.4). Footnotes: [1] FWIW, the former destroys information about order and duplicity, while the latter preserves duplicity while introducing some arbitrariness into the order, so that IMHO the former is dangerous in a way the latter isn't. YMMV.