Python allows developers to use common arithmetic operators on strings. This high-level manipulation of textual data is one of the many reasons Python has surged in popularity. One example of this is the ability to use arithmetic operators to manipulate strings in Python.
While convenient, there are some limitations to using arithmetic operators such as the plus, minus, plus-equal, and even multiplication operators on strings. For example, multiplying a string by a float will result in TypeError: can’t multiply sequence by non-int of type ‘float’ exception.
Highlights
In this article, we look at some use cases for applying arithmetic operators to Python strings. The discussion will include some common cases, advanced cases, and also common issues. By the end, we will cover the following:
- Basic string concatenation with arithmetic operators.
- Which operators aren’t able to be used in string operations.
- Novel means of applying the multiplication operator to generate copies of strings.
- How operator precedence still applies to string operations.
- Tips for using arithmetic operators on user input.
- How
bytearray
objects can mutate strings.
Before we dive in, let us first consider the nature of Python strings. Namely, we will briefly discuss how Python strings are immutable, what that means, and the implications this has on performing string-based arithmetic operations.
Quick Intro: Understanding Python Strings
Python strings are different from strings in many other languages. Without diving deeply into the details, it is important to note the following points about Python strings:
- Python strings are immutable. Changing a string requires making a copy.
- Strings are arrays (list) objects whereby each individual character can be accessed via index-notation as
[i]
wherei
is a numerical index into the array (list.) - Numerical user input is converted to a string and must be typecast to the intended format before arithmetic operations can be accurately applied.
Example 0: Strings are Immutable
This is not an example of multiplying, adding, or subtracting strings. However, this point is so essential to understanding how strings are stored and accessed in Python that it must be discussed. Consider the following example:
# example of number mutability a = 5 print(a) a += 1 print(a) # output 5 6
This shows a variable a
being assigned an integer value of 5
then, via the +=
operator (equivalent to a = a + 1
) the value is “incremented” by 1 resulting in a final value of 6. This is confirmed via the output from the 2 print
statements. In most other programming languages it would be insane to consider doing such an operation to a string. Let’s see how Python handles it:
# Create an example string s1 = "alpharithms" print(s1) # concatenate some text s1 += " is cool." print(s1) # output alpharithms alpharithms is cool.
Here we see additional text added to the s1
variable. It seems like we are mutating a string but really we are not. Rather, we are simply reassigning a new value to the variables s1
. Let’s see what happens if we try to mutate a string directly in Python:
# create a string s1 = "alpharithms" # attempt to remove the s from the end s1[-1] = "" TypeError: 'str' object does not support item assignment
Here we get our first glimpse at the immutable nature of Python strings. A TypeError
exception is raised when trying to directly manipulate a value contained in a Python string array. This is in stark contrast to how one can manipulate arrays of numerical values — or even arrays of characters. Consider the following code:
# change single character a1 = ['a', 'l', 'p', 'h', 'a', 'r', 'i', 't', 'h', 'm', 's'] a1[-1] = "\0" print(a1) # change integer item a2 = [1, 2, 3, 4, 5] a2[0] = 6 print(a2) # Output ['a', 'l', 'p', 'h', 'a', 'r', 'i', 't', 'h', 'm', '\x00'] [6, 2, 3, 4, 5]
Here we see the output reflect our changes without any exception being raised. This kind of illustrates how Python strings are “special” arrays in that their contained data cannot be manipulated. For a deeper dive, read this article. Simple recognition of the immutability of Python strings is enough for our discussion here.
Example 1: Arithmetic Operator-Based Concatenation
Python allows the use of arithmetic operators to be used for string manipulation. This allows the addition, subtraction, and even multiplication of strings to take place via common operators such as +
and *
, as well as the assignment operator, but not -=
or *=
. We’ll take a look at those last cases in a moment. For now, let’s consider the others:
# creates a string a1 = "alpharithms" # create a new string with extra text a2 = a1 + " is cool" print(a2) # use assignment operator to do the same a1 += " is cool" print(a1) # output alpharithms is cool alpharithms is cool
Here we see the output to be identical whether we explicitly add a new string via assignment to a new variable a2
or implicitly via the +=
operator (assigning a new value to the initial variables a1
.) Let’s see how some other arithmetic operators hold up in this approach:
subtraction
# Subtraction and multiplication don't always work b1 = "alpharithms" b2 = b1 - "s" print(b2) # Output TypeError: unsupported operand type(s) for -: 'str' and 'str' b1 = "alpharithms" b1 -= "s" print(b1) # Output TypeError: unsupported operand type(s) for -=: 'str' and 'str'
Here we see the same TypeError
exception being raised. Clearly, the subtraction operator doesn’t work well with strings. Division and multiplication operations generate similar exceptions. However, multiplication-based string manipulation is possible in another way.
Example 2: Multiplication-Based String Manipulation
Multiplication is an edge case whereby Python provides some extended features. Similar to subtraction and division, the multiplication operator doesn’t work in the direct manipulation of a string:
# create the sample string s1 = "alpharithms" # attempt a direct multiplication operation s2 = s1 * " is cool" # output TypeError: can't multiply sequence by non-int of type 'str' # attempt an assignment operation s1 *= " is cool" # output TypeError: can't multiply sequence by non-int of type 'str'
Python does allow the multiplication operator to be used to create multiple copies of a string. Consider the following example:
# Create an example string s1 = "alpharithms" # Use multiplication modifier with an integer value # to create a new string made of duplicates of the # initial string s2 = s1 * 5 print(s2) # output alpharithmsalpharithmsalpharithmsalpharithmsalpharithms
The same output would be produced using the s1 *= 5
syntax. On a conceptual level this makes sense — equivalent to issuing the instruction “create 5 copies of s1
“. What would happen if we used a fractional value though? Let’s see what happens when we try to create 5 and a half copies of the string:
# Create an example string s1 = "alpharithms" # Create 5 and a half copies s2 = s1 * 5.5 TypeError: can't multiply sequence by non-int of type 'float'
Here we see a TypeError: can’t multiply sequence by non-int of type ‘float’ exception being raised. Python could 100% have elected to interpret fractional values such as floats to return a percentage of total characters with creating copies of strings. In this case the 0.5
would be 11/2 would be 5.5, rounded down to the nearest whole-number 5 characters: alpha
. This is not the case however and float multiplication cannot be used with strings.
Example 3: Mixing Operators
It should be clear at this point that the addition and multiplication operators are useful for manipulating strings. We have seen the application of each in isolate but have not yet considered how one might use them in conjunction with one another. Consider the following code:
# create a sample string s1 = "alpharithms" # Create 5 copies separated by a space s2 = s1 + " " * 5 # print result print(s2) # result alpharithms
Rather than the expected 5 copies of alpharithms
we instead see what looks to be a single copy! In fact, this is a single copy with 5 additional space characters appended to the end. If the " "
portion was replaced with "-"
the output would be alpharithms-----
.
This example highlights that operator precedence is valid even when dealing with strings. To add the space between 5 copies as intended, we would need to add parenthesis to specify a different order of operations as such:
# create a sample string s1 = "alpharithms" # Create 5 copies separated by a space s2 = s1 + " " * 5 # print result print(s2) # result alpharithms alpharithms alpharithms alpharithms alpharithms
Here we see the output returned as expected. By adding the (
and )
characters around the s1 + " "
operation we add the space character before creating 5 copies of the string!
Example 4: User Input Converts to Strings
When accepting user input in Python, any input provided by the user is converted into a string. For example, the following code prompts the user for a number that will be multiplied by the value 3
:
# prompt user value = input("Enter a number to multiply by 3: ") # < enter the number 3 here > # Multiply input by 3 result = value * 3 # Display result to user print("The result is:", result) # View result The Result is: 333
Here we entered a value of 3
at the prompt and noticed a return value of 333
— clearly not an accurate calculation of 3 * 3 = 9
. The issue is that python interprets the user inputted value of 3
as a string, which results in an operation equivalent to "3" * 3
which, as shown above, creates 3 copies of the string input.
This problem can be solved by converting user input that is expected to be numerical to numerical data before performing arithmetic operations. For example, the following code would result in the expected output:
# prompt user value = input("Enter a number to multiply by 3: ") # Multiply input by 3 result = int(value) * 3 # Display result to user print("The result is:", result) # Result Enter a number to multiply by 3: 3 The result is: 9
Example x: Using Bytearry Objects to Manipulate Strings
Python creates new copies of any strings in which changing manipulations are made. As a broadly implying description — Python allocates new memory when strings are being mutated. As such, performing large numbers of string manipulations can become a burden on memory. To get around this issue, byte-arrays can be created as such:
# create a byte array from a string, encoded as ascii s1 = bytearray("alpharithms", encoding='ascii') # Change the last character to a newline s1[-1:] = bytearray("\n", encoding='ascii') # print decoded result print(s1.decode('ascii')) # Output alpharithm
We have an entire article dedicated to the topic of using Python bytearry objects. Check it out to learn more about this built-in function of Python and how it relates to manipulating strings!
Final Thoughts
Python strings are just one of the many data types offered to developers. The decisions made during Python’s development prioritize syntactic ease vs. conceptual precedence such that somewhat odd syntaxes such as using the *
operator to create copies of strings can be used. However alien compared to other popular programming languages this approach is without a doubt convenient.
Here we’ve seen how arithmetic operators can be used to manipulate Python strings. The most common error is when attempting to multiply via floating-point values or in creating copies rather than numerical values from user input. Keeping these in mind can help leverage the power of Python string manipulation syntax without generating a TypeError
.