I'm not saying that's necessarily the correct design approach, but it's the one that Python has explicitly established as their guiding principle! A different language will take a different approach: Ruby, for example, likes to build multiple alternative ways of accomplishing the same thing, which leads to multiple aliases for built-in methods or various alternative flags that allow a method to behave in a number of ways. It shouldn't be a surprise that Ruby is fine with the equivalent operation:
irb(main):001:0> ["a", "b"].sum(identity="")
=> "ab"
But that's because Ruby's design sensibility is different from Python's. You might like one better than another, but they both come from a consistent vision of how to design programming languages.This philosophy falls apart immediately once you start using Python though. I have yet to see it actually be used for anything but not implementing features that someone wants.
mylist = [x*2 for x in range(1,10)]
but also do mylist = []
for x in range(1,10):
mylist.append(x*2)
That's two ways to do the same thing, and I bet you would find a lot of the latter in beginner's python code. I think a better choice would to just have a linter which enforced the correct usage of sum() and join(), and not the implementation itself. mylist = map(lambda x: x * 2, range(1, 10))The point stands.
["a", "b"].sum("") a + b
f"{a}{b}"
"".join([a, b])
"{}{}".format(a, b)
If a == b, then you can also, which itself is a bit ridiculous: a * 2
Is mentioning StringIO considered cheating?I would feel the Zen if Python would have a separate concatenation operator, it could be even the same for strings and lists. Lack of the operator in popular languages is my pet peeve.
"%s%s" % (a, b)
(Still works at least as of python 3.6)As a reader, you don't know what an abstract operation does, just that it should obey certain rules. Writing code abstractly when abstraction is unnecessary removes information, which has a cost in readability.
Now if you want to write abstract code then it will be frustrating, but some languages are optimized for writing concrete code.
At least, in this particular case. I'm not sure how consistent Python is about this.
If 3 + 2 is allowed but you don't think "a" + "b" should work, why should 8.1 + 1.9 work? If your argument is that it's because they're numbers, what about a Complex library, or numeric vectors, or GMP? If I can do 3 + 2 and 8.1 + 1.9, I should be allowed to make Complex{0, 1} + Complex{4, 9} work, or Vector{1, 0} + Vector{2, 2}, or BigInt{99999...} + BigInt{9999...}.
I can sort of see the argument in dynamically-typed languages when you might not know what the operands are ahead of time, but for statically-typed languages like Rust and Go (Rust gets this very, very right FWIW)? There's no defensible argument against it. Why is `+` magic and holy, but a `fn add(...)` isn't?
> isn’t it already inspecting the type of the list elements
Actually it only checks if the type of start (the initial accumulator value) is a string. If the accumulator becomes a string later on, it's happy to sum strings.
if trying_to_sum_strings(args):
- complain_bitterly()
+ separator.join(args)EDIT: OK, I think if the example is sum([some_string, some_not_string], '') then there is a possibility that some_string + some_not_string might be a valid expression computing a value while ''.join([some_string, some_not_string]) would complain that some_not_string is not a string.
At least, readability suffers.
"".join(["a", "b"]) is a lot more intuitive than sum(["a", "b"], start="")
edit: A beautiful abstraction is the one that feels as good when it's read as when it's written.
No it isn't. The join method being on the separator string is a Pythonism that looks crazy coming from any other language.
join(xs, "")
xs.join("")
or whatever else you want to come up with is more readable than building more string stuff in to sum, not that having join be a str method is the best way. sum("abc") =
sum(ord(c) for c in "abc") =
sum([97, 98, 99]) =
294
because wow, i... can't think of why you'd ever actually want that. (though it'd be perfectly natural for byte arrays)