0

I'm trying to learn Python, and, while I managed to stumble on the answer to my current problem, I'd like to know how I can better find answers in the future.

My goal was to take a list of strings as input, and return a string whose characters were the union of the characters in the strings, e.g.

unionStrings( ("ab", "bc"))

would return "abc".

I implemented it like this:

def unionStrings( strings ):
     # Input: A list of strings
     # Output: A string that is the (set) union of input strings
    all = set()
    for s in strings:
         all = all.union(set(s))

    return "".join(sorted(list(all)))

I felt the for loop was unnecessary, and searched for more neater, more pythonic(?), improvements .

First question: I stumbled on using the class method set.union(), instead of set1.union(set2). Should I have been able to find this in the standard Python docs? I've not been able to find it there.

So I tried using set.union() like this:

>>> set.union( [set(x) for x in ("ab","bc")] )
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor 'union' requires a 'set' object but received a 'list'

Again, I stumbled around and finally found that I should be calling it like this:

>>> set.union( *[set(x) for x in ("ab","bc")] )
set(['a', 'c', 'b'])

Second question: I think this means that set.union is (effectively) declared as

set.union( *sets)

and not

set.union( setsList ) 

Is that correct? (I'm still learning how to use splat '*'.)

Third question: Where could I find documentation on the signature of set.union()? I didn't see it in the set/freezeset doc's, and I couldn't get the inspect module to give me anything. I'm not even sure set is a module, it seems to be a type. Is it defined in a module, or what?

Thanks for reading my complicated question. It's more "How do I navigate Python documentation?" than "How do I do this in Python code?".


Responding to jonrsharpe's comment:

Ohhhhh! I'm so used to C++ where you define separate static and instance methods. Now that you explain it I can really see what's happening.

The only thing I might do different is write it as

t = set.union( *[set(x) for x strings] )
return "".join(sorted(t))

because it bugs me to treat strings[0] differently from the strings in strings[1:] when, functionally, they don't play different roles. If I have to call set() on one of them, I'd rather call it on all of them, since union() is going to do it anyways. But that's just style, right?

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
JonathanZ
  • 1,046
  • 11
  • 20

1 Answers1

2

There are several questions here. Firstly, you should know that:

Class.method(instance, arg)

is equivalent to:

instance.method(arg)

for instance methods. You can call the method on the class and explicitly provide the instance, or just call it on the instance.

For historical reasons, many of the standard library and built-in types don't follow the UppercaseWords convention for class names, but they are classes. Therefore

set.union(aset, anotherset)

is the same as

aset.union(anotherset)

set methods specifically can be tricky, because of the way they're often used. set.method(arg1, arg2, ...) requires arg1 to already be a set, the instance for the method, but all the other arguments will be converted (from 2.6 on).

This isn't directly covered in the set docs, because it's true for everything; Python is pretty consistent.

In terms of needing to "splat", note that the docs say:

union(other, ...)

rather than

union(others)

i.e. each iterable is a separate argument, hence you need to unpack your list of iterables.


Your function could therefore be:

def union_strings(strings):
    if not strings:
        return ""
    return "".join(sorted(set(strings[0]).union(*strings[1:])))

or, avoiding the special-casing of strings[0]:

def union_strings(strings): 
    if not strings:
        return ""
    return "".join(sorted(set.union(*map(set, strings))))
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • Great answer. I added a longer response to the original question. Much thanks. – JonathanZ Aug 05 '14 at 20:21
  • @Jonathan no problem; I've added a second implementation that might be more to your liking! – jonrsharpe Aug 05 '14 at 20:24
  • Ha ha! As I was writing my response I had though "I really just want to map `set` over my list of strings. I need to check out if/how `map()` works in Python?". Perfect answer! – JonathanZ Aug 05 '14 at 20:37
  • No problem. Note that Python also supports static (no class or instance argument) and class methods (first argument is class `cls`) in classes, but this is just an alternative syntax for calling instance methods (first argument is instance `self`). – jonrsharpe Aug 05 '14 at 21:46