Table of Contents
Python’s assignment operators allow you to defineassignment statements. This type of statement lets you create, initialize, and update variables throughout your code. Variables are a fundamental cornerstone in every piece of code, and assignment statements give you complete control over variable creation and mutation.
Learning about the Python assignment operator and its use for writing assignment statements will arm you with powerful tools for writing better and more robust Python code.
In this tutorial, you’ll:
You’ll dive deep into Python’s assignment statements. To get the most out of this tutorial, you should be comfortable with several basic topics, includingvariables, built-indata types,comprehensions,functions, and Pythonkeywords. Before diving into some of the later sections, you should also be familiar with intermediate topics, such asobject-oriented programming,constants,imports,type hints,properties,descriptors, anddecorators.
Free Source Code:Click here to download the free assignment operator source code that you’ll use to write assignment statements that allow you to create, initialize, and update variables in your code.
One of the most powerful programming language features is the ability to create, access, and mutatevariables. In Python, a variable is a name that refers to a concrete value or object, allowing you to reuse that value or object throughout your code.
To create a new variable or to update the value of an existing one in Python, you’ll use anassignment statement. This statement has the following three components:
=
)Here’s how an assignment statement will generally look in Python:
variable=expression
Here,variable
represents a generic Python variable, whileexpression
represents any Python object that you can provide as a concrete value—also known as aliteral—or an expression that evaluates to a value.
To execute an assignment statement like the above, Python runs the following steps:
The second step shows that variables work differently in Python than in other programming languages. In Python, variables aren’t containers for objects. Python variables point to a value or object through its memory address. Theystore memory addresses rather than objects.
This behavior difference directly impacts how data moves around in Python, which is alwaysby reference. In most cases, this difference is irrelevant in your day-to-day coding, but it’s still good to know.
The central component of an assignment statement is theassignment operator. This operator is represented by the=
symbol, which separates two operands:
Operators are special symbols that performmathematical,logical, andbitwise operations in a programming language. The objects (or object) on which an operator operates are calledoperands.
Unary operators, like thenot
Boolean operator, operate on a single object or operand, whilebinary operators act on two. That means the assignment operator is a binary operator.
Note: LikeC, Python uses==
for equality comparisons and=
for assignments. Unlike C, Python doesn’t allow you to accidentally use the assignment operator (=
) in an equality comparison.
Equality is a symmetrical relationship, and assignment is not. For example, the expressiona == 42
is equivalent to42 == a
. In contrast, the statementa = 42
is correct and legal, while42 = a
isn’t allowed. You’ll learn more aboutillegal assignments later on.
The right-hand operand in an assignment statement can be any Python object, such as anumber,list,string,dictionary, or even a user-defined object. It can also be an expression. In the end, expressions always evaluate to concrete objects, which is their return value.
Here are a few examples of assignments in Python:
>>>number=42>>>greeting="Hello, World!">>>total=15+25>>>is_true=42<84
The first two sample assignments in this code snippet use concrete values, also known asliterals, to create and initializenumber
andgreeting
. The third example assigns the result of a math expression to thetotal
variable, while the last example uses aBoolean expression.
Note: You can use the built-inid()
function to inspect the memory address stored in a given variable.
Here’s a short example of how this function works:
>>>number=42>>>id(number)4311827984
The number in your output represents the memory address stored innumber
. Through this address, Python can access the content ofnumber
, which is the integer42
in this example.
If you run this code on your computer, then you’ll get a different memory address because this value varies from execution to execution and computer to computer.
Unlike expressions, assignment statements don’t have a return value because their purpose is to make the association between the variable and its value. That’s why the Pythoninterpreter doesn’t issue any output in the above examples.
Now that you know the basics ofhow to write an assignment statement, it’s time to tacklewhy you would want to use one.
The assignment statement is the explicit way for you to associate a name with an object in Python. You can use this statement for two main purposes:
When you use a variable name as the left operand in an assignment statement for the first time, you’re creating a new variable. At the same time, you’re initializing the variable to point to the value of the right operand.
On the other hand, when you use an existing variable in a new assignment, you’re updating or mutating the variable’s value. Strictly speaking, every new assignment will make the variable refer to a new value and stop referring to the old one. Python willgarbage-collect all the values that are no longer referenced by any existing variable.
Assignment statements not only assign a value to a variable but also determine thedata type of the variable at hand. This additional behavior is another important detail to consider in this kind of statement.
Because Python is adynamically typed language, successive assignments to a given variable can change the variable’s data type. Changing the data type of a variable during a program’s execution is considered bad practice and highly discouraged. It can lead to subtle bugs that can be difficult to track down.
Unlike in math equations, in Python assignments, the left operand must be a variable rather than an expression or a value. For example, the following construct is illegal, and Python flags it as invalid syntax:
>>>a=3>>>b=4>>>hypotenuse**2=a**2+b**2 ...SyntaxError: cannot assign to expression here. Maybe you meant '==' instead of '='?
In this example, you have expressions on both sides of the=
sign, and this isn’t allowed in Python code. The error message suggests that you may be confusing the equality operator with the assignment one, but that’s not the case. You’re really running an invalid assignment.
To correct this construct and convert it into a valid assignment, you’ll have to do something like the following:
>>>frommathimportsqrt>>>a=3>>>b=4>>>hypotenuse=sqrt(a**2+b**2)>>>hypotenuse5.0
In this code snippet, you first import thesqrt()
function from themath
module. Then you isolate thehypotenuse
variable in the original equation by using thesqrt()
function. Now your code works correctly.
Now you know what kind of syntax is invalid. But don’t get the idea that assignment statements are rigid and inflexible. In fact, they offer lots of room for customization, as you’ll learn next.
Python’s assignment statements are pretty flexible and versatile. You can write them in several ways, depending on your specific needs and preferences. Here’s a quick summary of the main ways to write assignments in Python:
# Base assignmentsvariable=value# A literal, such as None, 0, 3.14, "Hello", {}variable=expression# Math, Boolean, bitwise expression, function calls...# Multiple assignmentsvariable_1=variable_2=...=variable_n=expression# Parallel assignmentsvariable_1,variable_2,...,variable_n=value_1,value_2,...,value_nvariable_1,variable_2,...,variable_n=exp_1,exp_2,...,exp_n# Augmented assignmentsexisting_variable+=valueexisting_variable+=expression# Parallel assignments with iterable unpackingvariable_1,variable_2,...,variable_n=n_length_iterable(variable_1,variable_2,...,variable_n)=n_length_iterable[variable_1,variable_2,...,variable_n]=n_length_iterablevariable,*bag_variable,...,variable_n=unknown_length_iterable
Up to this point, you’ve mostly learned about thebase assignment syntax in the above code snippet. In the following sections, you’ll learn about multiple, parallel, and augmented assignments. You’ll also learn about assignments with iterable unpacking.
Read on to see the assignment statements in action!
You’ll find and use assignment statements everywhere in your Python code. They’re a fundamental part of the language, providing an explicit way to create, initialize, and mutate variables.
You can use assignment statements with plain names, likenumber
orcounter
. You can also use assignments in more complicated scenarios, such as with:
user.name
a_list[i]
anda_list[i:j]
a_dict[key]
This list isn’t exhaustive. However, it gives you some idea of how flexible these statements are. You can even assign multiple values to an equal number of variables in a single line, commonly known asparallel assignment. Additionally, you can simultaneously assign the values in an iterable to a comma-separated group of variables in what’s known as aniterable unpacking operation.
In the following sections, you’ll dive deeper into all these topics and a few other exciting things that you can do with assignment statements in Python.
The most elementary use case of an assignment statement is to create a new variable and initialize it using a particular value or expression:
>>>counter=0>>>celsius=25>>>fahrenheit=(celsius*9/5)+32>>>user_template={"id":None,"name":"","permissions":("r",)}>>>welcome_message="Welcome to Real Python!">>>is_empty=False
All these statements create new variables, assigning them initial values or expressions. For an initial value, you should always use the most sensible andleast surprising value that you can think of. For example, initializing a counter to something different from0
may be confusing and unexpected because counters almost always start having counted no objects.
Updating a variable’s current value orstate is another common use case of assignment statements. In Python, assigning a new value to an existing variable doesn’t modify the variable’s current value. Instead, it causes the variable to refer to adifferent value. The previous value will be garbage-collected if no other variable refers to it.
Consider the following examples:
>>>greeting="Hello, World!">>>greeting'Hello, World!'>>>greeting="Hi, Pythonistas!">>>greeting'Hi, Pythonistas!'
These examples run two consecutive assignments on the same variable. The first one assigns the string"Hello, World!"
to a new variable namedgreeting
.
The second assignment updates the value ofgreeting
by reassigning it the"Hi, Pythonistas!"
string. In this example, the original value ofgreeting
—the"Hello, World!"
string— is lost and garbage-collected. From this point on, you can’t access the old"Hello, World!"
string.
Even though running multiple assignments on the same variable during a program’s execution is common practice, you should use this feature with caution. Changing the value of a variable can make your code difficult to read, understand, and debug. To comprehend the code fully, you’ll have to remember all the places where the variable was changed and the sequential order of those changes.
Because assignments also define the data type of their target variables, it’s also possible for your code to accidentally change the type of a given variable at runtime. A change like this can lead to breaking errors, likeAttributeError
exceptions. Remember that strings don’t have the same methods and attributes as lists or dictionaries, for example.
In Python, you can make several variables reference the same object in a multiple-assignment line. This can be useful when you want to initialize several similar variables using the same initial value:
>>>letter_counter=word_counter=0>>>id(letter_counter)==id(word_counter)True
In this example, you chain two assignment operators in a single line. This way, your two variables refer to the same initial value of0
. Note how both variables hold the same memory address, so they point to the same instance of0
.
When it comes to integer variables, Python exhibits a curious behavior. It provides a numeric interval where multiple assignments behave the same as independent assignments. Consider the following examples:
>>># Independent assignments>>>n=42>>>m=42>>>id(n)==id(m)True>>># Multiple assignments>>>x=y=42>>>id(x)==id(y)True
To createn
andm
, you use independent assignments. Therefore, they should point to different instances of the number42
. However, both variables hold the same object, which you confirm by comparing their corresponding memory addresses.
Now check what happens when you use a greater initial value:
>>>n=300>>>m=300>>>id(x)==id(y)False>>>x=y=300>>>id(x)==id(y)True
Nown
andm
hold different memory addresses, which means they point to different instances of the integer number300
. In contrast, when you use multiple assignments, both variables refer to the same object. This tiny difference can save you small bits of memory if you frequently initialize integer variables in your code.
The implicit behavior of making independent assignments point to the same integer number is actually an optimization calledinterning. It consists of globallycaching the most commonly used integer values in day-to-day programming.
Under the hood, Python defines a numeric interval in which interning takes place. That’s theinterning interval for integer numbers. You can determine this interval using a small script like the following:
# interning.pyfromplatformimportpython_versioninterning=[xforx,yinzip(range(-10,500),range(-10,500))ifxisy]print(f"Interning interval for Python{python_version()} is:"f" [{interning[0]} to{interning[-1]}]")
This script helps you determine the interning interval by comparing integer numbers from-10
to500
. If you run the script from your command line, then you’ll get an output like the following:
$pythoninterning.pyInterning interval for Python 3.11.0 is: (-5 to 256)
This output means that if you use a single number between-5
and256
to initialize several variables in independent statements, then all these variables will point to the same object, which will help you save small bits of memory in your code.
In contrast, if you use a number that falls outside of the interning interval, then your variables will point to different objects instead. Each of these objects will occupy a different memory spot.
You can use the assignment operator to mutate the value stored at a given index in a Python list. The operator also works with listslices. The syntax to write these types of assignment statements is the following:
a_list[index]=expressiona_list[start:stop:step]=expression
In the first construct,expression
can return any Python object, including another list. In the second construct,expression
must return a series of values as a list, tuple, or any other sequence. You’ll get aTypeError
ifexpression
returns a single value.
Note: When creatingslice
objects, you can use up to three arguments. These arguments arestart
,stop
, andstep
. They define the number thatstarts the slice, the number at which the slicing muststop retrieving values, and thestep between values.
Here’s an example of updating an individual value in a list:
>>>numbers=[1,2,7,4,5]>>>numbers[1, 2, 7, 4, 5]>>>numbers[2]=3>>>numbers[1, 2, 3, 4, 5]
In this example, you update the value at index2
using an assignment statement. The original number at that index was7
, and after the assignment, the number is3
.
Note: Using indices and the assignment operator to update a value in a tuple or a character in a string isn’t possible because tuples and strings are immutable data types in Python.
Their immutability means that you can’t change their itemsin place:
>>>numbers=(1,2,2,4,5)>>>numbers[2]=3Traceback (most recent call last):...TypeError:'tuple' object does not support item assignment>>>letters="ABcDE">>>letters[2]="C"Traceback (most recent call last):...TypeError:'str' object does not support item assignment
You can’t use the assignment operator to change individual items in tuples or strings. These data types are immutable and don’t support item assignments.
It’s important to note that you can’t add new values to a list by using indices that don’t exist in the target list:
>>>numbers[5]=6Traceback (most recent call last):...IndexError:list assignment index out of range
In this example, you try to add a new value to the end ofnumbers
by using an index that doesn’t exist. This assignment isn’t allowed because there’s no way to guarantee that new indices will be consecutive. If you ever want to add a single value to the end of a list, then use the.append()
method.
If you want to update several consecutive values in a list, then you can use slicing and an assignment statement:
>>>letters=["A","b","c","D"]>>>letters[1:3]=["B","C"]>>>letters['A', 'B', 'C', 'D']>>>letters[3:]=("F","G")>>>letters['A', 'B', 'C', 'F', 'G']>>>letters[3:3]=["D"]>>>letters['A', 'B', 'C', 'D', 'F', 'G']>>>letters[1::2]=["b","d","g"]>>>letters['A', 'b', 'C', 'd', 'F', 'g']
In the first example, you update the letters between indices1
and3
without including the letter at3
. The second example updates the letters from index3
until the end of the list. Note that this slicing appends a new value to the list because the target slice is shorter than the assigned values.
Also note that the new values were provided through a tuple, which means that this type of assignment allows you to use other types of sequences to update your target list.
The third example updates a single value using a slice where both indices are equal. In this example, the assignment inserts a new item into your target list.
In the final example, you use astep
of2
to replace alternating letters with their lowercase counterparts. This slicing starts at index1
and runs through the whole list, stepping by two items each time.
Updating the value of an existing key or adding new key-value pairs to a dictionary is another common use case of assignment statements. To do these operations, you can use the following syntax:
a_dict[existing_key]=expressiona_dict[new_key]=expression
The first construct helps you update the current value of an existing key, while the second construct allows you to add a new key-value pair to the dictionary.
For example, to update an existing key, you can do something like this:
>>>inventory={"apple":100,"orange":80,"banana":120}>>>inventory{'apple': 100, 'orange': 80, 'banana': 120}>>>inventory["orange"]=140>>>inventory{'apple': 100, 'orange': 140, 'banana': 120}
In this example, you update the current inventory of oranges in your store using an assignment. The left operand is the existing dictionary key, and the right operand is the desired new value.
While you can’t add new values to a list by assignment, dictionariesdo allow you to add new key-value pairs using the assignment operator. In the example below, you add alemon
key toinventory
:
>>>inventory["lemon"]=100>>>inventory{'apple': 100, 'orange': 140, 'banana': 120, 'lemon': 100}
In this example, you successfully add a new key-value pair to your inventory with100
units. This addition is possible because dictionaries don’t have consecutive indices but unique keys, which are safe to add by assignment.
The assignment statement does more than assign the result of a single expression to a single variable. It can also cope nicely with assigning multiple values to multiple variables simultaneously in what’s known as aparallel assignment.
Here’s the general syntax for parallel assignments in Python:
variable_1,variable_2,...,variable_n=value_1,value_2,...,value_nvariable_1,variable_2,...,variable_n=exp_1,exp_2,...,exp_n(variable_1,variable_2,...,variable_n)=exp_1,exp_2,...,exp_n[variable_1,variable_2,...,variable_n]=exp_1,exp_2,...,exp_n
Note that the left side of the statement can be either a tuple or a list of variables. Remember that to create a tuple, you just need a series of comma-separated elements. In this case, these elements must be variables.
The right side of the statement must be a sequence or iterable of values or expressions. In any case, the number of elements in the right operand must match the number of variables on the left. Otherwise, you’ll get aValueError
exception.
In the following example, you compute the two solutions of aquadratic equation using a parallel assignment:
>>>frommathimportsqrt>>>a,b,c=2.0,-1.0,-4.0>>>x1,x2=(...(-b-sqrt(b**2-4*a*c))/(2*a),...(-b+sqrt(b**2-4*a*c))/(2*a),...)>>>f"{x1=},{x2=}"'x1=-1.1861406616345072, x2=1.6861406616345072'
In this example, you first importsqrt()
from themath
module. Then you initialize the equation’s coefficients in a parallel assignment.
The equation’s solution is computed in another parallel assignment. The left operand contains a tuple of two variables,x1
andx2
. The right operand consists of a tuple of expressions that compute the solutions for the equation. Note how each result is assigned to each variable by position.
A classical use case of parallel assignment is to swap values between variables:
>>>previous_value=42>>>next_value=43>>>next_value,previous_value=previous_value,next_value>>>previous_value43>>>next_value42
The highlighted line does the magic and swaps the values ofprevious_value
andnext_value
at the same time. Note that in a programming language that doesn’t support this kind of assignment, you’d have to use a temporary variable to produce the same effect:
>>>previous_value=42>>>next_value=43>>>temp=previous_value>>>previous_value=next_value>>>next_value=temp>>>previous_value43>>>next_value42
In this example, instead of using parallel assignment to swap values between variables, you use a new variable to temporarily store the value ofprevious_value
to avoid losing its reference.
For a concrete example of when you’d need to swap values between variables, say you’re learning how to implement thebubble sort algorithm, and you come up with the following function:
>>>defbubble_sort_list(a_list):...n=len(a_list)...foriinrange(n):...is_sorted=True...forjinrange(n-i-1):...ifa_list[j]>a_list[j+1]:...a_list[j],a_list[j+1]=a_list[j+1],a_list[j]...is_sorted=False...ifis_sorted:...break...returna_list...>>>bubble_sort_list([1,3,2,4,7,6,3,8,9,1])[1, 1, 2, 3, 3, 4, 6, 7, 8, 9]
In the highlighted line, you use a parallel assignment to swap values in place if the current value is less than the next value in the input list. To dive deeper into the bubble sort algorithm and into sorting algorithms in general, check outSorting Algorithms in Python.
You can use assignment statements foriterable unpacking in Python. Unpacking an iterable means assigning its values to a series of variables one by one. The iterable must be the right operand in the assignment, while the variables must be the left operand.
Like in parallel assignments, the variables must come as a tuple or list. The number of variables must match the number of values in the iterable. Alternatively, you can use the unpacking operator (*
) to grab several values in a variable if the number of variables doesn’t match the iterable length.
Here’s the general syntax for iterable unpacking in Python:
variable_1,variable_2,...,variable_n=n_length_iterable(variable_1,variable_2,...,variable_n)=n_length_iterable[variable_1,variable_2,...,variable_n]=n_length_iterablevariable,*bag_variable,...,variable_n=unknown_length_iterable
Iterable unpacking is a powerful feature that you can use all around your code. It can help you write more readable and concise code. For example, you may find yourself doing something like this:
>>>numbers=[1,2,3,4]>>>one=numbers[0]>>>two=numbers[1]>>>three=numbers[2]>>>four=numbers[3]>>>one1>>>two2>>>three3>>>four4
Whenever you do something like this in your code, go ahead and replace it with a more readable iterable unpacking using a single and elegant assignment, like in the following code snippet:
>>>numbers=[1,2,3,4]>>>one,two,three,four=numbers>>>one1>>>two2>>>three3>>>four4
Thenumbers
list on the right side contains four values. The assignment operator unpacks these values into the four variables on the left side of the statement. The values innumbers
get assigned to variables in the same order that they appear in the iterable. The assignment is done by position.
Note: Because Pythonsets are also iterables, you can use them in an iterable unpacking operation. However, it won’t be clear which value goes to which variable because sets are unordered data structures.
The above example shows the most common form of iterable unpacking in Python. The main condition for the example to work is that the number of variables matches the number of values in the iterable.
What if you don’t know the iterable length upfront? Will the unpacking work? It’ll work if you use the*
operator to pack several values into one of your target variables.
For example, say that you want to unpack the first and second values innumbers
into two different variables. Additionally, you would like to pack the rest of the values in a single variable conveniently calledrest
. In this case, you can use the unpacking operator like in the following code:
>>>first,second,*rest=numbers>>>first1>>>second2>>>rest[3, 4]
In this example,first
andsecond
hold the first and second values innumbers
, respectively. These values are assigned by position. The*
operator packs all the remaining values in the input iterable intorest
.
The unpacking operator (*
) can appear at any position in your series of target variables. However, you can only use one instance of the operator:
>>>*head,last=numbers>>>head[1, 2, 3]>>>last4>>>head,*body,tail=numbers>>>head1>>>body[2, 3]>>>tail4>>>*head,*rest=numbers ...SyntaxError: multiple starred expressions in assignment
The iterable unpacking operator works in any position in your list of variables. Note that you can only use one unpacking operator per assignment. Using more than one unpacking operator isn’t allowed and raises aSyntaxError
.
Dropping away unwanted values from the iterable is a common use case for the iterable unpacking operator. Consider the following example:
>>>*_,useful=numbers>>>useful4>>>_[1, 2, 3]
In Python, if you want to signal that a variable won’t be used, then you use anunderscore (_
) as the variable’s name. In this example,useful
holds the only value that you need to use from the input iterable. The_
variable is a placeholder that guarantees that the unpacking works correctly. You won’t use the values that end up in this disposable variable.
Note: In the example above, if your target iterable is a sequence data type, such as a list or tuple, then it’s best to access its last item directly.
To do this, you can use the-1
index:
>>>useful=numbers[-1]>>>useful4
Using-1
gives you access to the last item of any sequence data type. In contrast, if you’re dealing withiterators, then you won’t be able to use indices. That’s when the*_
syntax comes to your rescue.
The pattern used in the above example comes in handy when you have a function that returns multiple values, and you only need a few of these values in your code. Theos.walk()
function may provide a good example of this situation.
This function allows you toiterate over the content of a directory recursively. The function returns a generator object that yields three-item tuples. Each tuple contains the following items:
Now say that you want to iterate over your home directory and list only the files. You can do something like this:
>>>importos>>>forcontentinos.walk("/path/to/your/home"):...*_,filenames=content...print(filenames)...
This code will issue a long output depending on the current content of your home directory. Note that you need to provide a string with the path to your user folder for the example to work. The_
placeholder variable will hold the unwanted data.
In contrast, thefilenames
variable will hold the list of files in the current directory, which is the data that you need. The code will print the list of filenames. Go ahead and give it a try!
The assignment operator also comes in handy when you need to providedefault argument values in your functions and methods. Default argument values allow you to define functions that take arguments with sensible defaults. These defaults allow you to call the function with specific values or to simply rely on the defaults.
As an example, consider the following function:
>>>defgreet(name="World"):...print(f"Hello,{name}!")...
This function takes one argument, calledname
. This argument has a sensible default value that’ll be used when you call the function without arguments. To provide this sensible default value, you use an assignment.
Note: According toPEP 8, the style guide for Python code, you shouldn’t use spaces around the assignment operator when providing default argument values in function definitions.
Here’s how the function works:
>>>greet()Hello, World!>>>greet("Pythonista")Hello, Pythonista!
If you don’t provide a name during the call togreet()
, then the function uses the default value provided in the definition. If you provide a name, then the function uses it instead of the default one.
Up to this point, you’ve learned a lot about the Python assignment operator and how to use it for writing different types of assignment statements. In the following sections, you’ll dive into a great feature of assignment statements in Python. You’ll learn aboutaugmented assignments.
Python supports what are known asaugmented assignments. An augmented assignment combines the assignment operator with another operator to make the statement more concise. Most Python math and bitwise operators have an augmented assignment variation that looks something like this:
variable$=expression
Note that$
isn’t a valid Python operator. In this example, it’s a placeholder for a generic operator. This statement works as follows:
expression
to produce a value.=
sign, using the previous value ofvariable
and the return value ofexpression
as operands.variable
.In practice, an augmented assignment like the above is equivalent to the following statement:
variable=variable$expression
As you can conclude, augmented assignments aresyntactic sugar. They provide a shorthand notation for a specific and popular kind of assignment.
For example, say that you need to define acounter
variable to count some stuff in your code. You can use the+=
operator to incrementcounter
by1
using the following code:
>>>counter=0>>>counter+=1>>>counter1>>>counter+=1>>>counter2
In this example, the+=
operator, known asaugmented addition, adds1
to the previous value incounter
each time you run the statementcounter += 1
.
It’s important to note that unlike regular assignments, augmented assignments don’t create new variables. They only allow you toupdate existing variables. If you use an augmented assignment with an undefined variable, then you get aNameError
:
>>>x+=1Traceback (most recent call last):...NameError:name 'x' is not defined
Python evaluates the right side of the statement before assigning the resulting value back to the target variable. In this specific example, when Python tries to computex + 1
, it finds thatx
isn’t defined.
Great! You now know that an augmented assignment consists of combining the assignment operator with another operator, like a math or bitwise operator. To continue this discussion, you’ll learn which math operators have an augmented variation in Python.
An equation like x = x + b doesn’t make sense in math. But in programming, a statement likex = x + b
is perfectly valid and can be extremely useful. It addsb
tox
and reassigns the result back tox
.
As you already learned, Python provides an operator to shortenx = x + b
. Yes, the+=
operator allows you to writex += b
instead. Python also offers augmented assignment operators for most math operators. Here’s a summary:
Operator | Description | Example | Equivalent |
---|---|---|---|
+= | Adds the right operand to the left operand and stores the result in the left operand | x += y | x = x + y |
-= | Subtracts the right operand from the left operand and stores the result in the left operand | x -= y | x = x - y |
*= | Multiplies the right operand with the left operand and stores the result in the left operand | x *= y | x = x * y |
/= | Divides the left operand by the right operand and stores the result in the left operand | x /= y | x = x / y |
//= | Performsfloor division of the left operand by the right operand and stores the result in the left operand | x //= y | x = x // y |
%= | Finds the remainder of dividing the left operand by the right operand and stores the result in the left operand | x %= y | x = x % y |
**= | Raises the left operand to the power of the right operand and stores the result in the left operand | x **= y | x = x ** y |
TheExample column provides generic examples of how to use the operators in actual code. Note thatx
must be previously defined for the operators to work correctly. On the other hand,y
can be either a concrete value or an expression that returns a value.
Note: The matrix multiplication operator (@
) doesn’t support augmented assignments yet.
Consider the following example of matrix multiplication usingNumPy arrays:
>>>importnumpyasnp>>>x=np.ones(3)>>>xarray([1., 1., 1.])>>>m=np.eye(3)>>>marray([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])>>>x@marray([1., 1., 1.])>>>x@=mTraceback (most recent call last):...TypeError:In-place matrix multiplication is not (yet) supported. Use 'a = a @ b' instead of 'a @= b'.
Note that the exceptiontraceback indicates that the operation isn’t supported yet.
To illustrate how augmented assignment operators work, say that you need to create a function that takes an iterable of numeric values and returns their sum. You can write this function like in the code below:
>>>defsum_all(numbers):...total=0...fornumberinnumbers:...total+=number# Augmented addition...returntotal...>>>sum_all([1,2,3,4])10
In this function, you first initializetotal
to0
. In each iteration, the loop adds a new number tototal
using the augmented addition operator (+=
). When the loop terminates,total
holds the sum of all the input numbers. Variables liketotal
are known asaccumulators. The+=
operator is typically used to update accumulators.
Note: Computing the sum of a series of numeric values is a common operation in programming. Python provides the built-insum()
function for this specific computation.
Another interesting example of using an augmented assignment is when you need to implement a countdownwhile
loop to reverse an iterable. In this case, you can use the-=
operator:
>>>defcustom_reversed(sequence):...index=len(sequence)-1...whileindex>=0:...yieldsequence[index]...index-=1...>>>list(custom_reversed("12345"))['5', '4', '3', '2', '1']
In this example,custom_reversed()
is agenerator function because it usesyield
. Calling the function creates an iterator that yields items from the input iterable in reverse order. To decrement the control variable,index
, you use an augmented subtraction statement that subtracts1
from the variable in every iteration.
Note: Similar to summing the values in an iterable, reversing an iterable is also a common requirement. Python provides the built-inreversed()
function for this specific computation, so you don’t have to implement your own. The above example only intends to show the-=
operator in action.
Finally, counters are a special type of accumulators that allow you to count objects. Here’s an example of a letter counter:
>>>word="mississippi">>>counter={}>>>forletterinword:...ifletternotincounter:...counter[letter]=0...counter[letter]+=1...>>>counter{'m': 1, 'i': 4, 's': 4, 'p': 2}
To create this counter, you use a Python dictionary. The keys store the letters. The values store the counts. Again, to increment the counter, you use an augmented addition.
Counters are so common in programming that Python provides a tool specially designed to facilitate the task of counting. Check outPython’s Counter: The Pythonic Way to Count Objects for a complete guide on how to use this tool.
The+=
and*=
augmented assignment operators also work withsequences, such as lists, tuples, and strings. The+=
operator performsaugmented concatenations, while the*=
operator performsaugmented repetition.
These operators behave differently with mutable and immutable data types:
Operator | Description | Example |
---|---|---|
+= | Runs an augmented concatenation operation on the target sequence. Mutable sequences are updated in place. If the sequence is immutable, then a new sequence is created and assigned back to the target name. | seq_1 += seq_2 |
*= | Addsseq to itselfn times. Mutable sequences are updated in place. If the sequence is immutable, then a new sequence is created and assigned back to the target name. | seq *= n |
Note that the augmentedconcatenation operator operates on two sequences, while the augmentedrepetition operator works on a sequence and an integer number.
Consider the following examples and pay attention to the result of calling theid()
function:
>>># Mutable target>>>list_1=[1,2,3]>>>id(list_1)4323479104>>>list_2=[4,5]>>>list_2[4, 5]>>>list_1+=list_2# Concatenation...>>>list_1[1, 2, 3, 4, 5]>>>id(list_1)4323479104>>># Immutable target>>>tuple_1=(1,2,3)>>>id(tuple_1)4387336896>>>tuple_2=(4,5)>>>tuple_2(4, 5)>>>tuple_1+=tuple_2# Concatenation...>>>tuple_1(1, 2, 3, 4, 5)>>>id(tuple_1)4387485104
Mutable sequences like lists support the+=
augmented assignment operator through the.__iadd__()
method, which performs an in-place addition. This method mutates the underlying list,appending new values to its end.
Note: If the left operand is mutable, thenx += y
may not be completely equivalent tox = x + y
. For example, if you dolist_1 = list_1 + list_2
instead oflist_1 += list_2
above, then you’ll create a new list instead of mutating the existing one. This may be important if other variables refer to the same list.
Immutable sequences, such as tuples and strings, don’t provide an.__iadd__()
method. Therefore, augmented concatenations fall back to the.__add__()
method, which doesn’t modify the sequence in place but returns a new sequence.
There’s another difference between mutable and immutable sequences when you use them in an augmented concatenation. Consider the following examples:
>>>a_list=[1,2,3]>>>a_list+=(4,5)>>>a_list[1, 2, 3, 4, 5]>>>a_list+="67">>>a_list[1, 2, 3, 4, 5, '6', '7']>>>a_tuple=(1,2,3)>>>a_tuple+=[4,5]Traceback (most recent call last):...TypeError:can only concatenate tuple (not "list") to tuple>>>a_string="123">>>a_string+=("4","5")Traceback (most recent call last):...TypeError:can only concatenate str (not "tuple") to str
With mutable sequences, the data to be concatenated can come as a list, tuple, string, or any other iterable. In contrast, with immutable sequences, the data can only come as objects of the same type. You can concatenate tuples to tuples and strings to strings, for example.
Again, the augmented repetition operator works with a sequence on the left side of the operator and an integer on the right side. This integer value represents the number of repetitions to get in the resulting sequence:
>>># Mutable target>>>a_list=[1,2,3]>>>id(a_list)4395563840>>>n=2>>>a_list*=n# Repetition...>>>a_list[1, 2, 3, 1, 2, 3]>>>id(a_list)4395563840>>>a_list[0]isa_list[3]True>>># Immutable target>>>a_tuple=(1,2,3)>>>id(a_tuple)4393522944>>>n=2>>>a_tuple*=n# Repetition...>>>a_tuple(1, 2, 3, 1, 2, 3)>>>id(a_tuple)4394199328>>>a_tuple[0]isa_tuple[3]True
When the*=
operator operates on a mutable sequence, it falls back to the.__imul__()
method, which performs the operation in place, modifying the underlying sequence. In contrast, if*=
operates on an immutable sequence, then.__mul__()
is called, returning a new sequence of the same type.
Note: Values ofn
less than0
are treated as0
, which returns an empty sequence of the same data type as the target sequence on the left side of the*=
operand.
Note thata_list[0] is a_list[3]
returnsTrue
. This is because the*=
operator doesn’t make a copy of the repeated data. It only reflects the data. This behavior can be a source of issues when you use the operator with mutable values.
For example, say that you want to create a list of lists to represent a matrix, and you need to initialize the list withn
empty lists, like in the following code:
>>>n=3>>>matrix=[[]]>>>matrix*=n>>>matrix[[], [], []]
In this example, you use the*=
operator to populatematrix
with three empty lists. Now check out what happens when you try to populate the first sublist inmatrix
:
>>>matrix[0].append(1)>>>matrix[0].append(2)>>>matrix[0].append(3)>>>matrix[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
The appended values are reflected in the three sublists. This happens because the*=
operator doesn’t make copies of the data that you want to repeat. It only reflects the data. Therefore, every sublist inmatrix
points to the same object and memory address.
If you ever need to initialize a list with a bunch of empty sublists, then use alist comprehension:
>>>n=3>>>matrix=[[]for_inrange(n)]>>>matrix[[], [], []]>>>matrix[0].append(1)>>>matrix[0].append(2)>>>matrix[0].append(3)>>>matrix[[1, 2, 3], [], []]
This time, when you populate the first sublist ofmatrix
, your changes aren’t propagated to the other sublists. This is because all the sublists are different objects that live in different memory addresses.
Bitwise operators also have their augmented versions. The logic behind them is similar to that of the math operators. The following table summarizes the augmented bitwise operators that Python provides:
Operator | Operation | Example | Equivalent |
---|---|---|---|
&= | Augmented bitwise AND (conjunction) | x &= y | x = x & y |
|= | Augmented bitwise OR (disjunction) | x |= y | x = x | y |
^= | Augmented bitwise XOR (exclusive disjunction) | x ^= y | x = x ^ y |
>>= | Augmented bitwise right shift | x >>= y | x = x >> y |
<<= | Augmented bitwise left shift | x <<= y | x = x << y |
The augmented bitwise assignment operators perform the intended operation by taking the current value of the left operand as a starting point for the computation. Consider the following example, which uses the&
and&=
operators:
>>>number_1=123>>>bin(number_1)'0b1111011'>>>number_2=456>>>bin(number_2)'0b111001000'>>># Bitwise AND>>># 0b1111011 (int 123)>>># & 0b111001000 (int 456)>>># ------------->>># 0b1001000 (int 72)>>>number_1&number_272>>>bin(number_1&number_2)'0b1001000'>>>number_1&=number_2>>>number_172>>>bin(number_1)'0b1001000'
Programmers who work with high-level languages like Python rarely use bitwise operations in day-to-day coding. However, these types of operations can be useful in some situations.
For example, say that you’re implementing aUnix-style permission system for your users to access a given resource. In this case, you can use the characters"r"
for reading,"w"
for writing, and"x"
for execution permissions, respectively. However, using bit-based permissions could be more memory efficient:
>>>r=0b100>>>w=0b010>>>x=0b001>>># Assign permissions to users with |>>>admin=r|w|x>>>bin(admin)'0b111'>>># Assign permissions to users with |=>>>user=r>>>user|=w>>>bin(user)'0b110'>>># Check permission with & and bool()>>>bool(r&user)True>>>bool(x&user)False
You can assign permissions to your users with the OR bitwise operator or the augmented OR bitwise operator. Finally, you can use the bitwise AND operator to check if a user has a certain permission, as you did in the final two examples.
You’ve learned a lot about augmented assignment operators and statements in this and the previous sections. These operators apply to math, concatenation, repetition, and bitwise operations. Now you’re ready to look at other assignment variants that you can use in your code or find in other developers’ code.
So far, you’ve learned that Python’s assignment statements and the assignment operator are present in many different scenarios and use cases. Those use cases include variable creation and initialization, parallel assignments, iterable unpacking, augmented assignments, and more.
In the following sections, you’ll learn about a few variants of assignment statements that can be useful in your future coding. You can also find these assignment variants in other developers’ code. So, you should be aware of them and know how they work in practice.
In short, you’ll learn about:
These topics will take you through several interesting and useful examples that showcase the power of Python’s assignment statements.
PEP526 introduced a dedicated syntax forvariable annotation back in Python3.6. The syntax consists of the variable name followed by a colon (:
) and the variable type:
>>>counter:int>>>name:str>>>fruits:list[str]
Even though these statements declare three variables with their corresponding data types, the variables aren’t actually created or initialized. So, for example, you can’t use any of these variables in an augmented assignment statement:
>>>counter+=1Traceback (most recent call last):...NameError:name 'counter' is not defined
If you try to use one of the previously declared variables in an augmented assignment, then you get aNameError
because the annotation syntax doesn’t define the variable. To actually define it, you need to use an assignment.
The good news is that you can use the variable annotation syntax in an assignment statement with the=
operator:
>>>counter:int=0>>>counter+=1>>>counter+=1>>>counter2
The first statement in this example is what you can call anannotated assignment statement in Python. You may ask yourself why you should use type annotations in this type of assignment if everybody can see thatcounter
holds an integer number. You’re right. In this example, the variable type is unambiguous.
However, imagine what would happen if you found a variable initialization like the following:
>>>classUser:...# Class implementation......pass...>>>users=[]
What would be the data type of each user inusers
? If the initialization ofusers
is far away from the definition of theUser
class, then there’s no quick way to answer this question. To clarify this ambiguity, you can provide the appropriate type hint forusers
:
>>>classUser:...# Class implementation......pass...>>>users:list[User]=[]
Now you’re clearly communicating thatusers
will hold a list ofUser
instances. Using type hints in assignment statements that initialize variables to empty collection data types—such as lists, tuples, or dictionaries—allows you to provide more context about how your code works. This practice will make your code more explicit and less error-prone.
Up to this point, you’ve learned that regular assignment statements with the=
operator don’t have a return value. They just create or update variables. Therefore, you can’t use a regular assignment to assign a value to a variable within the context of an expression.
Python 3.8 changed this by introducing a new type of assignment statement throughPEP 572. This new statement is known as anassignment expression ornamed expression.
Note: Expressions are a special type of statement in Python. Their distinguishing characteristic is that expressions always have a return value, which isn’t the case with all types of statements.
Unlike regular assignments, assignment expressions have a return value, which is why they’re calledexpressions in the first place. This return value is automatically assigned to a variable. To write an assignment expression, you must use thewalrus operator (:=
), which was named for its resemblance to the eyes and tusks of a walrus lying on its side.
The general syntax of an assignment statement is as follows:
(variable:=expression)
This expression looks like a regular assignment. However, instead of using the assignment operator (=
), it uses the walrus operator (:=
). For the expression to work correctly, the enclosing parentheses are required in most use cases. However, there are certain situations in which these parentheses are superfluous. Either way, they won’t hurt you.
Assignment expressions come in handy when you want to reuse the result of an expression or part of an expression without using a dedicated assignment to grab this value beforehand.
Note: Assignment expressions with the walrus operator have several practical use cases. They also have a few restrictions. For example, they’re illegal in certain contexts, such aslambda
functions, parallel assignments, and augmented assignments.
For a deep dive into this special type of assignment, check outThe Walrus Operator: Python’s Assignment Expressions.
A particularly handy use case for assignment expressions is when you need to grab the result of an expression used in the context of aconditional statement. For example, say that you need to write a function to compute the mean of a sample of numeric values. Without the walrus operator, you could do something like this:
>>>defmean(sample):...n=len(sample)...ifn==0:...raiseValueError("input data required")...returnsum(sample)/n...>>>mean([1,2,3,4,5])3.0
In this example, the sample size (n
) is a value that you need to reuse in two different computations. First, you need to check whether the sample has data points or not. Then you need to use the sample size to compute the mean. To be able to reusen
, you wrote a dedicated assignment statement at the beginning of your function to grab the sample size.
You can avoid this extra step by combining it with the first use of the target value,len(sample)
, using an assignment expression like the following:
>>>defmean(sample):...if(n:=len(sample))==0:...raiseValueError("input data required")...returnsum(sample)/n...>>>mean([1,2,3,4,5])3.0
The assignment expression introduced in the conditional computes the sample size and assigns it ton
. This way, you guarantee that you have a reference to the sample size to use in further computations.
Because the assignment expression returns the sample size anyway, the conditional can check whether that size equals0
or not and then take a certain course of action depending on the result of this check. Thereturn
statement computes the sample’s mean and sends the result back to the function caller.
Python provides a few tools that allow you to fine-tune the operations behind the assignment of attributes. The attributes that run implicit operations on assignments are commonly referred to asmanaged attributes.
Properties are the most commonly used tool for providing managed attributes in your classes. However, you can also use descriptors and, in some cases, the.__setitem__()
special method.
To understand whatfine-tuning the operation behind an assignment means, say that you need aPoint
class that only allows numeric values for its coordinates,x
andy
. To write this class, you must set up a validation mechanism to reject non-numeric values. You can use properties to attach the validation functionality on top ofx
andy
.
Here’s how you can write your class:
# point.pyclassPoint:@propertydefx(self):returnself._x@x.setterdefx(self,value):try:self._x=float(value)exceptValueError:raiseValueError('"x" must be a number')fromNone@propertydefy(self):returnself._y@y.setterdefy(self,value):try:self._y=float(value)exceptValueError:raiseValueError('"y" must be a number')fromNone
InPoint
, you use properties for the.x
and.y
coordinates. Each property has agetter and a setter method. The getter method returns the attribute at hand. The setter method runs theinput validation using atry
…except
block and the built-infloat()
function. Then the method assigns the result to the actual attribute.
Here’s how your class works in practice:
>>>frompointimportPoint>>>point_1=Point()>>>point_1.x=1>>>point_1.y=2>>>point_1.x,point_1.y1.0 2.0>>>point_2=Point()>>>point_2.x="one"Traceback (most recent call last):...ValueError:"x" must be a number
When you use a property-based attribute as the left operand in an assignment statement, Python automatically calls the property’s setter method, running any computation from it.
Because both.x
and.y
are properties, the input validation runs whenever you assign a value to either attribute. In the first example, the input values are valid numbers and the validation passes. In the final example,"one"
isn’t a valid numeric value, so the validation fails.
If you look at yourPoint
class, you’ll note that it follows a repetitive pattern, with the getter and setter methods looking quite similar. To avoid this repetition, you can use adescriptor instead of a property.
A descriptor is a class that implements thedescriptor protocol, which consists of fourspecial methods:
.__get__()
runs when you access the attribute represented by the descriptor..__set__()
runs when you use the attribute in an assignment statement..__delete__()
runs when you use the attribute in adel
statement..__set_name__()
sets the attribute’s name, creating a name-aware attribute.Here’s how your code may look if you use a descriptor to represent the coordinates of yourPoint
class:
# point.pyclassCoordinate:def__set_name__(self,owner,name):self._name=namedef__get__(self,instance,owner):returninstance.__dict__[self._name]def__set__(self,instance,value):try:instance.__dict__[self._name]=float(value)exceptValueError:raiseValueError(f'"{self._name}" must be a number')fromNoneclassPoint:x=Coordinate()y=Coordinate()
You’ve removed repetitive code by definingCoordinate
as a descriptor that manages the input validation in a single place. Go ahead and run the following code to try out the new implementation ofPoint
:
>>>frompointimportPoint>>>point_1=Point()>>>point_1.x=1>>>point_1.y=2>>>point_1.x,point_1.y1.0 2.0>>>point_2=Point()>>>point_2.x="one"Traceback (most recent call last):...ValueError:"x" must be a number
Great! The class works as expected. Thanks to theCoordinate
descriptor, you now have a more concise and non-repetitive version of your original code.
Another way to fine-tune the operations behind an assignment statement is to provide a custom implementation of.__setitem__()
in your class. You’ll use this method in classes representing mutable data collections, such as customlist-like ordictionary-like classes.
As an example, say that you need to create a dictionary-like class that stores its keys in lowercase letters:
>>>fromcollectionsimportUserDict>>>classLowerCasedDict(UserDict):...def__setitem__(self,key,value):...key=key.lower()...super().__setitem__(key,value)...>>>numbers=LowerCasedDict()>>>numbers["ONE"]=1>>>numbers["Two"]=2>>>numbers["Three"]=3>>>numbers{'one': 1, 'two': 2, 'three': 3}
In this example, you create a dictionary-like class by subclassingUserDict
fromcollections
. Your class implements a.__setitem__()
method, which takeskey
andvalue
as arguments. The method usesstr.lower()
to convertkey
into lowercase letters before storing it in the underlying dictionary.
Python implicitly calls.__setitem__()
every time you use a key as the left operand in an assignment statement. This behavior allows you to tweak how you process the assignment of keys in your custom dictionary.
Python implicitly runs assignments in many different contexts. In most cases, these implicit assignments are part of the language syntax. In other cases, they support specific behaviors.
Whenever you complete an action in the following list, Python runs an implicit assignment for you:
self
for
loop or a comprehensionas
qualifier inwith
statements, imports, andtry
…except
blocks_
special variable in an interactive sessionBehind the scenes, Python performs an assignment in every one of the above situations. In the following subsections, you’ll take a tour of all these situations.
When you define a function, thedef
keyword implicitly assigns afunction object to your function’s name. Here’s an example:
>>>defgreet(name):...print(id(name))...print(f"Hello,{name}!")...>>>greet<function greet at 0x105e9bb00>>>>id(greet)4394171136>>>fellow="Pythonista">>>greet(fellow)4381781552Hello, Pythonista!>>>id(fellow)4381781552
From this point on, the namegreet
refers to a function object that lives at a given memory address in your computer. You can call the function using its name and a pair of parentheses with appropriate arguments. This way, you can reusegreet()
wherever you need it.
If you call yourgreet()
function withfellow
as an argument, then Python implicitly assigns the input argument value to thename
parameter on the function’s definition. The parameter will hold a reference to the input arguments.
When you define a class with theclass
keyword, you’re assigning a specific name to aclass object. You can later use this name to create instances of that class. Consider the following example:
>>>classUser:...def__init__(self,name,job):...self.name=name...self.job=job...>>>User<class '__main__.User'>>>>id(User)5035278528>>>john=User("John Doe","Python Dev")>>>john.name'John Doe'>>>john.job'Python Dev'
In this example, the nameUser
holds a reference to a class object, which was defined in__main__.User
. Like with a function, when you call the class’sconstructor with the appropriate arguments to create an instance, Python assigns the arguments to the parameters defined in the classinitializer.
Another example of implicit assignments is the current instance of a class, which in Python is calledself
by convention. This name implicitly gets a reference to the current object whenever you instantiate a class. Thanks to this implicit assignment, you can access.name
and.job
from within the class without getting aNameError
in your code.
Import statements are another variant of implicit assignments in Python. Through an import statement, you assign a name to a module object, class, function, or any other imported object. This name is then created in your currentnamespace so that you can access it later in your code:
>>>dir()['__builtins__', '__doc__', ..., '__spec__', 'help']>>>importsys>>>dir()['__builtins__', '__doc__', ..., '__spec__', 'help', 'sys']>>>sys<module 'sys' (built-in)>
In this example, you import thesys
module object from the standard library and assign it to thesys
name, which is now available in your namespace, as you can conclude from the second call to the built-indir()
function.
You also run an implicit assignment when you use adecorator in your code. The decorator syntax is just a shortcut for a formal assignment like the following:
func=decorator(func)
Here, you calldecorator()
with a function object as an argument. This call will typically add functionality on top of the existing function,func()
, and return a function object, which is then reassigned to thefunc
name.
The decorator syntax issyntactic sugar for replacing the previous assignment, which you can now write as follows:
@decoratordeffunc():# Implementation here...pass
Even though this new code looks pretty different from the above assignment, the code implicitly runs the same steps.
for
Loop or a ComprehensionAnother situation in which Python automatically runs an implicit assignment is when you use afor
loop or a comprehension. In both cases, you can have one or more control variables that you then use in the loop or comprehension body:
>>>forcontrol_variableinrange(5):...print(f"{control_variable=}{id(control_variable)=}")...control_variable=0 id(control_variable)=4376944840control_variable=1 id(control_variable)=4376944872control_variable=2 id(control_variable)=4376944904control_variable=3 id(control_variable)=4376944936control_variable=4 id(control_variable)=4376944968
The memory address ofcontrol_variable
changes on each iteration of the loop. This is because Python internally reassigns a new value from the loop iterable to the loop control variable on each cycle.
The same behavior appears in comprehensions:
>>>[...f"{control_variable=}{id(control_variable)=}"...forcontrol_variableinrange(5)...][ 'control_variable=0 id(control_variable)=4376944840', 'control_variable=1 id(control_variable)=4376944872', 'control_variable=2 id(control_variable)=4376944904', 'control_variable=3 id(control_variable)=4376944936', 'control_variable=4 id(control_variable)=4376944968']
In the end, comprehensions work likefor
loops but use a more concise syntax. This comprehension creates a new list of strings that mimic the output from the previous example.
as
KeywordTheas
keyword inwith
statements,except
clauses, andimport
statements is another example of an implicit assignment in Python. This time, the assignment isn’t completely implicit because theas
keyword provides an explicit way to define the target variable.
In awith
statement, the target variable that follows theas
keyword will hold a reference to the context manager that you’re working with. As an example, say that you have ahello.txt
file with the following content:
Hello, Pythonista!Welcome to Real Python!
You want to open this file and print each of its lines on your screen. In this case, you can use thewith
statement to open the file using the built-inopen()
function.
In the example below, you accomplish this. You also add some calls toprint()
that display information about the target variable defined by theas
keyword:
>>>withopen("hello.txt",mode="r")ashello:...print(f"File object:{hello}")...print(f"Memory address:{id(hello)}")...print("File content:")...forlineinhello:...print("> ",line)...File object: <_io.TextIOWrapper name='hello.txt' mode='r' encoding='UTF-8'>Memory address: 4372378896File content:> Hello, Pythonista!> Welcome to Real Python!
Thiswith
statement uses theopen()
function to openhello.txt
. Theopen()
function is a context manager that returns a text file object represented by anio.TextIOWrapper
instance.
Since you’ve defined ahello
target variable with theas
keyword, now that variable holds a reference to the file object itself. You confirm this by printing the object and its memory address. Finally, thefor
loop iterates over the lines and prints this content to the screen.
When it comes to using theas
keyword in the context of anexcept
clause, the target variable will contain an exception object if any exception occurs:
>>>try:...5/0...exceptZeroDivisionErroraserror:...print(f"Exception:{error}")...print(f"Memory address:{id(error)}")...Exception: division by zeroMemory address: 4382188704>>>errorTraceback (most recent call last):...NameError:name 'error' is not defined
In this example, you run a division that raises aZeroDivisionError
. Theas
keyword assigns the raised exception toerror
. Note that when you print the exception object, you get only the message because exceptions have a custom.__str__()
method that supports this behavior.
There’s a final detail to remember when using theas
specifier in atry
…except
block like the one in the above example. Once you leave theexcept
block, the target variable goes out ofscope, and you can’t use it anymore.
Finally, Python’simport
statements also support theas
keyword. In this context, you can useas
to import objects with a different name:
>>>importnumpyasnp>>>importpandasaspd>>>dir()['__builtins__', '__doc__', ..., 'help', 'np', 'pd']
In these examples, you use theas
keyword to import thenumpy
package with thenp
name andpandas
with the namepd
. If you calldir()
, then you’ll realize thatnp
andpd
are now in your namespace. However, thenumpy
andpandas
names are not.
Using theas
keyword in your imports comes in handy when you want to use shorter names for your objects or when you need to use different objects that originally had the same name in your code. It’s also useful when you want to make your imported namesnon-public using a leading underscore, like inimport sys as _sys
.
_
Special Variable in an Interactive SessionThe final implicit assignment that you’ll learn about in this tutorial only occurs when you’re using Python in aninteractive session. Every time you run a statement that returns a value, the interpreter stores the result in a special variable denoted by a single underscore character (_
).
You can access this special variable as you’d access any other variable:
>>># Expressions>>>5<7True>>>_True>>>12+3042>>>_42>>># Function calls>>>sum([1,2,3,4])10>>>_10>>>print("Hello, Pythonista!")Hello, Pythonista!>>>_10>>># Assignments>>>counter=0>>>_10>>># Variable accesses>>>counter0>>>_0
These examples cover several situations in which Python internally uses the_
variable. The first two examples evaluate expressions. Expressions always have a return value, which is automatically assigned to the_
variable every time.
When it comes to function calls, note that if your function returns a fruitful value, then_
will hold it. In contrast, if your function returnsNone
, then the_
variable will remain untouched.
The next example consists of a regular assignment statement. As you already know, regular assignments don’t return any value, so the_
variable isn’t updated after these statements run. Finally, note that accessing a variable in an interactive session returns the value stored in the target variable. This value is then assigned to the_
variable.
Note that since_
is a regular variable, you can use it in other expressions:
>>>numbers=[1,2,3,4]>>>len(numbers)4>>>sum(numbers)/_2.5
In this example, you first create a list of values. Then you calllen()
to get the number of values in the list. Python automatically stores this value in the_
variable. Finally, you use_
to compute the mean of your list of values.
Now that you’ve learned about some of the implicit assignments that Python runs under the hood, it’s time to dig into a final assignment-related topic. In the following few sections, you’ll learn about some illegal and dangerous assignments that you should be aware of and avoid in your code.
In Python, you’ll find a few situations in which using assignments is either forbidden or dangerous. You must be aware of these special situations and try to avoid them in your code.
In the following sections, you’ll learn when using assignment statements isn’t allowed in Python. You’ll also learn about some situations in which using assignments should be avoided if you want to keep your code consistent and robust.
You can’t use Pythonkeywords as variable names in assignment statements. This kind of assignment is explicitly forbidden. If you try to use a keyword as a variable name in an assignment, then you get aSyntaxError
:
>>>class= "Economy" File"<stdin>", line1class= "Economy"^SyntaxError:invalid syntax>>>global=42 File"<input>", line1global=42^SyntaxError:invalid syntax
Whenever you try to use a keyword as the left operand in an assignment statement, you get aSyntaxError
. Keywords are an intrinsic part of the language and can’t be overridden.
If you ever feel the need to name one of your variables using a Python keyword, then you can append an underscore to the name of your variable:
>>>class_="Economy">>>class_'Economy'>>>global_=42>>>global_42
In this example, you’re using the desired name for your variables. Because you added a final underscore to the names, Python doesn’t recognize them as keywords, so it doesn’t raise an error.
Note: Even though adding an underscore at the end of a name is an officiallyrecommended practice, it can be confusing sometimes. Therefore, try to find an alternative name or use a synonym whenever you find yourself using this convention.
For example, you can write something like this:
>>>booking_class="Economy">>>booking_class'Economy'
In this example, using the namebooking_class
for your variable is way clearer and more descriptive than usingclass_
.
You’ll also find that you can use only a few keywords as part of the right operand in an assignment statement. Those keywords will generally definesimple statements that return a value or object. These includelambda
,and
,or
,not
,True
,False
,None
,in
, andis
. You can also use thefor
keyword when it’s part of a comprehension and theif
keyword when it’s used as part of aternary operator.
In an assignment, you can never use a compound statement as the right operand. Compound statements are those that require an indented block, such asfor
andwhile
loops, conditionals,with
statements,try
…except
blocks, and class or function definitions.
Sometimes, you need to name variables, but the desired or ideal name is already taken and used as a built-in name. If this is your case, think harder and find another name. Don’t shadow the built-in.
Shadowing built-in names can cause hard-to-identify problems in your code. A common example of this issue is usinglist
ordict
to name user-defined variables. In this case, you override the corresponding built-in names, which won’t work as expected if you use them later in your code.
Consider the following example:
>>>list=[1,2,3,4]>>>squares=list(map(lambdax:x**2,[1,2,3,4]))Traceback (most recent call last):...TypeError:'list' object is not callable
The exception in this example may sound surprising. How come you can’t uselist()
to build a list from a call tomap()
that returns a generator of square numbers?
By using the namelist
to identify your list of numbers, you shadowed the built-inlist
name. Now that name points to a list object rather than the built-in class. List objects aren’t callable, so your code no longer works.
In Python, you’ll have nothing that warns against using built-in, standard-library, or even relevant third-party names to identify your own variables. Therefore, you should keep an eye out for this practice. It can be a source of hard-to-debug errors.
In programming, aconstant refers to a name associated with a value that never changes during a program’s execution. Unlike other programming languages, Python doesn’t have a dedicated syntax for defining constants. This fact implies that Python doesn’t have constants in the strict sense of the word.
Python only has variables. If you need a constant in Python, then you’ll have to define a variable and guarantee that it won’t change during your code’s execution. To do that, you must avoid using that variable as the left operand in an assignment statement.
To tell other Python programmers that a given variable should be treated as a constant, you must write your variable’s name in capital letters with underscores separating the words. This naming convention has been adopted by the Python community and is a recommendation that you’ll find in theConstants section ofPEP 8.
In the following examples, you define some constants in Python:
>>>PI=3.14>>>MAX_SPEED=300>>>WIDTH=20>>>BASE_URL="https://api.example.com"
The problem with these constants is that they’re actually variables. Nothing prevents you from changing their value during your code’s execution. So, at any time, you can do something like the following:
>>>PI=3.141592653589793>>>MAX_SPEED=1_000
These assignments modify the value of two of your original constants. Python doesn’t complain about these changes, which can cause issues later in your code. As a Python developer, you must guarantee that named constants in your code remain constant.
The only way to do that is never to use named constants in an assignment statement other than the constant definition.
You’ve learned a lot about Python’s assignment operators and how to use them for writingassignment statements. With this type of statement, you can create, initialize, and update variables according to your needs. Now you have the required skills to fully manage the creation and mutation of variables in your Python code.
In this tutorial, you’ve learned how to:
Learning about the Python assignment operator and how to use it in assignment statements is a fundamental skill in Python. It empowers you to write reliable and effective Python code.
Free Source Code:Click here to download the free assignment operator source code that you’ll use to write assignment statements that allow you to create, initialize, and update variables in your code.
🐍 Python Tricks 💌
Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.
AboutLeodanis Pozo Ramos
Leodanis is an industrial engineer who loves Python and software development. He's a self-taught Python developer with 6+ years of experience. He's an avid technical writer with a growing number of articles published on Real Python and other sites.
» More about LeodanisMasterReal-World Python Skills With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
MasterReal-World Python Skills
With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
What Do You Think?
What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.
Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.
Keep Learning
Related Topics:intermediatebest-practicespython
Related Tutorials:
Already have an account?Sign-In
Almost there! Complete this form and click the button below to gain instant access:
Python's Assignment Operator: Write Robust Assignments (Source Code)