String concatenation in Python and commas
Solution 1
The comma-separated list to the left of Lyrics +=
is defining a "tuple".
You'll want to change the "," to "+", since string concatenation does not take a comma-separated list of values.
Also, consider building up a list of strings to concatenate and then using the "join" method.
Solution 2
Don't use string concatenation for this. Put the strings in a list and join the list at the end. I'd also suggest using str.format
.
verse = "{0} bottles of beer on the wall, {0} bottles of beer.\n" \
"Take one down and pass it around, {1} bottles of beer on the wall.\n"
def bottles_of_beer_lyrics(bottles=99):
lyrics = []
for x in range(bottles, 0, -1):
lyrics.append(verse.format(x, x-1))
return '\n'.join(lyrics)
You could also get the result more directly by using a generator expression:
def bottles_of_beer_lyrics(bottles=99):
return '\n'.join(verse.format(x, x-1) for x in range(bottles, 0, -1))
As a final point you should note that "1 bottles" is not grammatically correct. You may want to create a function that can give you the correct form of "bottle" depending on the number. If internationalization is an issue (I know it's probably not) then you should also be aware that some languages have more forms than just "singular" and "plural".
Solution 3
The comma operator creates a tuple. The line "Lyrics += ..." is creating a tuple on the right side.
To concatenate strings in Python use the "+" operator.
Lyrics += str(BottlesOfBeer) + " bottles of beer..." + ...
However, that's still not the preferred way for this kind of thing, but is fine for learning string concatenation.
Solution 4
It looks like you've got the original problem cleared up and are now asking about using join()
-- yes, that's generally a better than using +=
for string concatenation (although not always faster).
But even better is to not even do it or do so as little as possible. Python had something called format strings which are something like those used by C's printf()
function -- see Format String Syntax in the online documentation. Using that along with the format()
string method (and some other Pythonisms) can really simplify things:
def BottlesOfBeerLyrics(NumOfBottlesOfBeer=99):
PluralSuffix = lambda n: "s" if n != 1 else ""
Stanza = "\n".join([
"{0} bottle{1} of beer on the wall, {0} bottle{1} of beer.",
"Take one down and pass it around, {2} bottle{3} of beer on the wall.",
"\n"])
Lyrics = ""
for Bottles in range(NumOfBottlesOfBeer, 0, -1):
Lyrics += Stanza.format(Bottles, PluralSuffix(Bottles),
Bottles-1, PluralSuffix(Bottles-1))
return Lyrics
print BottlesOfBeerLyrics(3)
# 3 bottles of beer on the wall, 3 bottles of beer.
# Take one down and pass it around, 2 bottles of beer on the wall.
#
# 2 bottles of beer on the wall, 2 bottles of beer.
# Take one down and pass it around, 1 bottle of beer on the wall.
#
# 1 bottle of beer on the wall, 1 bottle of beer.
# Take one down and pass it around, 0 bottles of beer on the wall.
jkeys
Updated on July 09, 2022Comments
-
jkeys almost 2 years
I'm having a hard time figuring out how the native data types interact in Python. Here, I am trying to concatenate different parts of the lyrics into one long string which will be returned as output.
The error I receive upon trying run the script is:
TypeError: cannot concatenate 'str' and 'tuple' objects.
I put everything that wasn't a string in the function str(), but apparently something is still a "tuple" (a data type I've never used before).
How can I get whatever tuple is in there to a string so this will all concatenate smoothly?
(P.S.: I used the variable "Copy", because I wasn't sure if, when I decremented the other variable, it would mess with the for loop construct. Would it?)
#99 bottles of beer on the wall lyrics def BottlesOfBeerLyrics(NumOfBottlesOfBeer = 99): BottlesOfBeer = NumOfBottlesOfBeer Copy = BottlesOfBeer Lyrics = '' for i in range(Copy): Lyrics += BottlesOfBeer, " bottles of beer on the wall, ", str(BottlesOfBeer), " bottles of beer. \n", \ "Take one down and pass it around, ", str(BottlesOfBeer - 1), " bottles of beer on the wall. \n" if (BottlesOfBeer > 1): Lyrics += "\n" BottlesOfBeer -= 1 return Lyrics print BottlesOfBeerLyrics(99)
Some people suggested building a list and the joining it. I edited it a little bit, but is this the preferred method?
#99 bottles of beer on the wall lyrics - list method def BottlesOfBeerLyrics(NumOfBottlesOfBeer = 99): BottlesOfBeer = NumOfBottlesOfBeer Copy = BottlesOfBeer Lyrics = [] for i in range(Copy): Lyrics += str(BottlesOfBeer) + " bottles of beer on the wall, " + str(BottlesOfBeer) + " bottles of beer. \n" + \ "Take one down and pass it around, " + str(BottlesOfBeer - 1) + " bottles of beer on the wall. \n" if (BottlesOfBeer > 1): Lyrics += "\n" BottlesOfBeer -= 1 return "".join(Lyrics) print BottlesOfBeerLyrics(99)
-
Keith over 13 yearsPS. numbers are also immutable objects, and implicitly copied. Thus you don't need the Copy variable.
-
Keith over 13 yearsRE: attempt #2, use Lyrics.append(...) now. You can also remove all "\n" in the strings and use "\n".join(Lyrics) at the end.
-
jkeys over 13 yearsBut I would still use the + operator and append the whole thing all at once? Also, wouldn't that solution overlook the newline in between the two lines, or does the '\' operator make the append method act as if there were two method calls?
-
-
jkeys over 13 yearsPerfect! Also, I forgot to put the first BottlesOfBeer into the str() function. Both changes fixed it. Thanks for that! But could you tell me why building it into a list and using "".join(Lyrics) would be a better solution?
-
jkeys over 13 yearsWhy is using the join function a superior method? Is string concatenation inherently slower?
-
Josh Bleecher Snyder over 13 yearsIt is slower in some implementations of Python; in cPython, it happens not be much slower. The reason to do this is that it is now a Pythonic idiom (originally due to performance, I believe).
-
Keith over 13 yearsStrings are immutable. Concatenating them creates a new string and copies the other stings into it, for each "+" operator. Putting into a list and joining only creates each string once, and one final string copy at the end. Thus it's faster.
-
Mark Byers over 13 years@Hooked: It also means you don't have the special case for not adding the separator after the last line.
str.join
already handles this for you. -
jkeys over 4 years@Keith lol looking back at questions I asked nearly ten years ago. Learned a lot since then, though I'm not sure I'm a much better programmer. It's just my goalposts have shifted significantly... :D