Python's Assignment Operator: Write Robust Assignments

Python's Assignment Operator: Write Robust Assignments

Table of Contents

The Assignment Statement Syntax

The assignment operator, assignments and variables, other assignment syntax, initializing and updating variables, making multiple variables refer to the same object, updating lists through indices and slices, adding and updating dictionary keys, doing parallel assignments, unpacking iterables, providing default argument values, augmented mathematical assignment operators, augmented assignments for concatenation and repetition, augmented bitwise assignment operators, annotated assignment statements, assignment expressions with the walrus operator, managed attribute assignments, define or call a function, work with classes, import modules and objects, use a decorator, access the control variable in a for loop or a comprehension, use the as keyword, access the _ special variable in an interactive session, built-in objects, named constants.

Python’s assignment operators allow you to define assignment 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:

  • Use Python’s assignment operator to write assignment statements
  • Take advantage of augmented assignments in Python
  • Explore assignment variants, like assignment expressions and managed attributes
  • Become aware of illegal and dangerous assignments in Python

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, including variables , built-in data types , comprehensions , functions , and Python keywords . Before diving into some of the later sections, you should also be familiar with intermediate topics, such as object-oriented programming , constants , imports , type hints , properties , descriptors , and decorators .

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.

Assignment Statements and the Assignment Operator

One of the most powerful programming language features is the ability to create, access, and mutate variables . 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 an assignment statement . This statement has the following three components:

  • A left operand, which must be a variable
  • The assignment operator ( = )
  • A right operand, which can be a concrete value , an object , or an expression

Here’s how an assignment statement will generally look in Python:

Here, variable represents a generic Python variable, while expression represents any Python object that you can provide as a concrete value—also known as a literal —or an expression that evaluates to a value.

To execute an assignment statement like the above, Python runs the following steps:

  • Evaluate the right-hand expression to produce a concrete value or object . This value will live at a specific memory address in your computer.
  • Store the object’s memory address in the left-hand variable . This step creates a new variable if the current one doesn’t already exist or updates the value of an existing variable.

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. They store memory addresses rather than objects.

This behavior difference directly impacts how data moves around in Python, which is always by 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 the assignment operator . This operator is represented by the = symbol, which separates two operands:

  • A value or an expression that evaluates to a concrete value

Operators are special symbols that perform mathematical , logical , and bitwise operations in a programming language. The objects (or object) on which an operator operates are called operands .

Unary operators, like the not Boolean operator, operate on a single object or operand, while binary operators act on two. That means the assignment operator is a binary operator.

Note: Like C , 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 expression a == 42 is equivalent to 42 == a . In contrast, the statement a = 42 is correct and legal, while 42 = a isn’t allowed. You’ll learn more about illegal assignments later on.

The right-hand operand in an assignment statement can be any Python object, such as a number , 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:

The first two sample assignments in this code snippet use concrete values, also known as literals , to create and initialize number and greeting . The third example assigns the result of a math expression to the total variable, while the last example uses a Boolean expression.

Note: You can use the built-in id() function to inspect the memory address stored in a given variable.

Here’s a short example of how this function works:

The number in your output represents the memory address stored in number . Through this address, Python can access the content of number , which is the integer 42 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 Python interpreter doesn’t issue any output in the above examples.

Now that you know the basics of how to write an assignment statement, it’s time to tackle why 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:

  • Creating and initializing new variables
  • Updating the values of existing variables

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 will garbage-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 the data type of the variable at hand. This additional behavior is another important detail to consider in this kind of statement.

Because Python is a dynamically 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:

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:

In this code snippet, you first import the sqrt() function from the math module. Then you isolate the hypotenuse variable in the original equation by using the sqrt() 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:

Up to this point, you’ve mostly learned about the base 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!

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, like number or counter . You can also use assignments in more complicated scenarios, such as with:

  • Qualified attribute names , like user.name
  • Indices and slices of mutable sequences, like a_list[i] and a_list[i:j]
  • Dictionary keys , like 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 as parallel assignment . Additionally, you can simultaneously assign the values in an iterable to a comma-separated group of variables in what’s known as an iterable 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:

All these statements create new variables, assigning them initial values or expressions. For an initial value, you should always use the most sensible and least surprising value that you can think of. For example, initializing a counter to something different from 0 may be confusing and unexpected because counters almost always start having counted no objects.

Updating a variable’s current value or state 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 a different value. The previous value will be garbage-collected if no other variable refers to it.

Consider the following examples:

These examples run two consecutive assignments on the same variable. The first one assigns the string "Hello, World!" to a new variable named greeting .

The second assignment updates the value of greeting by reassigning it the "Hi, Pythonistas!" string. In this example, the original value of greeting —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, like AttributeError 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:

In this example, you chain two assignment operators in a single line. This way, your two variables refer to the same initial value of 0 . Note how both variables hold the same memory address, so they point to the same instance of 0 .

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:

To create n and m , you use independent assignments. Therefore, they should point to different instances of the number 42 . 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:

Now n and m hold different memory addresses, which means they point to different instances of the integer number 300 . 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 called interning . It consists of globally caching 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 the interning interval for integer numbers. You can determine this interval using a small script like the following:

This script helps you determine the interning interval by comparing integer numbers from -10 to 500 . If you run the script from your command line, then you’ll get an output like the following:

This output means that if you use a single number between -5 and 256 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 list slices . The syntax to write these types of assignment statements is the following:

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 a TypeError if expression returns a single value.

Note: When creating slice objects, you can use up to three arguments. These arguments are start , stop , and step . They define the number that starts the slice, the number at which the slicing must stop retrieving values, and the step between values.

Here’s an example of updating an individual value in a list:

In this example, you update the value at index 2 using an assignment statement. The original number at that index was 7 , and after the assignment, the number is 3 .

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 items in place :

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:

In this example, you try to add a new value to the end of numbers 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:

In the first example, you update the letters between indices 1 and 3 without including the letter at 3 . The second example updates the letters from index 3 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 a step of 2 to replace alternating letters with their lowercase counterparts. This slicing starts at index 1 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:

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:

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, dictionaries do allow you to add new key-value pairs using the assignment operator. In the example below, you add a lemon key to inventory :

In this example, you successfully add a new key-value pair to your inventory with 100 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 a parallel assignment .

Here’s the general syntax for parallel assignments in Python:

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 a ValueError exception.

In the following example, you compute the two solutions of a quadratic equation using a parallel assignment:

In this example, you first import sqrt() from the math 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 and x2 . 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:

The highlighted line does the magic and swaps the values of previous_value and next_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:

In this example, instead of using parallel assignment to swap values between variables, you use a new variable to temporarily store the value of previous_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 the bubble sort algorithm , and you come up with the following function:

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 out Sorting Algorithms in Python .

You can use assignment statements for iterable 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:

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:

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:

The numbers 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 in numbers get assigned to variables in the same order that they appear in the iterable. The assignment is done by position.

Note: Because Python sets 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 in numbers into two different variables. Additionally, you would like to pack the rest of the values in a single variable conveniently called rest . In this case, you can use the unpacking operator like in the following code:

In this example, first and second hold the first and second values in numbers , respectively. These values are assigned by position. The * operator packs all the remaining values in the input iterable into rest .

The unpacking operator ( * ) can appear at any position in your series of target variables. However, you can only use one instance of the operator:

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 a SyntaxError .

Dropping away unwanted values from the iterable is a common use case for the iterable unpacking operator. Consider the following example:

In Python, if you want to signal that a variable won’t be used, then you use an underscore ( _ ) 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:

Using -1 gives you access to the last item of any sequence data type. In contrast, if you’re dealing with iterators , 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. The os.walk() function may provide a good example of this situation.

This function allows you to iterate over the content of a directory recursively. The function returns a generator object that yields three-item tuples. Each tuple contains the following items:

  • The path to the current directory as a string
  • The names of all the immediate subdirectories as a list of strings
  • The names of all the files in the current directory as a list of strings

Now say that you want to iterate over your home directory and list only the files. You can do something like this:

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, the filenames 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 provide default 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:

This function takes one argument, called name . 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 to PEP 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:

If you don’t provide a name during the call to greet() , 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 about augmented assignments .

Augmented Assignment Operators in Python

Python supports what are known as augmented 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:

Note that $ isn’t a valid Python operator. In this example, it’s a placeholder for a generic operator. This statement works as follows:

  • Evaluate expression to produce a value.
  • Run the operation defined by the operator that prefixes the = sign, using the previous value of variable and the return value of expression as operands.
  • Assign the resulting value back to variable .

In practice, an augmented assignment like the above is equivalent to the following statement:

As you can conclude, augmented assignments are syntactic sugar . They provide a shorthand notation for a specific and popular kind of assignment.

For example, say that you need to define a counter variable to count some stuff in your code. You can use the += operator to increment counter by 1 using the following code:

In this example, the += operator, known as augmented addition , adds 1 to the previous value in counter each time you run the statement counter += 1 .

It’s important to note that unlike regular assignments, augmented assignments don’t create new variables. They only allow you to update existing variables. If you use an augmented assignment with an undefined variable, then you get a NameError :

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 compute x + 1 , it finds that x 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 like x = x + b is perfectly valid and can be extremely useful. It adds b to x and reassigns the result back to x .

As you already learned, Python provides an operator to shorten x = x + b . Yes, the += operator allows you to write x += 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
Subtracts the right operand from the left operand and stores the result in the left operand
Multiplies the right operand with the left operand and stores the result in the left operand
Divides the left operand by the right operand and stores the result in the left operand
Performs of the left operand by the right operand and stores the result in the left operand
Finds the remainder of dividing the left operand by the right operand and stores the result in the left operand
Raises the left operand to the power of the right operand and stores the result in the left operand

The Example column provides generic examples of how to use the operators in actual code. Note that x 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 using NumPy arrays:

Note that the exception traceback 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:

In this function, you first initialize total to 0 . In each iteration, the loop adds a new number to total using the augmented addition operator ( += ). When the loop terminates, total holds the sum of all the input numbers. Variables like total are known as accumulators . 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-in sum() function for this specific computation.

Another interesting example of using an augmented assignment is when you need to implement a countdown while loop to reverse an iterable. In this case, you can use the -= operator:

In this example, custom_reversed() is a generator function because it uses yield . 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 subtracts 1 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-in reversed() 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:

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 out Python’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 with sequences , such as lists, tuples, and strings. The += operator performs augmented concatenations , while the *= operator performs augmented 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.
Adds to itself 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.

Note that the augmented concatenation operator operates on two sequences, while the augmented repetition operator works on a sequence and an integer number.

Consider the following examples and pay attention to the result of calling the id() function:

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, then x += y may not be completely equivalent to x = x + y . For example, if you do list_1 = list_1 + list_2 instead of list_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:

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:

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 of n less than 0 are treated as 0 , which returns an empty sequence of the same data type as the target sequence on the left side of the *= operand.

Note that a_list[0] is a_list[3] returns True . 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 with n empty lists, like in the following code:

In this example, you use the *= operator to populate matrix with three empty lists. Now check out what happens when you try to populate the first sublist in matrix :

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 in matrix points to the same object and memory address.

If you ever need to initialize a list with a bunch of empty sublists, then use a list comprehension :

This time, when you populate the first sublist of matrix , 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 ( )
Augmented bitwise OR ( )
Augmented bitwise XOR ( )
Augmented bitwise right shift
Augmented bitwise left shift

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:

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 a Unix-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:

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.

Other Assignment Variants

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:

  • Annotated assignment statements with type hints
  • Assignment expressions with the walrus operator
  • Managed attribute assignments with properties and descriptors
  • Implicit assignments in Python

These topics will take you through several interesting and useful examples that showcase the power of Python’s assignment statements.

PEP 526 introduced a dedicated syntax for variable annotation back in Python 3.6 . The syntax consists of the variable name followed by a colon ( : ) and the variable type:

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:

If you try to use one of the previously declared variables in an augmented assignment, then you get a NameError 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:

The first statement in this example is what you can call an annotated assignment statement in Python. You may ask yourself why you should use type annotations in this type of assignment if everybody can see that counter 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:

What would be the data type of each user in users ? If the initialization of users is far away from the definition of the User class, then there’s no quick way to answer this question. To clarify this ambiguity, you can provide the appropriate type hint for users :

Now you’re clearly communicating that users will hold a list of User 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 through PEP 572 . This new statement is known as an assignment expression or named 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 called expressions in the first place. This return value is automatically assigned to a variable. To write an assignment expression, you must use the walrus 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:

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 as lambda functions, parallel assignments, and augmented assignments.

For a deep dive into this special type of assignment, check out The Walrus Operator: Python 3.8 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 a conditional 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:

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 reuse n , 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:

The assignment expression introduced in the conditional computes the sample size and assigns it to n . 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 equals 0 or not and then take a certain course of action depending on the result of this check. The return 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 as managed 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 what fine-tuning the operation behind an assignment means, say that you need a Point class that only allows numeric values for its coordinates, x and y . 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 of x and y .

Here’s how you can write your class:

In Point , you use properties for the .x and .y coordinates. Each property has a getter and a setter method . The getter method returns the attribute at hand. The setter method runs the input validation using a try … except block and the built-in float() function. Then the method assigns the result to the actual attribute.

Here’s how your class works in practice:

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 your Point 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 a descriptor instead of a property.

A descriptor is a class that implements the descriptor protocol , which consists of four special 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 a del 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 your Point class:

You’ve removed repetitive code by defining Coordinate 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 of Point :

Great! The class works as expected. Thanks to the Coordinate 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 custom list-like or dictionary-like classes.

As an example, say that you need to create a dictionary-like class that stores its keys in lowercase letters:

In this example, you create a dictionary-like class by subclassing UserDict from collections . Your class implements a .__setitem__() method, which takes key and value as arguments. The method uses str.lower() to convert key 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.

Implicit Assignments in Python

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:

  • Define or call a function
  • Define or instantiate a class
  • Use the current instance , self
  • Import modules and objects
  • Use a decorator
  • Use the control variable in a for loop or a comprehension
  • Use the as qualifier in with statements , imports, and try … except blocks
  • Access the _ special variable in an interactive session

Behind 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, the def keyword implicitly assigns a function object to your function’s name. Here’s an example:

From this point on, the name greet 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 reuse greet() wherever you need it.

If you call your greet() function with fellow as an argument, then Python implicitly assigns the input argument value to the name parameter on the function’s definition. The parameter will hold a reference to the input arguments.

When you define a class with the class keyword, you’re assigning a specific name to a class object . You can later use this name to create instances of that class. Consider the following example:

In this example, the name User holds a reference to a class object, which was defined in __main__.User . Like with a function, when you call the class’s constructor with the appropriate arguments to create an instance, Python assigns the arguments to the parameters defined in the class initializer .

Another example of implicit assignments is the current instance of a class, which in Python is called self 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 a NameError 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 current namespace so that you can access it later in your code:

In this example, you import the sys module object from the standard library and assign it to the sys name, which is now available in your namespace, as you can conclude from the second call to the built-in dir() function.

You also run an implicit assignment when you use a decorator in your code. The decorator syntax is just a shortcut for a formal assignment like the following:

Here, you call decorator() 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 the func name.

The decorator syntax is syntactic sugar for replacing the previous assignment, which you can now write as follows:

Even though this new code looks pretty different from the above assignment, the code implicitly runs the same steps.

Another situation in which Python automatically runs an implicit assignment is when you use a for 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:

The memory address of control_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:

In the end, comprehensions work like for loops but use a more concise syntax. This comprehension creates a new list of strings that mimic the output from the previous example.

The as keyword in with statements, except clauses, and import statements is another example of an implicit assignment in Python. This time, the assignment isn’t completely implicit because the as keyword provides an explicit way to define the target variable.

In a with statement, the target variable that follows the as keyword will hold a reference to the context manager that you’re working with. As an example, say that you have a hello.txt file with the following content:

You want to open this file and print each of its lines on your screen. In this case, you can use the with statement to open the file using the built-in open() function.

In the example below, you accomplish this. You also add some calls to print() that display information about the target variable defined by the as keyword:

This with statement uses the open() function to open hello.txt . The open() function is a context manager that returns a text file object represented by an io.TextIOWrapper instance.

Since you’ve defined a hello target variable with the as keyword, now that variable holds a reference to the file object itself. You confirm this by printing the object and its memory address. Finally, the for loop iterates over the lines and prints this content to the screen.

When it comes to using the as keyword in the context of an except clause, the target variable will contain an exception object if any exception occurs:

In this example, you run a division that raises a ZeroDivisionError . The as keyword assigns the raised exception to error . 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 the as specifier in a try … except block like the one in the above example. Once you leave the except block, the target variable goes out of scope , and you can’t use it anymore.

Finally, Python’s import statements also support the as keyword. In this context, you can use as to import objects with a different name:

In these examples, you use the as keyword to import the numpy package with the np name and pandas with the name pd . If you call dir() , then you’ll realize that np and pd are now in your namespace. However, the numpy and pandas names are not.

Using the as 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 names non-public using a leading underscore, like in import sys as _sys .

The final implicit assignment that you’ll learn about in this tutorial only occurs when you’re using Python in an interactive 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:

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 returns None , 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:

In this example, you first create a list of values. Then you call len() 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.

Illegal and Dangerous Assignments in Python

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 Python keywords 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 a SyntaxError :

Whenever you try to use a keyword as the left operand in an assignment statement, you get a SyntaxError . 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:

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 officially recommended 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:

In this example, using the name booking_class for your variable is way clearer and more descriptive than using class_ .

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 define simple statements that return a value or object. These include lambda , and , or , not , True , False , None , in , and is . You can also use the for keyword when it’s part of a comprehension and the if keyword when it’s used as part of a ternary 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 as for and while 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 using list or dict 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:

The exception in this example may sound surprising. How come you can’t use list() to build a list from a call to map() that returns a generator of square numbers?

By using the name list to identify your list of numbers, you shadowed the built-in list 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, a constant 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 the Constants section of PEP 8 .

In the following examples, you define some constants in Python:

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:

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 writing assignment 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:

  • Write assignment statements using Python’s assignment operators
  • Work with augmented assignments in Python
  • Explore assignment variants, like assignment expression and managed attributes
  • Identify illegal and dangerous assignments in Python

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.

🐍 Python Tricks 💌

Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

About Leodanis Pozo Ramos

Leodanis 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.

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Aldren Santos

Master Real-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:

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 and get answers to common questions in our support portal . Looking for a real-time conversation? Visit the Real Python Community Chat or join the next “Office Hours” Live Q&A Session . Happy Pythoning!

Keep Learning

Related Topics: intermediate best-practices python

Keep reading Real Python by creating a free account or signing in:

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)

🔒 No spam. We take your privacy seriously.

assignment declaration python

  • Hands-on Python Tutorial »
  • 1. Beginning With Python »

1.6. Variables and Assignment ¶

Each set-off line in this section should be tried in the Shell.

Nothing is displayed by the interpreter after this entry, so it is not clear anything happened. Something has happened. This is an assignment statement , with a variable , width , on the left. A variable is a name for a value. An assignment statement associates a variable name on the left of the equal sign with the value of an expression calculated from the right of the equal sign. Enter

Once a variable is assigned a value, the variable can be used in place of that value. The response to the expression width is the same as if its value had been entered.

The interpreter does not print a value after an assignment statement because the value of the expression on the right is not lost. It can be recovered if you like, by entering the variable name and we did above.

Try each of the following lines:

The equal sign is an unfortunate choice of symbol for assignment, since Python’s usage is not the mathematical usage of the equal sign. If the symbol ↤ had appeared on keyboards in the early 1990’s, it would probably have been used for assignment instead of =, emphasizing the asymmetry of assignment. In mathematics an equation is an assertion that both sides of the equal sign are already, in fact, equal . A Python assignment statement forces the variable on the left hand side to become associated with the value of the expression on the right side. The difference from the mathematical usage can be illustrated. Try:

so this is not equivalent in Python to width = 10 . The left hand side must be a variable, to which the assignment is made. Reversed, we get a syntax error . Try

This is, of course, nonsensical as mathematics, but it makes perfectly good sense as an assignment, with the right-hand side calculated first. Can you figure out the value that is now associated with width? Check by entering

In the assignment statement, the expression on the right is evaluated first . At that point width was associated with its original value 10, so width + 5 had the value of 10 + 5 which is 15. That value was then assigned to the variable on the left ( width again) to give it a new value. We will modify the value of variables in a similar way routinely.

Assignment and variables work equally well with strings. Try:

Try entering:

Note the different form of the error message. The earlier errors in these tutorials were syntax errors: errors in translation of the instruction. In this last case the syntax was legal, so the interpreter went on to execute the instruction. Only then did it find the error described. There are no quotes around fred , so the interpreter assumed fred was an identifier, but the name fred was not defined at the time the line was executed.

It is both easy to forget quotes where you need them for a literal string and to mistakenly put them around a variable name that should not have them!

Try in the Shell :

There fred , without the quotes, makes sense.

There are more subtleties to assignment and the idea of a variable being a “name for” a value, but we will worry about them later, in Issues with Mutable Objects . They do not come up if our variables are just numbers and strings.

Autocompletion: A handy short cut. Idle remembers all the variables you have defined at any moment. This is handy when editing. Without pressing Enter, type into the Shell just

Assuming you are following on the earlier variable entries to the Shell, you should see f autocompleted to be

This is particularly useful if you have long identifiers! You can press Alt-/ several times if more than one identifier starts with the initial sequence of characters you typed. If you press Alt-/ again you should see fred . Backspace and edit so you have fi , and then and press Alt-/ again. You should not see fred this time, since it does not start with fi .

1.6.1. Literals and Identifiers ¶

Expressions like 27 or 'hello' are called literals , coming from the fact that they literally mean exactly what they say. They are distinguished from variables, whose value is not directly determined by their name.

The sequence of characters used to form a variable name (and names for other Python entities later) is called an identifier . It identifies a Python variable or other entity.

There are some restrictions on the character sequence that make up an identifier:

The characters must all be letters, digits, or underscores _ , and must start with a letter. In particular, punctuation and blanks are not allowed.

There are some words that are reserved for special use in Python. You may not use these words as your own identifiers. They are easy to recognize in Idle, because they are automatically colored orange. For the curious, you may read the full list:

There are also identifiers that are automatically defined in Python, and that you could redefine, but you probably should not unless you really know what you are doing! When you start the editor, we will see how Idle uses color to help you know what identifies are predefined.

Python is case sensitive: The identifiers last , LAST , and LaSt are all different. Be sure to be consistent. Using the Alt-/ auto-completion shortcut in Idle helps ensure you are consistent.

What is legal is distinct from what is conventional or good practice or recommended. Meaningful names for variables are important for the humans who are looking at programs, understanding them, and revising them. That sometimes means you would like to use a name that is more than one word long, like price at opening , but blanks are illegal! One poor option is just leaving out the blanks, like priceatopening . Then it may be hard to figure out where words split. Two practical options are

  • underscore separated: putting underscores (which are legal) in place of the blanks, like price_at_opening .
  • using camel-case : omitting spaces and using all lowercase, except capitalizing all words after the first, like priceAtOpening

Use the choice that fits your taste (or the taste or convention of the people you are working with).

Table Of Contents

  • 1.6.1. Literals and Identifiers

Previous topic

1.5. Strings, Part I

1.7. Print Function, Part I

  • Show Source

Quick search

Enter search terms or a module, class or function name.

Python Variables and Assignment

Python variables, variable assignment rules, every value has a type, memory and the garbage collector, variable swap, variable names are superficial labels, assignment = is shallow, decomp by var.

Python Function Examples – How to Declare and Invoke with Parameters

Dionysia Lemonaki

Python has a bunch of helpful built-in functions you can use to do all sorts of stuff. And each one performs a specific task.

But did you know that Python also allows you to define your own functions?

This article will show you how to create and call your own Python functions. It will also give you an overview of how to pass input parameters and arguments to your functions.

What is a Function?

A function is an isolated block of code that performs a specific task.

Functions are useful in programming because they eliminate needless and excessive copying and pasting of code throught a program.

If a certain action is required often and in different places, that is a good indicator that you can write a function for it. Functions are meant to be reusable.

Functions also help organize your code.

If you need to make a change, you'll only need to update that certain function. This saves you from having to search for different pieces of the same code that have been scattered in different locations in your program by copying and pasting.

This complies with the DRY (Don't Repeat Yourself) principle in software development.

The code inside a function runs only when they the function is called.

Functions can accept arguments and defaults and may or not return values back to the caller once the code has run.

How to Define a Function in Python

The general syntax for creating a function in Python looks something like this:

Let's break down what's happening here:

  • def is a keyword that tells Python a new function is being defined.
  • Next comes a valid function name of your choosing. Valid names start with a letter or underscore but can include numbers. Words are lowercase and separated by underscores. It's important to know that function names can't be a Python reserved keyword.
  • Then we have a set of opening and closing parentheses, () . Inside them, there can be zero, one, or more optional comma separated parameters with their optional default values. These are passed to the function.
  • Next is a colon, : , which ends the function's definition line.
  • Then there's a new line followed by a level of indentation (you can do this with 4 spaces using your keyboard or with 1 Tab instead). Indentation is important since it lets Python know what code will belong in the function.
  • Then we have the function's body. Here goes the code to be executed – the contents with the actions to be taken when the function is called.
  • Finally, there's an optional return statement in the function's body, passing back a value to the caller when the function is exited.

Keep in mind that if you forget the parentheses () or the colon : when trying to define a new function, Python will let you know with a SyntaxError .

How to Define and Call a Basic Function in Python

Below is an example of a basic function that has no return statement and doesn't take in any parameters.

It just prints hello world whenever it is called.

Once you've defined a function, the code will not run on its own.

To execute the code inside the function, you have make a function invokation or else a function call .

You can then call the function as many times as you want.

To call a function you need to do this:

Here's a breakdown of the code:

  • Type the function name.
  • The function name has to be followed by parentheses. If there are any required arguments, they have to be passed in the parentheses. If the function doesn't take in any arguments, you still need the parentheses.

To call the function from the example above, which doesn't take in any arguments, do the following:

How to Define and Call Functions with Parameters

So far you've seen simple functions that don't really do much besides printing something to the console.

What if you want to pass in some extra data to the function?

We've used terms here like parameter and arguments .

What are their definitions exactly?

Parameters are a named placeholder that pass information into functions.

They act as variables that are defined locally in the function's definition line.

In the example above, there is one parameter, name .

We could've used f-string formatting instead – it works the same way as the previous example:

There can be a list of parameters inside the parentheses, all separated by commas.

Here, the parameters in the function are name and age .

When a function is called, arguments are passed in.

Arguments, like parameters, are information passed to functions.

In particular, they are the actual values that correspond to the parameters in the function definition.

Calling the function from a previous example and passing in an argument would look like this:

The function can be called many times, passing in different values each time.

The arguments presented so far are called positional arguments .

All positional arguments are very much required .

The number of positional arguments matters

When calling functions, you need to pass the correct number of arguments, otherwise there will be an error.

When it comes to positional arguments, the number of arguments passed in to the function call has to be exactly the same as the number of parameters in the function's definition.

You can't leave any out or add in any more.

Say that you have this function that takes in two parameters:

If the function is called with just one argument passed in, like this, there will be an error:

If the function is called with three arguments passed in, there will again be an error:

There will also be an error if we pass in no arguments.

Instead, we need two arguments, since two parameters are listed in the function definition.

The order of positional arguments matters

Besides including the correct number of arguments, it is important to note that the order in which the arguments are passed in matters.

Arguments need to be passed in the exact same order as the order of the parameters that have been declared in the function's definition.

It works like variable assignment.

The first argument is the value of the first parameter in the function's definition. The second argument is the value of the second parameter in the function's ddefinition – and so on.

If the order is not correct, the output might not make much sense and not be what you intended it to be.

How to use keyword arguments in Python functions

So far you've seen one type of argument, positional arguments.

With positional arguments, functions are called with just the values passed in the function call. There, each value directly corresponds with the number, order, and position of each parameter in the function definition.

However, functions in Python can accept another type of argument, that is keyword arguments .

In this case, instead of just passing in values in the function call, you instead specify the name of the parameter and then the value you want to assign it, in the form of key = value .

Each key matches each parameter in the function definition.

Explicitely calling the name of parameters and the values they take helps in being extra clear about what you're passing in and avoids any possible confusion.

Keyword arguments, as seen above, can be in a particular order. But they are more flexible than positional arguments in the sense that order of arguments now does not matter.

So you could do this and there would be no errors:

But you still have to give the correct number of arguments.

You can use both positional and keyword arguments together in a function call, like the example below where there is one positional argument and one keyword argument:

In this case, order matters again.

Positional arguments always come first and all keyword arguments should follow the positional arguments. Otherwise there will be an error:

How to define a parameter with a default value in Python

Function arguments can also have default values. They are known as default or optional arguments .

For a function argument to have a default value, you have to assign a default value to the parameter in the function's definition.

You do this with the key=value form, where value will be the default value for that parameter.

As you see, default arguments are not required in the function call, and the value of language will always default to Python if another value isn't provided in the call.

However, default values can be easily overriden if you provide another value in the function's call:

There can be more than one default value passed to the function.

When the function is called, none, one, some, or all of the default arguments can be provided and order does not matter.

Default arguments can be combined with non-default arguments in the function's call.

Here is a function that accepts two arguments: one positional, non-default ( name ) and one optional, default ( language ).

The default argument, langyage="Python" , is optional . But the positional argument, name , will always always required. If another language is not passed in, the value will always default to Python.

Another thing to mention here is that, when defaults and non defaults are used together, the order they are defined in the function defintion matters.

All the positional arguments go first and are followed by the default arguments.

This will not work:

In this article, you learned how to declare functions and invoke them with parameters in the Python programming language.

This was an introduction on how to create simple functions and how to pass data into them, with parameters. We also went over the different types of arguments like positional , keyword , and default arguments.

  • The order and number of positional arguments matters.
  • With keyword arguments, order does not matter. Number does matter, though, since each keyword argument corresponds with each parameter in the function's definition.
  • Default arguments are entirely optional. You can pass in all of them, some of them, or none at all.

If you are interested in going more in-depth and learning more about the Python programming language, freeCodeCamp has a free Python certification .

You'll start from the basics and fundamentals of the language and then progress to more advanced concepts like data structures and relational databases. In the end you'll build 5 projects to put to practice what you've learnt.

Thank you for reading and happy learning.

Read more posts .

If this article was helpful, share it .

Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. Get started

Image by Mohammad Rahmani

The FinAnalytics

Screenshot 2022-12-23 at 9.45_edited.jpg

Financial Book

Interview guide, recommendations, understanding variables in python: declaration, assignment, and naming conventions.

Variables are essential elements in programming languages like Python. they serve as containers to store data values that can be manipulated or referenced in a program. Understanding how variables are declared, assigned values, and named according to conventions is crucial for writing clean and readable Python code.

assignment declaration python

Variable Declaration and Assignment in Python

In Python, variables are declared by simply assigning a value to them. Unlike some other programming languages, Python does not require explicit declaration of variables or specifying their data types. the variable's data type is inferred based on the value assigned to it. this process is known as variable assignment, which can be done using the equals sign "=" (also known as the assignment operator). Once the value is assigned to a variable, it is created, and we can start using it in other statements or expressions.

for instance, the following code snippet creates a variable named " stockPrice " and assigns the value 100 (an integer) to it.

assignment declaration python

the variable stockPrice is now created and holds the value 100 . You can use it in other expressions or statements as shown below.

assignment declaration python

Variables in Python can be reassigned to different values, allowing for dynamic changes in a program. A variable's value can be updated by simply assigning a new value to it. You can re-declare a variable by assigning a new value to it. for instance, we can change the value of the " stockPrice " variable to 150 as follows:

assignment declaration python

You can also assign the values to multiple variables simultaneously using the chaining assignment operation as shown below.

assignment declaration python

the simultaneous assignment operation in Python provides programmers with a concise and efficient way to assign values to multiple variables in a single line of code.

Naming Conventions for Variables

While naming variables in Python, it is essential to follow proper naming conventions for code clarity and maintainability. Here are some guidelines to follow when naming variables:

Start with a Letter or Underscore: Variable names must begin with a letter (a-z, A-Z) or an underscore (_) character. for example: "stockPrice" or "_stockPrice" are acceptable variable names.

Avoid Starting with a Number: Variable names cannot begin with a number. for example: "1stockPrice" or "1_stockPrice" are invalid variable names.

Use Alphanumeric Characters and Underscores: Variable names can only contain alphanumeric characters (A-Z, a-z, 0-9) and underscores (_). for example: "stockPrice", "stock_Price", "stockPrice_1", and "stockPrice_2" are all valid variable names.

Avoid Whitespace and Special Characters: Variable names should not contain whitespace or special characters such as +, -, etc. for example: "stock price" or "stock-price" are invalid variable names.

Case Sensitive: Variable names are case-sensitive. for example: "StockPrice", "stockPrice", and "Stockprice" are considered distinct variables.

Avoid Python Keywords: Avoid using Python keywords as variable names. for example: keywords such as "str", "is", and "for" cannot be used as variable names as they are reserved keywords in Python.

In addition to these guidelines, professional programmers follow certain conventions to enhance code readability (best practices). these practices include using a name that describes the purpose " stockPrice ", instead of using dummy or temporary names " temp ". It's also common practice to separate words in variable names with underscores " stock_price ", and start variable names with lowercase letters " stockPrice ".

Following these guidelines and practices can make your code more readable and maintainable. Remember that these are good coding practices recommended by professional programmers, which can be applied to any programming language.

  • Python »
  • 3.12.4 Documentation »
  • The Python Standard Library »
  • Development Tools »
  • typing — Support for type hints
  • Theme Auto Light Dark |

typing — Support for type hints ¶

Added in version 3.5.

Source code: Lib/typing.py

The Python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers , IDEs, linters, etc.

This module provides runtime support for type hints.

Consider the function below:

The function moon_weight takes an argument expected to be an instance of float , as indicated by the type hint earth_weight: float . The function is expected to return an instance of str , as indicated by the -> str hint.

While type hints can be simple classes like float or str , they can also be more complex. The typing module provides a vocabulary of more advanced type hints.

New features are frequently added to the typing module. The typing_extensions package provides backports of these new features to older versions of Python.

A quick overview of type hints (hosted at the mypy docs)

The Python typing system is standardised via PEPs, so this reference should broadly apply to most Python type checkers. (Some parts may still be specific to mypy.)

Type-checker-agnostic documentation written by the community detailing type system features, useful typing related tools and typing best practices.

Specification for the Python Type System ¶

The canonical, up-to-date specification of the Python type system can be found at “Specification for the Python type system” .

Type aliases ¶

A type alias is defined using the type statement, which creates an instance of TypeAliasType . In this example, Vector and list[float] will be treated equivalently by static type checkers:

Type aliases are useful for simplifying complex type signatures. For example:

The type statement is new in Python 3.12. For backwards compatibility, type aliases can also be created through simple assignment:

Or marked with TypeAlias to make it explicit that this is a type alias, not a normal variable assignment:

Use the NewType helper to create distinct types:

The static type checker will treat the new type as if it were a subclass of the original type. This is useful in helping catch logical errors:

You may still perform all int operations on a variable of type UserId , but the result will always be of type int . This lets you pass in a UserId wherever an int might be expected, but will prevent you from accidentally creating a UserId in an invalid way:

Note that these checks are enforced only by the static type checker. At runtime, the statement Derived = NewType('Derived', Base) will make Derived a callable that immediately returns whatever parameter you pass it. That means the expression Derived(some_value) does not create a new class or introduce much overhead beyond that of a regular function call.

More precisely, the expression some_value is Derived(some_value) is always true at runtime.

It is invalid to create a subtype of Derived :

However, it is possible to create a NewType based on a ‘derived’ NewType :

and typechecking for ProUserId will work as expected.

See PEP 484 for more details.

Recall that the use of a type alias declares two types to be equivalent to one another. Doing type Alias = Original will make the static type checker treat Alias as being exactly equivalent to Original in all cases. This is useful when you want to simplify complex type signatures.

In contrast, NewType declares one type to be a subtype of another. Doing Derived = NewType('Derived', Original) will make the static type checker treat Derived as a subclass of Original , which means a value of type Original cannot be used in places where a value of type Derived is expected. This is useful when you want to prevent logic errors with minimal runtime cost.

Added in version 3.5.2.

Changed in version 3.10: NewType is now a class rather than a function. As a result, there is some additional runtime cost when calling NewType over a regular function.

Changed in version 3.11: The performance of calling NewType has been restored to its level in Python 3.9.

Annotating callable objects ¶

Functions – or other callable objects – can be annotated using collections.abc.Callable or typing.Callable . Callable[[int], str] signifies a function that takes a single parameter of type int and returns a str .

For example:

The subscription syntax must always be used with exactly two values: the argument list and the return type. The argument list must be a list of types, a ParamSpec , Concatenate , or an ellipsis. The return type must be a single type.

If a literal ellipsis ... is given as the argument list, it indicates that a callable with any arbitrary parameter list would be acceptable:

Callable cannot express complex signatures such as functions that take a variadic number of arguments, overloaded functions , or functions that have keyword-only parameters. However, these signatures can be expressed by defining a Protocol class with a __call__() method:

Callables which take other callables as arguments may indicate that their parameter types are dependent on each other using ParamSpec . Additionally, if that callable adds or removes arguments from other callables, the Concatenate operator may be used. They take the form Callable[ParamSpecVariable, ReturnType] and Callable[Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable], ReturnType] respectively.

Changed in version 3.10: Callable now supports ParamSpec and Concatenate . See PEP 612 for more details.

The documentation for ParamSpec and Concatenate provides examples of usage in Callable .

Since type information about objects kept in containers cannot be statically inferred in a generic way, many container classes in the standard library support subscription to denote the expected types of container elements.

Generic functions and classes can be parameterized by using type parameter syntax :

Or by using the TypeVar factory directly:

Changed in version 3.12: Syntactic support for generics is new in Python 3.12.

Annotating tuples ¶

For most containers in Python, the typing system assumes that all elements in the container will be of the same type. For example:

list only accepts one type argument, so a type checker would emit an error on the y assignment above. Similarly, Mapping only accepts two type arguments: the first indicates the type of the keys, and the second indicates the type of the values.

Unlike most other Python containers, however, it is common in idiomatic Python code for tuples to have elements which are not all of the same type. For this reason, tuples are special-cased in Python’s typing system. tuple accepts any number of type arguments:

To denote a tuple which could be of any length, and in which all elements are of the same type T , use tuple[T, ...] . To denote an empty tuple, use tuple[()] . Using plain tuple as an annotation is equivalent to using tuple[Any, ...] :

The type of class objects ¶

A variable annotated with C may accept a value of type C . In contrast, a variable annotated with type[C] (or typing.Type[C] ) may accept values that are classes themselves – specifically, it will accept the class object of C . For example:

Note that type[C] is covariant:

The only legal parameters for type are classes, Any , type variables , and unions of any of these types. For example:

type[Any] is equivalent to type , which is the root of Python’s metaclass hierarchy .

User-defined generic types ¶

A user-defined class can be defined as a generic class.

This syntax indicates that the class LoggedVar is parameterised around a single type variable T . This also makes T valid as a type within the class body.

Generic classes implicitly inherit from Generic . For compatibility with Python 3.11 and lower, it is also possible to inherit explicitly from Generic to indicate a generic class:

Generic classes have __class_getitem__() methods, meaning they can be parameterised at runtime (e.g. LoggedVar[int] below):

A generic type can have any number of type variables. All varieties of TypeVar are permissible as parameters for a generic type:

Each type variable argument to Generic must be distinct. This is thus invalid:

Generic classes can also inherit from other classes:

When inheriting from generic classes, some type parameters could be fixed:

In this case MyDict has a single parameter, T .

Using a generic class without specifying type parameters assumes Any for each position. In the following example, MyIterable is not generic but implicitly inherits from Iterable[Any] :

User-defined generic type aliases are also supported. Examples:

For backward compatibility, generic type aliases can also be created through a simple assignment:

Changed in version 3.7: Generic no longer has a custom metaclass.

Changed in version 3.12: Syntactic support for generics and type aliases is new in version 3.12. Previously, generic classes had to explicitly inherit from Generic or contain a type variable in one of their bases.

User-defined generics for parameter expressions are also supported via parameter specification variables in the form [**P] . The behavior is consistent with type variables’ described above as parameter specification variables are treated by the typing module as a specialized type variable. The one exception to this is that a list of types can be used to substitute a ParamSpec :

Classes generic over a ParamSpec can also be created using explicit inheritance from Generic . In this case, ** is not used:

Another difference between TypeVar and ParamSpec is that a generic with only one parameter specification variable will accept parameter lists in the forms X[[Type1, Type2, ...]] and also X[Type1, Type2, ...] for aesthetic reasons. Internally, the latter is converted to the former, so the following are equivalent:

Note that generics with ParamSpec may not have correct __parameters__ after substitution in some cases because they are intended primarily for static type checking.

Changed in version 3.10: Generic can now be parameterized over parameter expressions. See ParamSpec and PEP 612 for more details.

A user-defined generic class can have ABCs as base classes without a metaclass conflict. Generic metaclasses are not supported. The outcome of parameterizing generics is cached, and most types in the typing module are hashable and comparable for equality.

The Any type ¶

A special kind of type is Any . A static type checker will treat every type as being compatible with Any and Any as being compatible with every type.

This means that it is possible to perform any operation or method call on a value of type Any and assign it to any variable:

Notice that no type checking is performed when assigning a value of type Any to a more precise type. For example, the static type checker did not report an error when assigning a to s even though s was declared to be of type str and receives an int value at runtime!

Furthermore, all functions without a return type or parameter types will implicitly default to using Any :

This behavior allows Any to be used as an escape hatch when you need to mix dynamically and statically typed code.

Contrast the behavior of Any with the behavior of object . Similar to Any , every type is a subtype of object . However, unlike Any , the reverse is not true: object is not a subtype of every other type.

That means when the type of a value is object , a type checker will reject almost all operations on it, and assigning it to a variable (or using it as a return value) of a more specialized type is a type error. For example:

Use object to indicate that a value could be any type in a typesafe manner. Use Any to indicate that a value is dynamically typed.

Nominal vs structural subtyping ¶

Initially PEP 484 defined the Python static type system as using nominal subtyping . This means that a class A is allowed where a class B is expected if and only if A is a subclass of B .

This requirement previously also applied to abstract base classes, such as Iterable . The problem with this approach is that a class had to be explicitly marked to support them, which is unpythonic and unlike what one would normally do in idiomatic dynamically typed Python code. For example, this conforms to PEP 484 :

PEP 544 allows to solve this problem by allowing users to write the above code without explicit base classes in the class definition, allowing Bucket to be implicitly considered a subtype of both Sized and Iterable[int] by static type checkers. This is known as structural subtyping (or static duck-typing):

Moreover, by subclassing a special class Protocol , a user can define new custom protocols to fully enjoy structural subtyping (see examples below).

Module contents ¶

The typing module defines the following classes, functions and decorators.

Special typing primitives ¶

Special types ¶.

These can be used as types in annotations. They do not support subscription using [] .

Special type indicating an unconstrained type.

Every type is compatible with Any .

Any is compatible with every type.

Changed in version 3.11: Any can now be used as a base class. This can be useful for avoiding type checker errors with classes that can duck type anywhere or are highly dynamic.

A constrained type variable .

Definition:

AnyStr is meant to be used for functions that may accept str or bytes arguments but cannot allow the two to mix.

Note that, despite its name, AnyStr has nothing to do with the Any type, nor does it mean “any string”. In particular, AnyStr and str | bytes are different from each other and have different use cases:

Special type that includes only literal strings.

Any string literal is compatible with LiteralString , as is another LiteralString . However, an object typed as just str is not. A string created by composing LiteralString -typed objects is also acceptable as a LiteralString .

LiteralString is useful for sensitive APIs where arbitrary user-generated strings could generate problems. For example, the two cases above that generate type checker errors could be vulnerable to an SQL injection attack.

See PEP 675 for more details.

Added in version 3.11.

Never and NoReturn represent the bottom type , a type that has no members.

They can be used to indicate that a function never returns, such as sys.exit() :

Or to define a function that should never be called, as there are no valid arguments, such as assert_never() :

Never and NoReturn have the same meaning in the type system and static type checkers treat both equivalently.

Added in version 3.6.2: Added NoReturn .

Added in version 3.11: Added Never .

Special type to represent the current enclosed class.

This annotation is semantically equivalent to the following, albeit in a more succinct fashion:

In general, if something returns self , as in the above examples, you should use Self as the return annotation. If Foo.return_self was annotated as returning "Foo" , then the type checker would infer the object returned from SubclassOfFoo.return_self as being of type Foo rather than SubclassOfFoo .

Other common use cases include:

classmethod s that are used as alternative constructors and return instances of the cls parameter.

Annotating an __enter__() method which returns self.

You should not use Self as the return annotation if the method is not guaranteed to return an instance of a subclass when the class is subclassed:

See PEP 673 for more details.

Special annotation for explicitly declaring a type alias .

TypeAlias is particularly useful on older Python versions for annotating aliases that make use of forward references, as it can be hard for type checkers to distinguish these from normal variable assignments:

See PEP 613 for more details.

Added in version 3.10.

Deprecated since version 3.12: TypeAlias is deprecated in favor of the type statement, which creates instances of TypeAliasType and which natively supports forward references. Note that while TypeAlias and TypeAliasType serve similar purposes and have similar names, they are distinct and the latter is not the type of the former. Removal of TypeAlias is not currently planned, but users are encouraged to migrate to type statements.

Special forms ¶

These can be used as types in annotations. They all support subscription using [] , but each has a unique syntax.

Union type; Union[X, Y] is equivalent to X | Y and means either X or Y.

To define a union, use e.g. Union[int, str] or the shorthand int | str . Using that shorthand is recommended. Details:

The arguments must be types and there must be at least one.

Unions of unions are flattened, e.g.:

Unions of a single argument vanish, e.g.:

Redundant arguments are skipped, e.g.:

When comparing unions, the argument order is ignored, e.g.:

You cannot subclass or instantiate a Union .

You cannot write Union[X][Y] .

Changed in version 3.7: Don’t remove explicit subclasses from unions at runtime.

Changed in version 3.10: Unions can now be written as X | Y . See union type expressions .

Optional[X] is equivalent to X | None (or Union[X, None] ).

Note that this is not the same concept as an optional argument, which is one that has a default. An optional argument with a default does not require the Optional qualifier on its type annotation just because it is optional. For example:

On the other hand, if an explicit value of None is allowed, the use of Optional is appropriate, whether the argument is optional or not. For example:

Changed in version 3.10: Optional can now be written as X | None . See union type expressions .

Special form for annotating higher-order functions.

Concatenate can be used in conjunction with Callable and ParamSpec to annotate a higher-order callable which adds, removes, or transforms parameters of another callable. Usage is in the form Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable] . Concatenate is currently only valid when used as the first argument to a Callable . The last parameter to Concatenate must be a ParamSpec or ellipsis ( ... ).

For example, to annotate a decorator with_lock which provides a threading.Lock to the decorated function, Concatenate can be used to indicate that with_lock expects a callable which takes in a Lock as the first argument, and returns a callable with a different type signature. In this case, the ParamSpec indicates that the returned callable’s parameter types are dependent on the parameter types of the callable being passed in:

PEP 612 – Parameter Specification Variables (the PEP which introduced ParamSpec and Concatenate )

  • Annotating callable objects

Special typing form to define “literal types”.

Literal can be used to indicate to type checkers that the annotated object has a value equivalent to one of the provided literals.

Literal[...] cannot be subclassed. At runtime, an arbitrary value is allowed as type argument to Literal[...] , but type checkers may impose restrictions. See PEP 586 for more details about literal types.

Added in version 3.8.

Changed in version 3.9.1: Literal now de-duplicates parameters. Equality comparisons of Literal objects are no longer order dependent. Literal objects will now raise a TypeError exception during equality comparisons if one of their parameters are not hashable .

Special type construct to mark class variables.

As introduced in PEP 526 , a variable annotation wrapped in ClassVar indicates that a given attribute is intended to be used as a class variable and should not be set on instances of that class. Usage:

ClassVar accepts only types and cannot be further subscribed.

ClassVar is not a class itself, and should not be used with isinstance() or issubclass() . ClassVar does not change Python runtime behavior, but it can be used by third-party type checkers. For example, a type checker might flag the following code as an error:

Added in version 3.5.3.

Special typing construct to indicate final names to type checkers.

Final names cannot be reassigned in any scope. Final names declared in class scopes cannot be overridden in subclasses.

There is no runtime checking of these properties. See PEP 591 for more details.

Special typing construct to mark a TypedDict key as required.

This is mainly useful for total=False TypedDicts. See TypedDict and PEP 655 for more details.

Special typing construct to mark a TypedDict key as potentially missing.

See TypedDict and PEP 655 for more details.

Special typing form to add context-specific metadata to an annotation.

Add metadata x to a given type T by using the annotation Annotated[T, x] . Metadata added using Annotated can be used by static analysis tools or at runtime. At runtime, the metadata is stored in a __metadata__ attribute.

If a library or tool encounters an annotation Annotated[T, x] and has no special logic for the metadata, it should ignore the metadata and simply treat the annotation as T . As such, Annotated can be useful for code that wants to use annotations for purposes outside Python’s static typing system.

Using Annotated[T, x] as an annotation still allows for static typechecking of T , as type checkers will simply ignore the metadata x . In this way, Annotated differs from the @no_type_check decorator, which can also be used for adding annotations outside the scope of the typing system, but completely disables typechecking for a function or class.

The responsibility of how to interpret the metadata lies with the tool or library encountering an Annotated annotation. A tool or library encountering an Annotated type can scan through the metadata elements to determine if they are of interest (e.g., using isinstance() ).

Here is an example of how you might use Annotated to add metadata to type annotations if you were doing range analysis:

Details of the syntax:

The first argument to Annotated must be a valid type

Multiple metadata elements can be supplied ( Annotated supports variadic arguments):

It is up to the tool consuming the annotations to decide whether the client is allowed to add multiple metadata elements to one annotation and how to merge those annotations.

Annotated must be subscripted with at least two arguments ( Annotated[int] is not valid)

The order of the metadata elements is preserved and matters for equality checks:

Nested Annotated types are flattened. The order of the metadata elements starts with the innermost annotation:

Duplicated metadata elements are not removed:

Annotated can be used with nested and generic aliases:

Annotated cannot be used with an unpacked TypeVarTuple :

This would be equivalent to:

where T1 , T2 , etc. are TypeVars . This would be invalid: only one type should be passed to Annotated.

By default, get_type_hints() strips the metadata from annotations. Pass include_extras=True to have the metadata preserved:

At runtime, the metadata associated with an Annotated type can be retrieved via the __metadata__ attribute:

The PEP introducing Annotated to the standard library.

Added in version 3.9.

Special typing construct for marking user-defined type guard functions.

TypeGuard can be used to annotate the return type of a user-defined type guard function. TypeGuard only accepts a single type argument. At runtime, functions marked this way should return a boolean.

TypeGuard aims to benefit type narrowing – a technique used by static type checkers to determine a more precise type of an expression within a program’s code flow. Usually type narrowing is done by analyzing conditional code flow and applying the narrowing to a block of code. The conditional expression here is sometimes referred to as a “type guard”:

Sometimes it would be convenient to use a user-defined boolean function as a type guard. Such a function should use TypeGuard[...] as its return type to alert static type checkers to this intention.

Using -> TypeGuard tells the static type checker that for a given function:

The return value is a boolean.

If the return value is True , the type of its argument is the type inside TypeGuard .

If is_str_list is a class or instance method, then the type in TypeGuard maps to the type of the second parameter after cls or self .

In short, the form def foo(arg: TypeA) -> TypeGuard[TypeB]: ... , means that if foo(arg) returns True , then arg narrows from TypeA to TypeB .

TypeB need not be a narrower form of TypeA – it can even be a wider form. The main reason is to allow for things like narrowing list[object] to list[str] even though the latter is not a subtype of the former, since list is invariant. The responsibility of writing type-safe type guards is left to the user.

TypeGuard also works with type variables. See PEP 647 for more details.

Typing operator to conceptually mark an object as having been unpacked.

For example, using the unpack operator * on a type variable tuple is equivalent to using Unpack to mark the type variable tuple as having been unpacked:

In fact, Unpack can be used interchangeably with * in the context of typing.TypeVarTuple and builtins.tuple types. You might see Unpack being used explicitly in older versions of Python, where * couldn’t be used in certain places:

Unpack can also be used along with typing.TypedDict for typing **kwargs in a function signature:

See PEP 692 for more details on using Unpack for **kwargs typing.

Building generic types and type aliases ¶

The following classes should not be used directly as annotations. Their intended purpose is to be building blocks for creating generic types and type aliases.

These objects can be created through special syntax ( type parameter lists and the type statement). For compatibility with Python 3.11 and earlier, they can also be created without the dedicated syntax, as documented below.

Abstract base class for generic types.

A generic type is typically declared by adding a list of type parameters after the class name:

Such a class implicitly inherits from Generic . The runtime semantics of this syntax are discussed in the Language Reference .

This class can then be used as follows:

Here the brackets after the function name indicate a generic function .

For backwards compatibility, generic classes can also be declared by explicitly inheriting from Generic . In this case, the type parameters must be declared separately:

Type variable.

The preferred way to construct a type variable is via the dedicated syntax for generic functions , generic classes , and generic type aliases :

This syntax can also be used to create bound and constrained type variables:

However, if desired, reusable type variables can also be constructed manually, like so:

Type variables exist primarily for the benefit of static type checkers. They serve as the parameters for generic types as well as for generic function and type alias definitions. See Generic for more information on generic types. Generic functions work as follows:

Note that type variables can be bound , constrained , or neither, but cannot be both bound and constrained.

The variance of type variables is inferred by type checkers when they are created through the type parameter syntax or when infer_variance=True is passed. Manually created type variables may be explicitly marked covariant or contravariant by passing covariant=True or contravariant=True . By default, manually created type variables are invariant. See PEP 484 and PEP 695 for more details.

Bound type variables and constrained type variables have different semantics in several important ways. Using a bound type variable means that the TypeVar will be solved using the most specific type possible:

Type variables can be bound to concrete types, abstract types (ABCs or protocols), and even unions of types:

Using a constrained type variable, however, means that the TypeVar can only ever be solved as being exactly one of the constraints given:

At runtime, isinstance(x, T) will raise TypeError .

The name of the type variable.

Whether the type var has been explicitly marked as covariant.

Whether the type var has been explicitly marked as contravariant.

Whether the type variable’s variance should be inferred by type checkers.

Added in version 3.12.

The bound of the type variable, if any.

Changed in version 3.12: For type variables created through type parameter syntax , the bound is evaluated only when the attribute is accessed, not when the type variable is created (see Lazy evaluation ).

A tuple containing the constraints of the type variable, if any.

Changed in version 3.12: For type variables created through type parameter syntax , the constraints are evaluated only when the attribute is accessed, not when the type variable is created (see Lazy evaluation ).

Changed in version 3.12: Type variables can now be declared using the type parameter syntax introduced by PEP 695 . The infer_variance parameter was added.

Type variable tuple. A specialized form of type variable that enables variadic generics.

Type variable tuples can be declared in type parameter lists using a single asterisk ( * ) before the name:

Or by explicitly invoking the TypeVarTuple constructor:

A normal type variable enables parameterization with a single type. A type variable tuple, in contrast, allows parameterization with an arbitrary number of types by acting like an arbitrary number of type variables wrapped in a tuple. For example:

Note the use of the unpacking operator * in tuple[T, *Ts] . Conceptually, you can think of Ts as a tuple of type variables (T1, T2, ...) . tuple[T, *Ts] would then become tuple[T, *(T1, T2, ...)] , which is equivalent to tuple[T, T1, T2, ...] . (Note that in older versions of Python, you might see this written using Unpack instead, as Unpack[Ts] .)

Type variable tuples must always be unpacked. This helps distinguish type variable tuples from normal type variables:

Type variable tuples can be used in the same contexts as normal type variables. For example, in class definitions, arguments, and return types:

Type variable tuples can be happily combined with normal type variables:

However, note that at most one type variable tuple may appear in a single list of type arguments or type parameters:

Finally, an unpacked type variable tuple can be used as the type annotation of *args :

In contrast to non-unpacked annotations of *args - e.g. *args: int , which would specify that all arguments are int - *args: *Ts enables reference to the types of the individual arguments in *args . Here, this allows us to ensure the types of the *args passed to call_soon match the types of the (positional) arguments of callback .

See PEP 646 for more details on type variable tuples.

The name of the type variable tuple.

Changed in version 3.12: Type variable tuples can now be declared using the type parameter syntax introduced by PEP 695 .

Parameter specification variable. A specialized version of type variables .

In type parameter lists , parameter specifications can be declared with two asterisks ( ** ):

For compatibility with Python 3.11 and earlier, ParamSpec objects can also be created as follows:

Parameter specification variables exist primarily for the benefit of static type checkers. They are used to forward the parameter types of one callable to another callable – a pattern commonly found in higher order functions and decorators. They are only valid when used in Concatenate , or as the first argument to Callable , or as parameters for user-defined Generics. See Generic for more information on generic types.

For example, to add basic logging to a function, one can create a decorator add_logging to log function calls. The parameter specification variable tells the type checker that the callable passed into the decorator and the new callable returned by it have inter-dependent type parameters:

Without ParamSpec , the simplest way to annotate this previously was to use a TypeVar with bound Callable[..., Any] . However this causes two problems:

The type checker can’t type check the inner function because *args and **kwargs have to be typed Any .

cast() may be required in the body of the add_logging decorator when returning the inner function, or the static type checker must be told to ignore the return inner .

Since ParamSpec captures both positional and keyword parameters, P.args and P.kwargs can be used to split a ParamSpec into its components. P.args represents the tuple of positional parameters in a given call and should only be used to annotate *args . P.kwargs represents the mapping of keyword parameters to their values in a given call, and should be only be used to annotate **kwargs . Both attributes require the annotated parameter to be in scope. At runtime, P.args and P.kwargs are instances respectively of ParamSpecArgs and ParamSpecKwargs .

The name of the parameter specification.

Parameter specification variables created with covariant=True or contravariant=True can be used to declare covariant or contravariant generic types. The bound argument is also accepted, similar to TypeVar . However the actual semantics of these keywords are yet to be decided.

Changed in version 3.12: Parameter specifications can now be declared using the type parameter syntax introduced by PEP 695 .

Only parameter specification variables defined in global scope can be pickled.

Concatenate

Arguments and keyword arguments attributes of a ParamSpec . The P.args attribute of a ParamSpec is an instance of ParamSpecArgs , and P.kwargs is an instance of ParamSpecKwargs . They are intended for runtime introspection and have no special meaning to static type checkers.

Calling get_origin() on either of these objects will return the original ParamSpec :

The type of type aliases created through the type statement.

The name of the type alias:

The module in which the type alias was defined:

The type parameters of the type alias, or an empty tuple if the alias is not generic:

The type alias’s value. This is lazily evaluated , so names used in the definition of the alias are not resolved until the __value__ attribute is accessed:

Other special directives ¶

These functions and classes should not be used directly as annotations. Their intended purpose is to be building blocks for creating and declaring types.

Typed version of collections.namedtuple() .

This is equivalent to:

To give a field a default value, you can assign to it in the class body:

Fields with a default value must come after any fields without a default.

The resulting class has an extra attribute __annotations__ giving a dict that maps the field names to the field types. (The field names are in the _fields attribute and the default values are in the _field_defaults attribute, both of which are part of the namedtuple() API.)

NamedTuple subclasses can also have docstrings and methods:

NamedTuple subclasses can be generic:

Backward-compatible usage:

Changed in version 3.6: Added support for PEP 526 variable annotation syntax.

Changed in version 3.6.1: Added support for default values, methods, and docstrings.

Changed in version 3.8: The _field_types and __annotations__ attributes are now regular dictionaries instead of instances of OrderedDict .

Changed in version 3.9: Removed the _field_types attribute in favor of the more standard __annotations__ attribute which has the same information.

Changed in version 3.11: Added support for generic namedtuples.

Helper class to create low-overhead distinct types .

A NewType is considered a distinct type by a typechecker. At runtime, however, calling a NewType returns its argument unchanged.

The module in which the new type is defined.

The name of the new type.

The type that the new type is based on.

Changed in version 3.10: NewType is now a class rather than a function.

Base class for protocol classes.

Protocol classes are defined like this:

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing), for example:

See PEP 544 for more details. Protocol classes decorated with runtime_checkable() (described later) act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures.

Protocol classes can be generic, for example:

In code that needs to be compatible with Python 3.11 or older, generic Protocols can be written as follows:

Mark a protocol class as a runtime protocol.

Such a protocol can be used with isinstance() and issubclass() . This raises TypeError when applied to a non-protocol class. This allows a simple-minded structural check, very similar to “one trick ponies” in collections.abc such as Iterable . For example:

runtime_checkable() will check only the presence of the required methods or attributes, not their type signatures or types. For example, ssl.SSLObject is a class, therefore it passes an issubclass() check against Callable . However, the ssl.SSLObject.__init__ method exists only to raise a TypeError with a more informative message, therefore making it impossible to call (instantiate) ssl.SSLObject .

An isinstance() check against a runtime-checkable protocol can be surprisingly slow compared to an isinstance() check against a non-protocol class. Consider using alternative idioms such as hasattr() calls for structural checks in performance-sensitive code.

Changed in version 3.12: The internal implementation of isinstance() checks against runtime-checkable protocols now uses inspect.getattr_static() to look up attributes (previously, hasattr() was used). As a result, some objects which used to be considered instances of a runtime-checkable protocol may no longer be considered instances of that protocol on Python 3.12+, and vice versa. Most users are unlikely to be affected by this change.

Changed in version 3.12: The members of a runtime-checkable protocol are now considered “frozen” at runtime as soon as the class has been created. Monkey-patching attributes onto a runtime-checkable protocol will still work, but will have no impact on isinstance() checks comparing objects to the protocol. See “What’s new in Python 3.12” for more details.

Special construct to add type hints to a dictionary. At runtime it is a plain dict .

TypedDict declares a dictionary type that expects all of its instances to have a certain set of keys, where each key is associated with a value of a consistent type. This expectation is not checked at runtime but is only enforced by type checkers. Usage:

To allow using this feature with older versions of Python that do not support PEP 526 , TypedDict supports two additional equivalent syntactic forms:

Using a literal dict as the second argument:

Using keyword arguments:

Deprecated since version 3.11, will be removed in version 3.13: The keyword-argument syntax is deprecated in 3.11 and will be removed in 3.13. It may also be unsupported by static type checkers.

The functional syntax should also be used when any of the keys are not valid identifiers , for example because they are keywords or contain hyphens. Example:

By default, all keys must be present in a TypedDict . It is possible to mark individual keys as non-required using NotRequired :

This means that a Point2D TypedDict can have the label key omitted.

It is also possible to mark all keys as non-required by default by specifying a totality of False :

This means that a Point2D TypedDict can have any of the keys omitted. A type checker is only expected to support a literal False or True as the value of the total argument. True is the default, and makes all items defined in the class body required.

Individual keys of a total=False TypedDict can be marked as required using Required :

It is possible for a TypedDict type to inherit from one or more other TypedDict types using the class-based syntax. Usage:

Point3D has three items: x , y and z . It is equivalent to this definition:

A TypedDict cannot inherit from a non- TypedDict class, except for Generic . For example:

A TypedDict can be generic:

To create a generic TypedDict that is compatible with Python 3.11 or lower, inherit from Generic explicitly:

A TypedDict can be introspected via annotations dicts (see Annotations Best Practices for more information on annotations best practices), __total__ , __required_keys__ , and __optional_keys__ .

Point2D.__total__ gives the value of the total argument. Example:

This attribute reflects only the value of the total argument to the current TypedDict class, not whether the class is semantically total. For example, a TypedDict with __total__ set to True may have keys marked with NotRequired , or it may inherit from another TypedDict with total=False . Therefore, it is generally better to use __required_keys__ and __optional_keys__ for introspection.

Point2D.__required_keys__ and Point2D.__optional_keys__ return frozenset objects containing required and non-required keys, respectively.

Keys marked with Required will always appear in __required_keys__ and keys marked with NotRequired will always appear in __optional_keys__ .

For backwards compatibility with Python 3.10 and below, it is also possible to use inheritance to declare both required and non-required keys in the same TypedDict . This is done by declaring a TypedDict with one value for the total argument and then inheriting from it in another TypedDict with a different value for total :

If from __future__ import annotations is used or if annotations are given as strings, annotations are not evaluated when the TypedDict is defined. Therefore, the runtime introspection that __required_keys__ and __optional_keys__ rely on may not work properly, and the values of the attributes may be incorrect.

See PEP 589 for more examples and detailed rules of using TypedDict .

Changed in version 3.11: Added support for marking individual keys as Required or NotRequired . See PEP 655 .

Changed in version 3.11: Added support for generic TypedDict s.

Protocols ¶

The following protocols are provided by the typing module. All are decorated with @runtime_checkable .

An ABC with one abstract method __abs__ that is covariant in its return type.

An ABC with one abstract method __bytes__ .

An ABC with one abstract method __complex__ .

An ABC with one abstract method __float__ .

An ABC with one abstract method __index__ .

An ABC with one abstract method __int__ .

An ABC with one abstract method __round__ that is covariant in its return type.

ABCs for working with IO ¶

Generic type IO[AnyStr] and its subclasses TextIO(IO[str]) and BinaryIO(IO[bytes]) represent the types of I/O streams such as returned by open() .

Functions and decorators ¶

Cast a value to a type.

This returns the value unchanged. To the type checker this signals that the return value has the designated type, but at runtime we intentionally don’t check anything (we want this to be as fast as possible).

Ask a static type checker to confirm that val has an inferred type of typ .

At runtime this does nothing: it returns the first argument unchanged with no checks or side effects, no matter the actual type of the argument.

When a static type checker encounters a call to assert_type() , it emits an error if the value is not of the specified type:

This function is useful for ensuring the type checker’s understanding of a script is in line with the developer’s intentions:

Ask a static type checker to confirm that a line of code is unreachable.

Here, the annotations allow the type checker to infer that the last case can never execute, because arg is either an int or a str , and both options are covered by earlier cases.

If a type checker finds that a call to assert_never() is reachable, it will emit an error. For example, if the type annotation for arg was instead int | str | float , the type checker would emit an error pointing out that unreachable is of type float . For a call to assert_never to pass type checking, the inferred type of the argument passed in must be the bottom type, Never , and nothing else.

At runtime, this throws an exception when called.

Unreachable Code and Exhaustiveness Checking has more information about exhaustiveness checking with static typing.

Ask a static type checker to reveal the inferred type of an expression.

When a static type checker encounters a call to this function, it emits a diagnostic with the inferred type of the argument. For example:

This can be useful when you want to debug how your type checker handles a particular piece of code.

At runtime, this function prints the runtime type of its argument to sys.stderr and returns the argument unchanged (allowing the call to be used within an expression):

Note that the runtime type may be different from (more or less specific than) the type statically inferred by a type checker.

Most type checkers support reveal_type() anywhere, even if the name is not imported from typing . Importing the name from typing , however, allows your code to run without runtime errors and communicates intent more clearly.

Decorator to mark an object as providing dataclass -like behavior.

dataclass_transform may be used to decorate a class, metaclass, or a function that is itself a decorator. The presence of @dataclass_transform() tells a static type checker that the decorated object performs runtime “magic” that transforms a class in a similar way to @dataclasses.dataclass .

Example usage with a decorator function:

On a base class:

On a metaclass:

The CustomerModel classes defined above will be treated by type checkers similarly to classes created with @dataclasses.dataclass . For example, type checkers will assume these classes have __init__ methods that accept id and name .

The decorated class, metaclass, or function may accept the following bool arguments which type checkers will assume have the same effect as they would have on the @dataclasses.dataclass decorator: init , eq , order , unsafe_hash , frozen , match_args , kw_only , and slots . It must be possible for the value of these arguments ( True or False ) to be statically evaluated.

The arguments to the dataclass_transform decorator can be used to customize the default behaviors of the decorated class, metaclass, or function:

eq_default ( bool ) – Indicates whether the eq parameter is assumed to be True or False if it is omitted by the caller. Defaults to True .

order_default ( bool ) – Indicates whether the order parameter is assumed to be True or False if it is omitted by the caller. Defaults to False .

kw_only_default ( bool ) – Indicates whether the kw_only parameter is assumed to be True or False if it is omitted by the caller. Defaults to False .

Indicates whether the frozen parameter is assumed to be True or False if it is omitted by the caller. Defaults to False .

field_specifiers ( tuple [ Callable [ ... , Any ] , ... ] ) – Specifies a static list of supported classes or functions that describe fields, similar to dataclasses.field() . Defaults to () .

**kwargs ( Any ) – Arbitrary other keyword arguments are accepted in order to allow for possible future extensions.

Type checkers recognize the following optional parameters on field specifiers:

Parameter name

Description

Indicates whether the field should be included in the synthesized method. If unspecified, defaults to .

Provides the default value for the field.

Provides a runtime callback that returns the default value for the field. If neither nor are specified, the field is assumed to have no default value and must be provided a value when the class is instantiated.

An alias for the parameter on field specifiers.

Indicates whether the field should be marked as keyword-only. If , the field will be keyword-only. If , it will not be keyword-only. If unspecified, the value of the parameter on the object decorated with will be used, or if that is unspecified, the value of on will be used.

Provides an alternative name for the field. This alternative name is used in the synthesized method.

At runtime, this decorator records its arguments in the __dataclass_transform__ attribute on the decorated object. It has no other runtime effect.

See PEP 681 for more details.

Decorator for creating overloaded functions and methods.

The @overload decorator allows describing functions and methods that support multiple different combinations of argument types. A series of @overload -decorated definitions must be followed by exactly one non- @overload -decorated definition (for the same function/method).

@overload -decorated definitions are for the benefit of the type checker only, since they will be overwritten by the non- @overload -decorated definition. The non- @overload -decorated definition, meanwhile, will be used at runtime but should be ignored by a type checker. At runtime, calling an @overload -decorated function directly will raise NotImplementedError .

An example of overload that gives a more precise type than can be expressed using a union or a type variable:

See PEP 484 for more details and comparison with other typing semantics.

Changed in version 3.11: Overloaded functions can now be introspected at runtime using get_overloads() .

Return a sequence of @overload -decorated definitions for func .

func is the function object for the implementation of the overloaded function. For example, given the definition of process in the documentation for @overload , get_overloads(process) will return a sequence of three function objects for the three defined overloads. If called on a function with no overloads, get_overloads() returns an empty sequence.

get_overloads() can be used for introspecting an overloaded function at runtime.

Clear all registered overloads in the internal registry.

This can be used to reclaim the memory used by the registry.

Decorator to indicate final methods and final classes.

Decorating a method with @final indicates to a type checker that the method cannot be overridden in a subclass. Decorating a class with @final indicates that it cannot be subclassed.

Changed in version 3.11: The decorator will now attempt to set a __final__ attribute to True on the decorated object. Thus, a check like if getattr(obj, "__final__", False) can be used at runtime to determine whether an object obj has been marked as final. If the decorated object does not support setting attributes, the decorator returns the object unchanged without raising an exception.

Decorator to indicate that annotations are not type hints.

This works as a class or function decorator . With a class, it applies recursively to all methods and classes defined in that class (but not to methods defined in its superclasses or subclasses). Type checkers will ignore all annotations in a function or class with this decorator.

@no_type_check mutates the decorated object in place.

Decorator to give another decorator the no_type_check() effect.

This wraps the decorator with something that wraps the decorated function in no_type_check() .

Decorator to indicate that a method in a subclass is intended to override a method or attribute in a superclass.

Type checkers should emit an error if a method decorated with @override does not, in fact, override anything. This helps prevent bugs that may occur when a base class is changed without an equivalent change to a child class.

There is no runtime checking of this property.

The decorator will attempt to set an __override__ attribute to True on the decorated object. Thus, a check like if getattr(obj, "__override__", False) can be used at runtime to determine whether an object obj has been marked as an override. If the decorated object does not support setting attributes, the decorator returns the object unchanged without raising an exception.

See PEP 698 for more details.

Decorator to mark a class or function as unavailable at runtime.

This decorator is itself not available at runtime. It is mainly intended to mark classes that are defined in type stub files if an implementation returns an instance of a private class:

Note that returning instances of private classes is not recommended. It is usually preferable to make such classes public.

Introspection helpers ¶

Return a dictionary containing type hints for a function, method, module or class object.

This is often the same as obj.__annotations__ , but this function makes the following changes to the annotations dictionary:

Forward references encoded as string literals or ForwardRef objects are handled by evaluating them in globalns , localns , and (where applicable) obj ’s type parameter namespace. If globalns or localns is not given, appropriate namespace dictionaries are inferred from obj .

None is replaced with types.NoneType .

If @no_type_check has been applied to obj , an empty dictionary is returned.

If obj is a class C , the function returns a dictionary that merges annotations from C ’s base classes with those on C directly. This is done by traversing C.__mro__ and iteratively combining __annotations__ dictionaries. Annotations on classes appearing earlier in the method resolution order always take precedence over annotations on classes appearing later in the method resolution order.

The function recursively replaces all occurrences of Annotated[T, ...] with T , unless include_extras is set to True (see Annotated for more information).

See also inspect.get_annotations() , a lower-level function that returns annotations more directly.

If any forward references in the annotations of obj are not resolvable or are not valid Python code, this function will raise an exception such as NameError . For example, this can happen with imported type aliases that include forward references, or with names imported under if TYPE_CHECKING .

Changed in version 3.9: Added include_extras parameter as part of PEP 593 . See the documentation on Annotated for more information.

Changed in version 3.11: Previously, Optional[t] was added for function and method annotations if a default value equal to None was set. Now the annotation is returned unchanged.

Get the unsubscripted version of a type: for a typing object of the form X[Y, Z, ...] return X .

If X is a typing-module alias for a builtin or collections class, it will be normalized to the original class. If X is an instance of ParamSpecArgs or ParamSpecKwargs , return the underlying ParamSpec . Return None for unsupported objects.

Get type arguments with all substitutions performed: for a typing object of the form X[Y, Z, ...] return (Y, Z, ...) .

If X is a union or Literal contained in another generic type, the order of (Y, Z, ...) may be different from the order of the original arguments [Y, Z, ...] due to type caching. Return () for unsupported objects.

Check if a type is a TypedDict .

Class used for internal typing representation of string forward references.

For example, List["SomeClass"] is implicitly transformed into List[ForwardRef("SomeClass")] . ForwardRef should not be instantiated by a user, but may be used by introspection tools.

PEP 585 generic types such as list["SomeClass"] will not be implicitly transformed into list[ForwardRef("SomeClass")] and thus will not automatically resolve to list[SomeClass] .

Added in version 3.7.4.

A special constant that is assumed to be True by 3rd party static type checkers. It is False at runtime.

The first type annotation must be enclosed in quotes, making it a “forward reference”, to hide the expensive_mod reference from the interpreter runtime. Type annotations for local variables are not evaluated, so the second annotation does not need to be enclosed in quotes.

If from __future__ import annotations is used, annotations are not evaluated at function definition time. Instead, they are stored as strings in __annotations__ . This makes it unnecessary to use quotes around the annotation (see PEP 563 ).

Deprecated aliases ¶

This module defines several deprecated aliases to pre-existing standard library classes. These were originally included in the typing module in order to support parameterizing these generic classes using [] . However, the aliases became redundant in Python 3.9 when the corresponding pre-existing classes were enhanced to support [] (see PEP 585 ).

The redundant types are deprecated as of Python 3.9. However, while the aliases may be removed at some point, removal of these aliases is not currently planned. As such, no deprecation warnings are currently issued by the interpreter for these aliases.

If at some point it is decided to remove these deprecated aliases, a deprecation warning will be issued by the interpreter for at least two releases prior to removal. The aliases are guaranteed to remain in the typing module without deprecation warnings until at least Python 3.14.

Type checkers are encouraged to flag uses of the deprecated types if the program they are checking targets a minimum Python version of 3.9 or newer.

Aliases to built-in types ¶

Deprecated alias to dict .

Note that to annotate arguments, it is preferred to use an abstract collection type such as Mapping rather than to use dict or typing.Dict .

This type can be used as follows:

Deprecated since version 3.9: builtins.dict now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to list .

Note that to annotate arguments, it is preferred to use an abstract collection type such as Sequence or Iterable rather than to use list or typing.List .

This type may be used as follows:

Deprecated since version 3.9: builtins.list now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to builtins.set .

Note that to annotate arguments, it is preferred to use an abstract collection type such as AbstractSet rather than to use set or typing.Set .

Deprecated since version 3.9: builtins.set now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to builtins.frozenset .

Deprecated since version 3.9: builtins.frozenset now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias for tuple .

tuple and Tuple are special-cased in the type system; see Annotating tuples for more details.

Deprecated since version 3.9: builtins.tuple now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to type .

See The type of class objects for details on using type or typing.Type in type annotations.

Deprecated since version 3.9: builtins.type now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Aliases to types in collections ¶

Deprecated alias to collections.defaultdict .

Deprecated since version 3.9: collections.defaultdict now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.OrderedDict .

Added in version 3.7.2.

Deprecated since version 3.9: collections.OrderedDict now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.ChainMap .

Added in version 3.6.1.

Deprecated since version 3.9: collections.ChainMap now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.Counter .

Deprecated since version 3.9: collections.Counter now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.deque .

Deprecated since version 3.9: collections.deque now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Aliases to other concrete types ¶

Deprecated since version 3.8, will be removed in version 3.13: The typing.io namespace is deprecated and will be removed. These types should be directly imported from typing instead.

Deprecated aliases corresponding to the return types from re.compile() and re.match() .

These types (and the corresponding functions) are generic over AnyStr . Pattern can be specialised as Pattern[str] or Pattern[bytes] ; Match can be specialised as Match[str] or Match[bytes] .

Deprecated since version 3.8, will be removed in version 3.13: The typing.re namespace is deprecated and will be removed. These types should be directly imported from typing instead.

Deprecated since version 3.9: Classes Pattern and Match from re now support [] . See PEP 585 and Generic Alias Type .

Deprecated alias for str .

Text is provided to supply a forward compatible path for Python 2 code: in Python 2, Text is an alias for unicode .

Use Text to indicate that a value must contain a unicode string in a manner that is compatible with both Python 2 and Python 3:

Deprecated since version 3.11: Python 2 is no longer supported, and most type checkers also no longer support type checking Python 2 code. Removal of the alias is not currently planned, but users are encouraged to use str instead of Text .

Aliases to container ABCs in collections.abc ¶

Deprecated alias to collections.abc.Set .

Deprecated since version 3.9: collections.abc.Set now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

This type represents the types bytes , bytearray , and memoryview of byte sequences.

Deprecated since version 3.9, will be removed in version 3.14: Prefer collections.abc.Buffer , or a union like bytes | bytearray | memoryview .

Deprecated alias to collections.abc.Collection .

Added in version 3.6.

Deprecated since version 3.9: collections.abc.Collection now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.Container .

Deprecated since version 3.9: collections.abc.Container now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.ItemsView .

Deprecated since version 3.9: collections.abc.ItemsView now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.KeysView .

Deprecated since version 3.9: collections.abc.KeysView now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.Mapping .

Deprecated since version 3.9: collections.abc.Mapping now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.MappingView .

Deprecated since version 3.9: collections.abc.MappingView now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.MutableMapping .

Deprecated since version 3.9: collections.abc.MutableMapping now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.MutableSequence .

Deprecated since version 3.9: collections.abc.MutableSequence now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.MutableSet .

Deprecated since version 3.9: collections.abc.MutableSet now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.Sequence .

Deprecated since version 3.9: collections.abc.Sequence now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.ValuesView .

Deprecated since version 3.9: collections.abc.ValuesView now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Aliases to asynchronous ABCs in collections.abc ¶

Deprecated alias to collections.abc.Coroutine .

The variance and order of type variables correspond to those of Generator , for example:

Deprecated since version 3.9: collections.abc.Coroutine now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.AsyncGenerator .

An async generator can be annotated by the generic type AsyncGenerator[YieldType, SendType] . For example:

Unlike normal generators, async generators cannot return a value, so there is no ReturnType type parameter. As with Generator , the SendType behaves contravariantly.

If your generator will only yield values, set the SendType to None :

Alternatively, annotate your generator as having a return type of either AsyncIterable[YieldType] or AsyncIterator[YieldType] :

Deprecated since version 3.9: collections.abc.AsyncGenerator now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.AsyncIterable .

Deprecated since version 3.9: collections.abc.AsyncIterable now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.AsyncIterator .

Deprecated since version 3.9: collections.abc.AsyncIterator now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.Awaitable .

Deprecated since version 3.9: collections.abc.Awaitable now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Aliases to other ABCs in collections.abc ¶

Deprecated alias to collections.abc.Iterable .

Deprecated since version 3.9: collections.abc.Iterable now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.Iterator .

Deprecated since version 3.9: collections.abc.Iterator now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.Callable .

See Annotating callable objects for details on how to use collections.abc.Callable and typing.Callable in type annotations.

Deprecated since version 3.9: collections.abc.Callable now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.Generator .

A generator can be annotated by the generic type Generator[YieldType, SendType, ReturnType] . For example:

Note that unlike many other generics in the typing module, the SendType of Generator behaves contravariantly, not covariantly or invariantly.

If your generator will only yield values, set the SendType and ReturnType to None :

Alternatively, annotate your generator as having a return type of either Iterable[YieldType] or Iterator[YieldType] :

Deprecated since version 3.9: collections.abc.Generator now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.Hashable .

Deprecated since version 3.12: Use collections.abc.Hashable directly instead.

Deprecated alias to collections.abc.Reversible .

Deprecated since version 3.9: collections.abc.Reversible now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to collections.abc.Sized .

Deprecated since version 3.12: Use collections.abc.Sized directly instead.

Aliases to contextlib ABCs ¶

Deprecated alias to contextlib.AbstractContextManager .

Added in version 3.5.4.

Deprecated since version 3.9: contextlib.AbstractContextManager now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecated alias to contextlib.AbstractAsyncContextManager .

Added in version 3.6.2.

Deprecated since version 3.9: contextlib.AbstractAsyncContextManager now supports subscripting ( [] ). See PEP 585 and Generic Alias Type .

Deprecation Timeline of Major Features ¶

Certain features in typing are deprecated and may be removed in a future version of Python. The following table summarizes major deprecations for your convenience. This is subject to change, and not all deprecations are listed.

Feature

Deprecated in

Projected removal

PEP/issue

and submodules

3.8

3.13

versions of standard collections

3.9

Undecided (see for more information)

3.9

3.14

3.11

Undecided

and

3.12

Undecided

3.12

Undecided

Table of Contents

  • Specification for the Python Type System
  • Type aliases
  • Annotating tuples
  • The type of class objects
  • User-defined generic types
  • The Any type
  • Nominal vs structural subtyping
  • Special types
  • Special forms
  • Building generic types and type aliases
  • Other special directives
  • ABCs for working with IO
  • Functions and decorators
  • Introspection helpers
  • Aliases to built-in types
  • Aliases to types in collections
  • Aliases to other concrete types
  • Aliases to container ABCs in collections.abc
  • Aliases to asynchronous ABCs in collections.abc
  • Aliases to other ABCs in collections.abc
  • Aliases to contextlib ABCs
  • Deprecation Timeline of Major Features

Previous topic

Development Tools

pydoc — Documentation generator and online help system

  • Report a Bug
  • Show Source

Python Tutorial

File handling, python modules, python numpy, python pandas, python matplotlib, python scipy, machine learning, python mysql, python mongodb, python reference, module reference, python how to, python examples, python assignment operators.

Assignment operators are used to assign values to variables:

Operator Example Same As Try it
= x = 5 x = 5
+= x += 3 x = x + 3
-= x -= 3 x = x - 3
*= x *= 3 x = x * 3
/= x /= 3 x = x / 3
%= x %= 3 x = x % 3
//= x //= 3 x = x // 3
**= x **= 3 x = x ** 3
&= x &= 3 x = x & 3
|= x |= 3 x = x | 3
^= x ^= 3 x = x ^ 3
>>= x >>= 3 x = x >> 3
<<= x <<= 3 x = x << 3

Related Pages

Get Certified

COLOR PICKER

colorpicker

Contact Sales

If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail: [email protected]

Report Error

If you want to report an error, or if you want to make a suggestion, send us an e-mail: [email protected]

Top Tutorials

Top references, top examples, get certified.

Stack Exchange Network

Stack Exchange network consists of 183 Q&A communities including Stack Overflow , the largest, most trusted online community for developers to learn, share their knowledge, and build their careers.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Variable declaration versus assignment syntax

Working on a statically typed language with type inference and streamlined syntax, and need to make final decision about syntax for variable declaration versus assignment. Specifically I'm trying to choose between:

Creating functions will use = regardless:

And assignment to compound objects will do likewise:

Which of options 1 or 2 would people find most convenient/least surprising/otherwise best?

  • language-design
  • language-features

rwallace's user avatar

  • 1 Why do you need differentiate between the two? Edit: To clarify, why can't you make foo = ... always introduce or assign to a local, with syntax to exempt one name from the "introducing" part, instead making = alter a global/closed over variable (i.e. , like Python's global and nonlocal , possibly unified into one concept)? –  user7043 Oct 27, 2013 at 20:09
  • If by type inference you mean implicit declaration of variables, may I refer you to the ALGOL committee's remarks? They roundly boxed Tony Hoare's ears when he suggested adding FORTRAN-style implicit declaration to ALGOL. This was before the (possibly apocryphal) story of a lost interplanetary probe from a typographical error combined with implicit declaration, that converted a FORTRAN DO-statement into a legal assignment statement. –  John R. Strohm Oct 28, 2013 at 3:31

3 Answers 3

There are many more aspects one should consider when settling for assignment/declaration syntax, than simple = vs. := bikeshedding.

Type inference or not, you will want a syntax for explicit type annotations. In some type systems, inference may not be possible without occasional explicit annotations. There two possible classes of syntax for this:

  • A type-variable statement without further operators implies a declaration, e.g. int i in C . Some languages use postfix types like i int , ( Golang to a certain degree).
  • There is a typing operator, often : or :: . Sometimes, this declares the type of a name: let i : int = 42 (e.g. Ocaml ). In an interesting spin of this, Julia allows a programmer to use type assertions for arbitrary expressions, along the lines of sum = (a + b):int .

You may also want to consider an explicit declaration keyword, like var , val or let . The advantage is not primarily that they make parsing and understanding of the code much easier, but that they unambiguously introduce a variable. Why is this important?

If you have closures, you need to precisely declare which scope a variable belongs to. Imagine a language without a declaration keyword, and implicit declaration through assignment (e.g. PHP or Python ). Both of these are syntactically challenged with respect to closures, because they either ascribe a variable to the outermost or innermost possible scope. Consider this Python:

Compare with a language that allows explicit declaration:

Explicit declarations allow variable shadowing. While generally a bad practice, it sometimes makes code much easier to follow – no reason to disallow it.

Explicit declarations offer a form of typo detection, because unbound variables are not implicitly declared. Consider:

You should also consider whether you would like to (optionally) enforce single-assignment form, e.g through keywords like val ( Scala ), let , or const or by default. In my experience, such code is easier to reason about.

How would a short declaration e.g. via := fare in these points?

  • Assuming you have typing via a : operator and assigment via = , then i : int = 42 could declare a variable, the syntax i : = 42 would invoke inference of the variable, and i := 42 would be a nice contraction, but not an operator in itself. This avoids problems later on.
  • Another rationale is the mathematical syntax for the declaration of new names x := expression or expression =: x . However, this has no significant difference to the = relation, except that the colon draws attention to one name. Simply using the := for similarity to maths is silly (considering the = abuse), as is using it for similarity to Pascal .

We can declare some more or less sane characteristics for := , like:

  • It declares a new variable in the current scope
  • which is re-assignable,
  • and performs type inference.
  • Re-declaring a variable in the same scope is a compilation error.
  • Shadowing is permitted.

But in practice, things get murky. What happens when you have multiple assignments (which you should seriously consider), like

Should this throw an error because x is already declared in this scope? Or should it just assign x and declare y ? Go takes the second route, with the result that typo detection is weakened:

Note that the “RHS of typing-operator is optional” idea from above would disambiguate this, as every new variable would have to be followed by a colon:

Should = be declaration but := be assignment? Hell no. First, no language I know of does this. Second, when you don't use single-assignment form, then assignment is more common than declaration. Huffman-coding of operator requires that the shorter operator is used for the more common operation. But if you don't generally allow reassignment, the = is somewhat free to use (depending on whether you use = or == as comparison operator, and whether you could disambiguate a = from context).

  • If assignment and declaration use the same operator, bad things happen: Closures, variable shadowing, and typo detection all get ugly with implicit declarations.
  • But if you don't have re-assignments, things clear up again.
  • Don't forget that explicit types and variable declarations are somewhat related. Combining their syntax has served many languages well.
  • Are you sure you want such little visual distinction between assignment and declaration?

Personal opinion

I am fond of declaration keywords like val or my . They stand out, making code easier to grok. Explicit declarations are always a good idea for a serious language.

amon's user avatar

  • Yeah, I'm using : for optional explicit type, so as you say, i := 42 is shorthand for i: int = 42 . I am allowing reassignment by default (so there needs to be some distinction), but it can be disabled with a final modifier as in Java. And I'm not a huge fan of declaring multiple variables on one line so I'm okay with losing that. –  rwallace Oct 28, 2013 at 21:07

Both alternatives are bad. The first because it is far from obvious that a := operator creates a local variable, and the second because it means you have two different meanings for the = operator. Learn Dennis Ritchie's lesson, and don't have two operators that appear to be assignments, one of which is not.

Ross Patterson's user avatar

  • 2 Further, if I see := , I assume pascal assignment. That also means I expect = to test equality, not declare a variable. –  Telastyn Oct 27, 2013 at 21:23
  • 1 Trying to read a source code without understanding its notation is bad habit. And your "obvious" point won't work when you know the notation. Otherwise := and = are visually distinguishable very well. –  lorus Oct 28, 2013 at 6:48
  • 1 @lorus Tell that to every seasoned C programmer who's typed if (a = b) ... . It's not just a rookie mistake, everyone does it once in a while. In other words, it's a language design flaw. –  Ross Patterson Oct 28, 2013 at 9:49
  • 4 This problem with C syntax is that assignment is an expression. If it would be a statement, the problem won't occur. –  lorus Oct 29, 2013 at 4:35

New variables should be declared with x := 5 and should be updated/reassigned with x = 5 . Kind of the norm now, eight years later. Mostly thanks to golang I think.

Luke Miles's user avatar

Your Answer

Reminder: Answers generated by artificial intelligence tools are not allowed on Software Engineering Stack Exchange. Learn more

Sign up or log in

Post as a guest.

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy .

Not the answer you're looking for? Browse other questions tagged language-design syntax language-features or ask your own question .

  • The Overflow Blog
  • Introducing Staging Ground: The private space to get feedback on questions...

Hot Network Questions

  • Converting NEMA 10-30 to 14-30 using ground from adjacent 15 amp receptacle
  • A phrase that means you are indifferent towards the things you are familiar with?
  • Why does the proposed Lunar Crater Radio Telescope suggest an optimal latitude of 20 degrees North?
  • What legal reason, if any, does my bank have to know if I am a dual citizen of the US?
  • Do we know how the SpaceX Starship stack handles engine shutdowns?
  • Is it theoretically possible for the sun to go dark?
  • What is the frequentist's Bayesian prior for a coin with unknown bias
  • Is obeying the parallelogram law of vector addition sufficient to make a physical quantity qualify as a vector?
  • How to create an enumerate environment with Case 1, Case 2,
  • Is 1.5 hours enough for flight transfer in Frankfurt?
  • How can I hang heavy bikes under a thick wooden shelf?
  • Death in the saddle
  • What does "however" mean in this sentence? Does it mean ''still'' or "moreover"?
  • An application of the (100/e)% rule applied to postdocs: moving on from an academic career, perhaps
  • Cannot open an .mbox file with neomutt (although it works with mutt)
  • StreamPlot does not give me the streamlines I ask for
  • Who are the mathematicians interested in the history of mathematics?
  • How do I get rid of the artifacts on this sphere?
  • Are there any jobs that are forbidden by law to convicted felons?
  • G string becomes out of tune in F shape barre chords
  • Why "Power & battery" stuck at spinning circle for 4 hours?
  • How do you keep the horror spooky when your players are a bunch of goofballs?
  • NES Emulator in C
  • \ifnum to draw a tikzpicture, less than or equals

assignment declaration python

Multiple assignment in Python: Assign multiple values or the same value to multiple variables

In Python, the = operator is used to assign values to variables.

You can assign values to multiple variables in one line.

Assign multiple values to multiple variables

Assign the same value to multiple variables.

You can assign multiple values to multiple variables by separating them with commas , .

You can assign values to more than three variables, and it is also possible to assign values of different data types to those variables.

When only one variable is on the left side, values on the right side are assigned as a tuple to that variable.

If the number of variables on the left does not match the number of values on the right, a ValueError occurs. You can assign the remaining values as a list by prefixing the variable name with * .

For more information on using * and assigning elements of a tuple and list to multiple variables, see the following article.

  • Unpack a tuple and list in Python

You can also swap the values of multiple variables in the same way. See the following article for details:

  • Swap values ​​in a list or values of variables in Python

You can assign the same value to multiple variables by using = consecutively.

For example, this is useful when initializing multiple variables with the same value.

After assigning the same value, you can assign a different value to one of these variables. As described later, be cautious when assigning mutable objects such as list and dict .

You can apply the same method when assigning the same value to three or more variables.

Be careful when assigning mutable objects such as list and dict .

If you use = consecutively, the same object is assigned to all variables. Therefore, if you change the value of an element or add a new element in one variable, the changes will be reflected in the others as well.

If you want to handle mutable objects separately, you need to assign them individually.

after c = []; d = [] , c and d are guaranteed to refer to two different, unique, newly created empty lists. (Note that c = d = [] assigns the same object to both c and d .) 3. Data model — Python 3.11.3 documentation

You can also use copy() or deepcopy() from the copy module to make shallow and deep copies. See the following article.

  • Shallow and deep copy in Python: copy(), deepcopy()

Related Categories

Related articles.

  • NumPy: arange() and linspace() to generate evenly spaced values
  • Chained comparison (a < x < b) in Python
  • pandas: Get first/last n rows of DataFrame with head() and tail()
  • pandas: Filter rows/columns by labels with filter()
  • Get the filename, directory, extension from a path string in Python
  • Sign function in Python (sign/signum/sgn, copysign)
  • How to flatten a list of lists in Python
  • None in Python
  • Create calendar as text, HTML, list in Python
  • NumPy: Insert elements, rows, and columns into an array with np.insert()
  • Shuffle a list, string, tuple in Python (random.shuffle, sample)
  • Add and update an item in a dictionary in Python
  • Cartesian product of lists in Python (itertools.product)
  • Remove a substring from a string in Python
  • pandas: Extract rows that contain specific strings from a DataFrame

Python Enhancement Proposals

  • Python »
  • PEP Index »

PEP 572 – Assignment Expressions

The importance of real code, exceptional cases, scope of the target, relative precedence of :=, change to evaluation order, differences between assignment expressions and assignment statements, specification changes during implementation, _pydecimal.py, datetime.py, sysconfig.py, simplifying list comprehensions, capturing condition values, changing the scope rules for comprehensions, alternative spellings, special-casing conditional statements, special-casing comprehensions, lowering operator precedence, allowing commas to the right, always requiring parentheses, why not just turn existing assignment into an expression, with assignment expressions, why bother with assignment statements, why not use a sublocal scope and prevent namespace pollution, style guide recommendations, acknowledgements, a numeric example, appendix b: rough code translations for comprehensions, appendix c: no changes to scope semantics.

This is a proposal for creating a way to assign to variables within an expression using the notation NAME := expr .

As part of this change, there is also an update to dictionary comprehension evaluation order to ensure key expressions are executed before value expressions (allowing the key to be bound to a name and then re-used as part of calculating the corresponding value).

During discussion of this PEP, the operator became informally known as “the walrus operator”. The construct’s formal name is “Assignment Expressions” (as per the PEP title), but they may also be referred to as “Named Expressions” (e.g. the CPython reference implementation uses that name internally).

Naming the result of an expression is an important part of programming, allowing a descriptive name to be used in place of a longer expression, and permitting reuse. Currently, this feature is available only in statement form, making it unavailable in list comprehensions and other expression contexts.

Additionally, naming sub-parts of a large expression can assist an interactive debugger, providing useful display hooks and partial results. Without a way to capture sub-expressions inline, this would require refactoring of the original code; with assignment expressions, this merely requires the insertion of a few name := markers. Removing the need to refactor reduces the likelihood that the code be inadvertently changed as part of debugging (a common cause of Heisenbugs), and is easier to dictate to another programmer.

During the development of this PEP many people (supporters and critics both) have had a tendency to focus on toy examples on the one hand, and on overly complex examples on the other.

The danger of toy examples is twofold: they are often too abstract to make anyone go “ooh, that’s compelling”, and they are easily refuted with “I would never write it that way anyway”.

The danger of overly complex examples is that they provide a convenient strawman for critics of the proposal to shoot down (“that’s obfuscated”).

Yet there is some use for both extremely simple and extremely complex examples: they are helpful to clarify the intended semantics. Therefore, there will be some of each below.

However, in order to be compelling , examples should be rooted in real code, i.e. code that was written without any thought of this PEP, as part of a useful application, however large or small. Tim Peters has been extremely helpful by going over his own personal code repository and picking examples of code he had written that (in his view) would have been clearer if rewritten with (sparing) use of assignment expressions. His conclusion: the current proposal would have allowed a modest but clear improvement in quite a few bits of code.

Another use of real code is to observe indirectly how much value programmers place on compactness. Guido van Rossum searched through a Dropbox code base and discovered some evidence that programmers value writing fewer lines over shorter lines.

Case in point: Guido found several examples where a programmer repeated a subexpression, slowing down the program, in order to save one line of code, e.g. instead of writing:

they would write:

Another example illustrates that programmers sometimes do more work to save an extra level of indentation:

This code tries to match pattern2 even if pattern1 has a match (in which case the match on pattern2 is never used). The more efficient rewrite would have been:

Syntax and semantics

In most contexts where arbitrary Python expressions can be used, a named expression can appear. This is of the form NAME := expr where expr is any valid Python expression other than an unparenthesized tuple, and NAME is an identifier.

The value of such a named expression is the same as the incorporated expression, with the additional side-effect that the target is assigned that value:

There are a few places where assignment expressions are not allowed, in order to avoid ambiguities or user confusion:

This rule is included to simplify the choice for the user between an assignment statement and an assignment expression – there is no syntactic position where both are valid.

Again, this rule is included to avoid two visually similar ways of saying the same thing.

This rule is included to disallow excessively confusing code, and because parsing keyword arguments is complex enough already.

This rule is included to discourage side effects in a position whose exact semantics are already confusing to many users (cf. the common style recommendation against mutable default values), and also to echo the similar prohibition in calls (the previous bullet).

The reasoning here is similar to the two previous cases; this ungrouped assortment of symbols and operators composed of : and = is hard to read correctly.

This allows lambda to always bind less tightly than := ; having a name binding at the top level inside a lambda function is unlikely to be of value, as there is no way to make use of it. In cases where the name will be used more than once, the expression is likely to need parenthesizing anyway, so this prohibition will rarely affect code.

This shows that what looks like an assignment operator in an f-string is not always an assignment operator. The f-string parser uses : to indicate formatting options. To preserve backwards compatibility, assignment operator usage inside of f-strings must be parenthesized. As noted above, this usage of the assignment operator is not recommended.

An assignment expression does not introduce a new scope. In most cases the scope in which the target will be bound is self-explanatory: it is the current scope. If this scope contains a nonlocal or global declaration for the target, the assignment expression honors that. A lambda (being an explicit, if anonymous, function definition) counts as a scope for this purpose.

There is one special case: an assignment expression occurring in a list, set or dict comprehension or in a generator expression (below collectively referred to as “comprehensions”) binds the target in the containing scope, honoring a nonlocal or global declaration for the target in that scope, if one exists. For the purpose of this rule the containing scope of a nested comprehension is the scope that contains the outermost comprehension. A lambda counts as a containing scope.

The motivation for this special case is twofold. First, it allows us to conveniently capture a “witness” for an any() expression, or a counterexample for all() , for example:

Second, it allows a compact way of updating mutable state from a comprehension, for example:

However, an assignment expression target name cannot be the same as a for -target name appearing in any comprehension containing the assignment expression. The latter names are local to the comprehension in which they appear, so it would be contradictory for a contained use of the same name to refer to the scope containing the outermost comprehension instead.

For example, [i := i+1 for i in range(5)] is invalid: the for i part establishes that i is local to the comprehension, but the i := part insists that i is not local to the comprehension. The same reason makes these examples invalid too:

While it’s technically possible to assign consistent semantics to these cases, it’s difficult to determine whether those semantics actually make sense in the absence of real use cases. Accordingly, the reference implementation [1] will ensure that such cases raise SyntaxError , rather than executing with implementation defined behaviour.

This restriction applies even if the assignment expression is never executed:

For the comprehension body (the part before the first “for” keyword) and the filter expression (the part after “if” and before any nested “for”), this restriction applies solely to target names that are also used as iteration variables in the comprehension. Lambda expressions appearing in these positions introduce a new explicit function scope, and hence may use assignment expressions with no additional restrictions.

Due to design constraints in the reference implementation (the symbol table analyser cannot easily detect when names are re-used between the leftmost comprehension iterable expression and the rest of the comprehension), named expressions are disallowed entirely as part of comprehension iterable expressions (the part after each “in”, and before any subsequent “if” or “for” keyword):

A further exception applies when an assignment expression occurs in a comprehension whose containing scope is a class scope. If the rules above were to result in the target being assigned in that class’s scope, the assignment expression is expressly invalid. This case also raises SyntaxError :

(The reason for the latter exception is the implicit function scope created for comprehensions – there is currently no runtime mechanism for a function to refer to a variable in the containing class scope, and we do not want to add such a mechanism. If this issue ever gets resolved this special case may be removed from the specification of assignment expressions. Note that the problem already exists for using a variable defined in the class scope from a comprehension.)

See Appendix B for some examples of how the rules for targets in comprehensions translate to equivalent code.

The := operator groups more tightly than a comma in all syntactic positions where it is legal, but less tightly than all other operators, including or , and , not , and conditional expressions ( A if C else B ). As follows from section “Exceptional cases” above, it is never allowed at the same level as = . In case a different grouping is desired, parentheses should be used.

The := operator may be used directly in a positional function call argument; however it is invalid directly in a keyword argument.

Some examples to clarify what’s technically valid or invalid:

Most of the “valid” examples above are not recommended, since human readers of Python source code who are quickly glancing at some code may miss the distinction. But simple cases are not objectionable:

This PEP recommends always putting spaces around := , similar to PEP 8 ’s recommendation for = when used for assignment, whereas the latter disallows spaces around = used for keyword arguments.)

In order to have precisely defined semantics, the proposal requires evaluation order to be well-defined. This is technically not a new requirement, as function calls may already have side effects. Python already has a rule that subexpressions are generally evaluated from left to right. However, assignment expressions make these side effects more visible, and we propose a single change to the current evaluation order:

  • In a dict comprehension {X: Y for ...} , Y is currently evaluated before X . We propose to change this so that X is evaluated before Y . (In a dict display like {X: Y} this is already the case, and also in dict((X, Y) for ...) which should clearly be equivalent to the dict comprehension.)

Most importantly, since := is an expression, it can be used in contexts where statements are illegal, including lambda functions and comprehensions.

Conversely, assignment expressions don’t support the advanced features found in assignment statements:

  • Multiple targets are not directly supported: x = y = z = 0 # Equivalent: (z := (y := (x := 0)))
  • Single assignment targets other than a single NAME are not supported: # No equivalent a [ i ] = x self . rest = []
  • Priority around commas is different: x = 1 , 2 # Sets x to (1, 2) ( x := 1 , 2 ) # Sets x to 1
  • Iterable packing and unpacking (both regular or extended forms) are not supported: # Equivalent needs extra parentheses loc = x , y # Use (loc := (x, y)) info = name , phone , * rest # Use (info := (name, phone, *rest)) # No equivalent px , py , pz = position name , phone , email , * other_info = contact
  • Inline type annotations are not supported: # Closest equivalent is "p: Optional[int]" as a separate declaration p : Optional [ int ] = None
  • Augmented assignment is not supported: total += tax # Equivalent: (total := total + tax)

The following changes have been made based on implementation experience and additional review after the PEP was first accepted and before Python 3.8 was released:

  • for consistency with other similar exceptions, and to avoid locking in an exception name that is not necessarily going to improve clarity for end users, the originally proposed TargetScopeError subclass of SyntaxError was dropped in favour of just raising SyntaxError directly. [3]
  • due to a limitation in CPython’s symbol table analysis process, the reference implementation raises SyntaxError for all uses of named expressions inside comprehension iterable expressions, rather than only raising them when the named expression target conflicts with one of the iteration variables in the comprehension. This could be revisited given sufficiently compelling examples, but the extra complexity needed to implement the more selective restriction doesn’t seem worthwhile for purely hypothetical use cases.

Examples from the Python standard library

env_base is only used on these lines, putting its assignment on the if moves it as the “header” of the block.

  • Current: env_base = os . environ . get ( "PYTHONUSERBASE" , None ) if env_base : return env_base
  • Improved: if env_base := os . environ . get ( "PYTHONUSERBASE" , None ): return env_base

Avoid nested if and remove one indentation level.

  • Current: if self . _is_special : ans = self . _check_nans ( context = context ) if ans : return ans
  • Improved: if self . _is_special and ( ans := self . _check_nans ( context = context )): return ans

Code looks more regular and avoid multiple nested if. (See Appendix A for the origin of this example.)

  • Current: reductor = dispatch_table . get ( cls ) if reductor : rv = reductor ( x ) else : reductor = getattr ( x , "__reduce_ex__" , None ) if reductor : rv = reductor ( 4 ) else : reductor = getattr ( x , "__reduce__" , None ) if reductor : rv = reductor () else : raise Error ( "un(deep)copyable object of type %s " % cls )
  • Improved: if reductor := dispatch_table . get ( cls ): rv = reductor ( x ) elif reductor := getattr ( x , "__reduce_ex__" , None ): rv = reductor ( 4 ) elif reductor := getattr ( x , "__reduce__" , None ): rv = reductor () else : raise Error ( "un(deep)copyable object of type %s " % cls )

tz is only used for s += tz , moving its assignment inside the if helps to show its scope.

  • Current: s = _format_time ( self . _hour , self . _minute , self . _second , self . _microsecond , timespec ) tz = self . _tzstr () if tz : s += tz return s
  • Improved: s = _format_time ( self . _hour , self . _minute , self . _second , self . _microsecond , timespec ) if tz := self . _tzstr (): s += tz return s

Calling fp.readline() in the while condition and calling .match() on the if lines make the code more compact without making it harder to understand.

  • Current: while True : line = fp . readline () if not line : break m = define_rx . match ( line ) if m : n , v = m . group ( 1 , 2 ) try : v = int ( v ) except ValueError : pass vars [ n ] = v else : m = undef_rx . match ( line ) if m : vars [ m . group ( 1 )] = 0
  • Improved: while line := fp . readline (): if m := define_rx . match ( line ): n , v = m . group ( 1 , 2 ) try : v = int ( v ) except ValueError : pass vars [ n ] = v elif m := undef_rx . match ( line ): vars [ m . group ( 1 )] = 0

A list comprehension can map and filter efficiently by capturing the condition:

Similarly, a subexpression can be reused within the main expression, by giving it a name on first use:

Note that in both cases the variable y is bound in the containing scope (i.e. at the same level as results or stuff ).

Assignment expressions can be used to good effect in the header of an if or while statement:

Particularly with the while loop, this can remove the need to have an infinite loop, an assignment, and a condition. It also creates a smooth parallel between a loop which simply uses a function call as its condition, and one which uses that as its condition but also uses the actual value.

An example from the low-level UNIX world:

Rejected alternative proposals

Proposals broadly similar to this one have come up frequently on python-ideas. Below are a number of alternative syntaxes, some of them specific to comprehensions, which have been rejected in favour of the one given above.

A previous version of this PEP proposed subtle changes to the scope rules for comprehensions, to make them more usable in class scope and to unify the scope of the “outermost iterable” and the rest of the comprehension. However, this part of the proposal would have caused backwards incompatibilities, and has been withdrawn so the PEP can focus on assignment expressions.

Broadly the same semantics as the current proposal, but spelled differently.

Since EXPR as NAME already has meaning in import , except and with statements (with different semantics), this would create unnecessary confusion or require special-casing (e.g. to forbid assignment within the headers of these statements).

(Note that with EXPR as VAR does not simply assign the value of EXPR to VAR – it calls EXPR.__enter__() and assigns the result of that to VAR .)

Additional reasons to prefer := over this spelling include:

  • In if f(x) as y the assignment target doesn’t jump out at you – it just reads like if f x blah blah and it is too similar visually to if f(x) and y .
  • import foo as bar
  • except Exc as var
  • with ctxmgr() as var

To the contrary, the assignment expression does not belong to the if or while that starts the line, and we intentionally allow assignment expressions in other contexts as well.

  • NAME = EXPR
  • if NAME := EXPR

reinforces the visual recognition of assignment expressions.

This syntax is inspired by languages such as R and Haskell, and some programmable calculators. (Note that a left-facing arrow y <- f(x) is not possible in Python, as it would be interpreted as less-than and unary minus.) This syntax has a slight advantage over ‘as’ in that it does not conflict with with , except and import , but otherwise is equivalent. But it is entirely unrelated to Python’s other use of -> (function return type annotations), and compared to := (which dates back to Algol-58) it has a much weaker tradition.

This has the advantage that leaked usage can be readily detected, removing some forms of syntactic ambiguity. However, this would be the only place in Python where a variable’s scope is encoded into its name, making refactoring harder.

Execution order is inverted (the indented body is performed first, followed by the “header”). This requires a new keyword, unless an existing keyword is repurposed (most likely with: ). See PEP 3150 for prior discussion on this subject (with the proposed keyword being given: ).

This syntax has fewer conflicts than as does (conflicting only with the raise Exc from Exc notation), but is otherwise comparable to it. Instead of paralleling with expr as target: (which can be useful but can also be confusing), this has no parallels, but is evocative.

One of the most popular use-cases is if and while statements. Instead of a more general solution, this proposal enhances the syntax of these two statements to add a means of capturing the compared value:

This works beautifully if and ONLY if the desired condition is based on the truthiness of the captured value. It is thus effective for specific use-cases (regex matches, socket reads that return '' when done), and completely useless in more complicated cases (e.g. where the condition is f(x) < 0 and you want to capture the value of f(x) ). It also has no benefit to list comprehensions.

Advantages: No syntactic ambiguities. Disadvantages: Answers only a fraction of possible use-cases, even in if / while statements.

Another common use-case is comprehensions (list/set/dict, and genexps). As above, proposals have been made for comprehension-specific solutions.

This brings the subexpression to a location in between the ‘for’ loop and the expression. It introduces an additional language keyword, which creates conflicts. Of the three, where reads the most cleanly, but also has the greatest potential for conflict (e.g. SQLAlchemy and numpy have where methods, as does tkinter.dnd.Icon in the standard library).

As above, but reusing the with keyword. Doesn’t read too badly, and needs no additional language keyword. Is restricted to comprehensions, though, and cannot as easily be transformed into “longhand” for-loop syntax. Has the C problem that an equals sign in an expression can now create a name binding, rather than performing a comparison. Would raise the question of why “with NAME = EXPR:” cannot be used as a statement on its own.

As per option 2, but using as rather than an equals sign. Aligns syntactically with other uses of as for name binding, but a simple transformation to for-loop longhand would create drastically different semantics; the meaning of with inside a comprehension would be completely different from the meaning as a stand-alone statement, while retaining identical syntax.

Regardless of the spelling chosen, this introduces a stark difference between comprehensions and the equivalent unrolled long-hand form of the loop. It is no longer possible to unwrap the loop into statement form without reworking any name bindings. The only keyword that can be repurposed to this task is with , thus giving it sneakily different semantics in a comprehension than in a statement; alternatively, a new keyword is needed, with all the costs therein.

There are two logical precedences for the := operator. Either it should bind as loosely as possible, as does statement-assignment; or it should bind more tightly than comparison operators. Placing its precedence between the comparison and arithmetic operators (to be precise: just lower than bitwise OR) allows most uses inside while and if conditions to be spelled without parentheses, as it is most likely that you wish to capture the value of something, then perform a comparison on it:

Once find() returns -1, the loop terminates. If := binds as loosely as = does, this would capture the result of the comparison (generally either True or False ), which is less useful.

While this behaviour would be convenient in many situations, it is also harder to explain than “the := operator behaves just like the assignment statement”, and as such, the precedence for := has been made as close as possible to that of = (with the exception that it binds tighter than comma).

Some critics have claimed that the assignment expressions should allow unparenthesized tuples on the right, so that these two would be equivalent:

(With the current version of the proposal, the latter would be equivalent to ((point := x), y) .)

However, adopting this stance would logically lead to the conclusion that when used in a function call, assignment expressions also bind less tight than comma, so we’d have the following confusing equivalence:

The less confusing option is to make := bind more tightly than comma.

It’s been proposed to just always require parentheses around an assignment expression. This would resolve many ambiguities, and indeed parentheses will frequently be needed to extract the desired subexpression. But in the following cases the extra parentheses feel redundant:

Frequently Raised Objections

C and its derivatives define the = operator as an expression, rather than a statement as is Python’s way. This allows assignments in more contexts, including contexts where comparisons are more common. The syntactic similarity between if (x == y) and if (x = y) belies their drastically different semantics. Thus this proposal uses := to clarify the distinction.

The two forms have different flexibilities. The := operator can be used inside a larger expression; the = statement can be augmented to += and its friends, can be chained, and can assign to attributes and subscripts.

Previous revisions of this proposal involved sublocal scope (restricted to a single statement), preventing name leakage and namespace pollution. While a definite advantage in a number of situations, this increases complexity in many others, and the costs are not justified by the benefits. In the interests of language simplicity, the name bindings created here are exactly equivalent to any other name bindings, including that usage at class or module scope will create externally-visible names. This is no different from for loops or other constructs, and can be solved the same way: del the name once it is no longer needed, or prefix it with an underscore.

(The author wishes to thank Guido van Rossum and Christoph Groth for their suggestions to move the proposal in this direction. [2] )

As expression assignments can sometimes be used equivalently to statement assignments, the question of which should be preferred will arise. For the benefit of style guides such as PEP 8 , two recommendations are suggested.

  • If either assignment statements or assignment expressions can be used, prefer statements; they are a clear declaration of intent.
  • If using assignment expressions would lead to ambiguity about execution order, restructure it to use statements instead.

The authors wish to thank Alyssa Coghlan and Steven D’Aprano for their considerable contributions to this proposal, and members of the core-mentorship mailing list for assistance with implementation.

Appendix A: Tim Peters’s findings

Here’s a brief essay Tim Peters wrote on the topic.

I dislike “busy” lines of code, and also dislike putting conceptually unrelated logic on a single line. So, for example, instead of:

instead. So I suspected I’d find few places I’d want to use assignment expressions. I didn’t even consider them for lines already stretching halfway across the screen. In other cases, “unrelated” ruled:

is a vast improvement over the briefer:

The original two statements are doing entirely different conceptual things, and slamming them together is conceptually insane.

In other cases, combining related logic made it harder to understand, such as rewriting:

as the briefer:

The while test there is too subtle, crucially relying on strict left-to-right evaluation in a non-short-circuiting or method-chaining context. My brain isn’t wired that way.

But cases like that were rare. Name binding is very frequent, and “sparse is better than dense” does not mean “almost empty is better than sparse”. For example, I have many functions that return None or 0 to communicate “I have nothing useful to return in this case, but since that’s expected often I’m not going to annoy you with an exception”. This is essentially the same as regular expression search functions returning None when there is no match. So there was lots of code of the form:

I find that clearer, and certainly a bit less typing and pattern-matching reading, as:

It’s also nice to trade away a small amount of horizontal whitespace to get another _line_ of surrounding code on screen. I didn’t give much weight to this at first, but it was so very frequent it added up, and I soon enough became annoyed that I couldn’t actually run the briefer code. That surprised me!

There are other cases where assignment expressions really shine. Rather than pick another from my code, Kirill Balunov gave a lovely example from the standard library’s copy() function in copy.py :

The ever-increasing indentation is semantically misleading: the logic is conceptually flat, “the first test that succeeds wins”:

Using easy assignment expressions allows the visual structure of the code to emphasize the conceptual flatness of the logic; ever-increasing indentation obscured it.

A smaller example from my code delighted me, both allowing to put inherently related logic in a single line, and allowing to remove an annoying “artificial” indentation level:

That if is about as long as I want my lines to get, but remains easy to follow.

So, in all, in most lines binding a name, I wouldn’t use assignment expressions, but because that construct is so very frequent, that leaves many places I would. In most of the latter, I found a small win that adds up due to how often it occurs, and in the rest I found a moderate to major win. I’d certainly use it more often than ternary if , but significantly less often than augmented assignment.

I have another example that quite impressed me at the time.

Where all variables are positive integers, and a is at least as large as the n’th root of x, this algorithm returns the floor of the n’th root of x (and roughly doubling the number of accurate bits per iteration):

It’s not obvious why that works, but is no more obvious in the “loop and a half” form. It’s hard to prove correctness without building on the right insight (the “arithmetic mean - geometric mean inequality”), and knowing some non-trivial things about how nested floor functions behave. That is, the challenges are in the math, not really in the coding.

If you do know all that, then the assignment-expression form is easily read as “while the current guess is too large, get a smaller guess”, where the “too large?” test and the new guess share an expensive sub-expression.

To my eyes, the original form is harder to understand:

This appendix attempts to clarify (though not specify) the rules when a target occurs in a comprehension or in a generator expression. For a number of illustrative examples we show the original code, containing a comprehension, and the translation, where the comprehension has been replaced by an equivalent generator function plus some scaffolding.

Since [x for ...] is equivalent to list(x for ...) these examples all use list comprehensions without loss of generality. And since these examples are meant to clarify edge cases of the rules, they aren’t trying to look like real code.

Note: comprehensions are already implemented via synthesizing nested generator functions like those in this appendix. The new part is adding appropriate declarations to establish the intended scope of assignment expression targets (the same scope they resolve to as if the assignment were performed in the block containing the outermost comprehension). For type inference purposes, these illustrative expansions do not imply that assignment expression targets are always Optional (but they do indicate the target binding scope).

Let’s start with a reminder of what code is generated for a generator expression without assignment expression.

  • Original code (EXPR usually references VAR): def f (): a = [ EXPR for VAR in ITERABLE ]
  • Translation (let’s not worry about name conflicts): def f (): def genexpr ( iterator ): for VAR in iterator : yield EXPR a = list ( genexpr ( iter ( ITERABLE )))

Let’s add a simple assignment expression.

  • Original code: def f (): a = [ TARGET := EXPR for VAR in ITERABLE ]
  • Translation: def f (): if False : TARGET = None # Dead code to ensure TARGET is a local variable def genexpr ( iterator ): nonlocal TARGET for VAR in iterator : TARGET = EXPR yield TARGET a = list ( genexpr ( iter ( ITERABLE )))

Let’s add a global TARGET declaration in f() .

  • Original code: def f (): global TARGET a = [ TARGET := EXPR for VAR in ITERABLE ]
  • Translation: def f (): global TARGET def genexpr ( iterator ): global TARGET for VAR in iterator : TARGET = EXPR yield TARGET a = list ( genexpr ( iter ( ITERABLE )))

Or instead let’s add a nonlocal TARGET declaration in f() .

  • Original code: def g (): TARGET = ... def f (): nonlocal TARGET a = [ TARGET := EXPR for VAR in ITERABLE ]
  • Translation: def g (): TARGET = ... def f (): nonlocal TARGET def genexpr ( iterator ): nonlocal TARGET for VAR in iterator : TARGET = EXPR yield TARGET a = list ( genexpr ( iter ( ITERABLE )))

Finally, let’s nest two comprehensions.

  • Original code: def f (): a = [[ TARGET := i for i in range ( 3 )] for j in range ( 2 )] # I.e., a = [[0, 1, 2], [0, 1, 2]] print ( TARGET ) # prints 2
  • Translation: def f (): if False : TARGET = None def outer_genexpr ( outer_iterator ): nonlocal TARGET def inner_generator ( inner_iterator ): nonlocal TARGET for i in inner_iterator : TARGET = i yield i for j in outer_iterator : yield list ( inner_generator ( range ( 3 ))) a = list ( outer_genexpr ( range ( 2 ))) print ( TARGET )

Because it has been a point of confusion, note that nothing about Python’s scoping semantics is changed. Function-local scopes continue to be resolved at compile time, and to have indefinite temporal extent at run time (“full closures”). Example:

This document has been placed in the public domain.

Source: https://github.com/python/peps/blob/main/peps/pep-0572.rst

Last modified: 2023-10-11 12:05:51 GMT

UnboundLocalError not triggered when using exec

When you write code like:

You get an UnboundLocalError exception at runtime. But, you can circumvent it by doing:

Which prints 1 instead. I was trying to find if this is documented behaviour, or is it potentially a bug and could be fixed.

Now that’s a head scratcher - great question!

I’m guessing a helpful UnBoundLocalError was added to discourage software design decisions that will inevitably be regretted. But the implementation of the warning mechanism doesn’t look into the args given to exec, as that could be given any arbitrary string or code object after all.

Note exec is executing in local scope:

Not really:

The behavior of exec without explicitly given locals inside of functions is confusing, and should probably just be disallowed IMO.

Are you sure of that? When I do that, it prints 1 each time.

Aha, yep, that behavior changed in python 3.13. In 3.11 and 3.12 I get 2 1 1 , in 3.13 I get 1 1 1 .

Which is another reason why this behavior is not something that should be relied upon…

Yeah, here’s the What’s New entry: What’s New In Python 3.13 — Python 3.13.0b1 documentation

This is not actually a change in defined behaviour; it’s giving definitions for things that were implementation-defined.

No, not really. The behaviour of the first code snippet is an extremely well known issue that generates frequent duplicate links on Stack Overflow:

The error isn’t a “warning mechanism”; it’s a consequence of how the code was understood.

And of course using exec as a workaround causes the code to run without error - because the code being exec ’d is just a string, so the compiler has no reason to consider what effect it could potentially have at runtime. And indeed, if we disassemble it (this disassembly from 3.8):

We can clearly see that x is still understood to mean a local variable - since there is still an x = 2 assignment, and no global declaration.

And, of course, when the code print(x) is exec d, it’s compiled at that moment, independently of the function where the exec call was made. Because that’s what exec means, and what it’s for. Its purpose is to interpret a string as if it were Python code, in the current context - i.e., in isolation , at the point when and where it’s called.

No; it’s executing in the scope that was passed :

So, eval("print(x)") will use the function’s locals as locals, and the current globals as globals. But because there is no assignment in the code string , it will compile such that x is looked up via LOAD_NAME - i.e., it will check globals after failing to find x in locals.

I don’t really see why. It’s following the same rules that led to UnboundLocalError in the first place.

This, on the other hand, is interesting. It does make sense that the separate exec calls should be “isolated” that way.

… Actually, thinking about it a bit more, the documentation seems misleading when it says “defaulting to the current globals and locals”. That strongly implies that those namespaces could be modified by the work done by the exec’d code - since it says it’s using those actual namespaces, not copies.

Yeah. Here’s a bit more detail: Calling exec(str) is like calling exec(str, globals(), locals()) . It doesn’t actually execute the code in the function’s actual scope, it executes it using locals() as a scope. That sounds like a distinction without a difference, but it’s important because locals() isn’t supposed to be mutated:

This warning goes all the way back to Python 2, although the exec statement had somewhat different behaviour in Python 2 and I’m not going into that here.

So, we really have code like this:

and the Python language didn’t previously stipulate which behaviour was correct. It’s entirely valid for a Python interpreter to completely ignore changes to locals() , and it’s also entirely valid to have those changes reflected in the actual function locals. Or some hybrid, like “changes to existing variables are valid, but new variables won’t be created”. Or “non-local names can be changed, others can’t”. Or “mutations take effect on Tuesdays but not on Fridays”. All would be conformant behaviour.

(Note that changes to globals() are well defined. If you used "global x; x=2" and "global x; print(x)" in the examples, it would affect and display the global. It’s only locals that are like this.)

That changed in PEP 667 . We now have well-defined behaviour. (I apologise for previously assuming PEP 667 semantics and forgetting how recently it was applied; when you live at the bleeding edge, it’s easy to forget which features have been around for how long - and that goes doubly so for things that are more subtle.) If you want the change to be applied, you can peek in the stack frame’s locals, but if you don’t, you now have a guarantee that locals() is just a copy.

There’s a clarifying note at the bottom of the function’s docs which elaborates a bit.

What I’m getting is that exec essentially has its own execution context, and it just happens to inherit a copy of the current locals.

is somewhat analogous to:

… at least somewhat. it does have its own scope, but only lived until exec returns.

which is why this should not raise UnboundLocalError . Is that about right?

:slight_smile:

Related Topics

Topic Replies Views Activity
Python Help 2 903 September 8, 2023
Python Help 9 9704 June 2, 2023
Python Help 9 3141 March 18, 2024
Python Help 3 3376 April 14, 2023
Python Help 3 2945 March 17, 2021
  • Python Basics
  • Interview Questions
  • Python Quiz
  • Popular Packages
  • Python Projects
  • Practice Python
  • AI With Python
  • Learn Python3
  • Python Automation
  • Python Web Dev
  • DSA with Python
  • Python OOPs
  • Dictionaries

Python Operators

Precedence and associativity of operators in python.

  • Python Arithmetic Operators
  • Difference between / vs. // operator in Python
  • Python - Star or Asterisk operator ( * )
  • What does the Double Star operator mean in Python?
  • Division Operators in Python
  • Modulo operator (%) in Python
  • Python Logical Operators
  • Python OR Operator
  • Difference between 'and' and '&' in Python
  • not Operator in Python | Boolean Logic

Ternary Operator in Python

  • Python Bitwise Operators

Python Assignment Operators

Assignment operators in python.

  • Walrus Operator in Python 3.8
  • Increment += and Decrement -= Assignment Operators in Python
  • Merging and Updating Dictionary Operators in Python 3.9
  • New '=' Operator in Python3.8 f-string

Python Relational Operators

  • Comparison Operators in Python
  • Python NOT EQUAL operator
  • Difference between == and is operator in Python
  • Chaining comparison operators in Python
  • Python Membership and Identity Operators
  • Difference between != and is not operator in Python

In Python programming, Operators in general are used to perform operations on values and variables. These are standard symbols used for logical and arithmetic operations. In this article, we will look into different types of Python operators. 

  • OPERATORS: These are the special symbols. Eg- + , * , /, etc.
  • OPERAND: It is the value on which the operator is applied.

Types of Operators in Python

  • Arithmetic Operators
  • Comparison Operators
  • Logical Operators
  • Bitwise Operators
  • Assignment Operators
  • Identity Operators and Membership Operators

Python Operators

Arithmetic Operators in Python

Python Arithmetic operators are used to perform basic mathematical operations like addition, subtraction, multiplication , and division .

In Python 3.x the result of division is a floating-point while in Python 2.x division of 2 integers was an integer. To obtain an integer result in Python 3.x floored (// integer) is used.

OperatorDescriptionSyntax
+Addition: adds two operandsx + y
Subtraction: subtracts two operandsx – y
*Multiplication: multiplies two operandsx * y
/Division (float): divides the first operand by the secondx / y
//Division (floor): divides the first operand by the secondx // y
%Modulus: returns the remainder when the first operand is divided by the secondx % y
**Power: Returns first raised to power secondx ** y

Example of Arithmetic Operators in Python

Division operators.

In Python programming language Division Operators allow you to divide two numbers and return a quotient, i.e., the first number or number at the left is divided by the second number or number at the right and returns the quotient. 

There are two types of division operators: 

Float division

  • Floor division

The quotient returned by this operator is always a float number, no matter if two numbers are integers. For example:

Example: The code performs division operations and prints the results. It demonstrates that both integer and floating-point divisions return accurate results. For example, ’10/2′ results in ‘5.0’ , and ‘-10/2’ results in ‘-5.0’ .

Integer division( Floor division)

The quotient returned by this operator is dependent on the argument being passed. If any of the numbers is float, it returns output in float. It is also known as Floor division because, if any number is negative, then the output will be floored. For example:

Example: The code demonstrates integer (floor) division operations using the // in Python operators . It provides results as follows: ’10//3′ equals ‘3’ , ‘-5//2’ equals ‘-3’ , ‘ 5.0//2′ equals ‘2.0’ , and ‘-5.0//2’ equals ‘-3.0’ . Integer division returns the largest integer less than or equal to the division result.

Precedence of Arithmetic Operators in Python

The precedence of Arithmetic Operators in Python is as follows:

  • P – Parentheses
  • E – Exponentiation
  • M – Multiplication (Multiplication and division have the same precedence)
  • D – Division
  • A – Addition (Addition and subtraction have the same precedence)
  • S – Subtraction

The modulus of Python operators helps us extract the last digit/s of a number. For example:

  • x % 10 -> yields the last digit
  • x % 100 -> yield last two digits

Arithmetic Operators With Addition, Subtraction, Multiplication, Modulo and Power

Here is an example showing how different Arithmetic Operators in Python work:

Example: The code performs basic arithmetic operations with the values of ‘a’ and ‘b’ . It adds (‘+’) , subtracts (‘-‘) , multiplies (‘*’) , computes the remainder (‘%’) , and raises a to the power of ‘b (**)’ . The results of these operations are printed.

Note: Refer to Differences between / and // for some interesting facts about these two Python operators.

Comparison of Python Operators

In Python Comparison of Relational operators compares the values. It either returns True or False according to the condition.

OperatorDescriptionSyntax
>Greater than: True if the left operand is greater than the rightx > y
<Less than: True if the left operand is less than the rightx < y
==Equal to: True if both operands are equalx == y
!=Not equal to – True if operands are not equalx != y
>=Greater than or equal to True if the left operand is greater than or equal to the rightx >= y
<=Less than or equal to True if the left operand is less than or equal to the rightx <= y

= is an assignment operator and == comparison operator.

Precedence of Comparison Operators in Python

In Python, the comparison operators have lower precedence than the arithmetic operators. All the operators within comparison operators have the same precedence order.

Example of Comparison Operators in Python

Let’s see an example of Comparison Operators in Python.

Example: The code compares the values of ‘a’ and ‘b’ using various comparison Python operators and prints the results. It checks if ‘a’ is greater than, less than, equal to, not equal to, greater than, or equal to, and less than or equal to ‘b’ .

Logical Operators in Python

Python Logical operators perform Logical AND , Logical OR , and Logical NOT operations. It is used to combine conditional statements.

OperatorDescriptionSyntax
andLogical AND: True if both the operands are truex and y
orLogical OR: True if either of the operands is true x or y
notLogical NOT: True if the operand is false not x

Precedence of Logical Operators in Python

The precedence of Logical Operators in Python is as follows:

  • Logical not
  • logical and

Example of Logical Operators in Python

The following code shows how to implement Logical Operators in Python:

Example: The code performs logical operations with Boolean values. It checks if both ‘a’ and ‘b’ are true ( ‘and’ ), if at least one of them is true ( ‘or’ ), and negates the value of ‘a’ using ‘not’ . The results are printed accordingly.

Bitwise Operators in Python

Python Bitwise operators act on bits and perform bit-by-bit operations. These are used to operate on binary numbers.

OperatorDescriptionSyntax
&Bitwise ANDx & y
|Bitwise ORx | y
~Bitwise NOT~x
^Bitwise XORx ^ y
>>Bitwise right shiftx>>
<<Bitwise left shiftx<<

Precedence of Bitwise Operators in Python

The precedence of Bitwise Operators in Python is as follows:

  • Bitwise NOT
  • Bitwise Shift
  • Bitwise AND
  • Bitwise XOR

Here is an example showing how Bitwise Operators in Python work:

Example: The code demonstrates various bitwise operations with the values of ‘a’ and ‘b’ . It performs bitwise AND (&) , OR (|) , NOT (~) , XOR (^) , right shift (>>) , and left shift (<<) operations and prints the results. These operations manipulate the binary representations of the numbers.

Python Assignment operators are used to assign values to the variables.

OperatorDescriptionSyntax
=Assign the value of the right side of the expression to the left side operand x = y + z
+=Add AND: Add right-side operand with left-side operand and then assign to left operanda+=b     a=a+b
-=Subtract AND: Subtract right operand from left operand and then assign to left operanda-=b     a=a-b
*=Multiply AND: Multiply right operand with left operand and then assign to left operanda*=b     a=a*b
/=Divide AND: Divide left operand with right operand and then assign to left operanda/=b     a=a/b
%=Modulus AND: Takes modulus using left and right operands and assign the result to left operanda%=b     a=a%b
//=Divide(floor) AND: Divide left operand with right operand and then assign the value(floor) to left operanda//=b     a=a//b
**=Exponent AND: Calculate exponent(raise power) value using operands and assign value to left operanda**=b     a=a**b
&=Performs Bitwise AND on operands and assign value to left operanda&=b     a=a&b
|=Performs Bitwise OR on operands and assign value to left operanda|=b     a=a|b
^=Performs Bitwise xOR on operands and assign value to left operanda^=b     a=a^b
>>=Performs Bitwise right shift on operands and assign value to left operanda>>=b     a=a>>b
<<=Performs Bitwise left shift on operands and assign value to left operanda <<= b     a= a << b

Let’s see an example of Assignment Operators in Python.

Example: The code starts with ‘a’ and ‘b’ both having the value 10. It then performs a series of operations: addition, subtraction, multiplication, and a left shift operation on ‘b’ . The results of each operation are printed, showing the impact of these operations on the value of ‘b’ .

Identity Operators in Python

In Python, is and is not are the identity operators both are used to check if two values are located on the same part of the memory. Two variables that are equal do not imply that they are identical. 

Example Identity Operators in Python

Let’s see an example of Identity Operators in Python.

Example: The code uses identity operators to compare variables in Python. It checks if ‘a’ is not the same object as ‘b’ (which is true because they have different values) and if ‘a’ is the same object as ‘c’ (which is true because ‘c’ was assigned the value of ‘a’ ).

Membership Operators in Python

In Python, in and not in are the membership operators that are used to test whether a value or variable is in a sequence.

Examples of Membership Operators in Python

The following code shows how to implement Membership Operators in Python:

Example: The code checks for the presence of values ‘x’ and ‘y’ in the list. It prints whether or not each value is present in the list. ‘x’ is not in the list, and ‘y’ is present, as indicated by the printed messages. The code uses the ‘in’ and ‘not in’ Python operators to perform these checks.

in Python, Ternary operators also known as conditional expressions are operators that evaluate something based on a condition being true or false. It was added to Python in version 2.5. 

It simply allows testing a condition in a single line replacing the multiline if-else making the code compact.

Syntax :   [on_true] if [expression] else [on_false] 

Examples of Ternary Operator in Python

The code assigns values to variables ‘a’ and ‘b’ (10 and 20, respectively). It then uses a conditional assignment to determine the smaller of the two values and assigns it to the variable ‘min’ . Finally, it prints the value of ‘min’ , which is 10 in this case.

In Python, Operator precedence and associativity determine the priorities of the operator.

Operator Precedence in Python

This is used in an expression with more than one operator with different precedence to determine which operation to perform first.

Let’s see an example of how Operator Precedence in Python works:

Example: The code first calculates and prints the value of the expression 10 + 20 * 30 , which is 610. Then, it checks a condition based on the values of the ‘name’ and ‘age’ variables. Since the name is “ Alex” and the condition is satisfied using the or operator, it prints “Hello! Welcome.”

Operator Associativity in Python

If an expression contains two or more operators with the same precedence then Operator Associativity is used to determine. It can either be Left to Right or from Right to Left.

The following code shows how Operator Associativity in Python works:

Example: The code showcases various mathematical operations. It calculates and prints the results of division and multiplication, addition and subtraction, subtraction within parentheses, and exponentiation. The code illustrates different mathematical calculations and their outcomes.

To try your knowledge of Python Operators, you can take out the quiz on Operators in Python . 

Python Operator Exercise Questions

Below are two Exercise Questions on Python Operators. We have covered arithmetic operators and comparison operators in these exercise questions. For more exercises on Python Operators visit the page mentioned below.

Q1. Code to implement basic arithmetic operations on integers

Q2. Code to implement Comparison operations on integers

Explore more Exercises: Practice Exercise on Operators in Python

Please Login to comment...

Similar reads.

  • python-basics
  • Python-Operators

Improve your Coding Skills with Practice

 alt=

What kind of Experience do you want to share?

  • Stack Overflow Public questions & answers
  • Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers
  • Talent Build your employer brand
  • Advertising Reach developers & technologists worldwide
  • Labs The future of collective knowledge sharing
  • About the company

Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Get early access and see previews of new features.

Mass variable declaration and assignment in Python

Trying to create a batch of dictionaries:

I would rather do something like: January, February, March... = {}

which of course doesn't work.

Ultimately, I'm wanting to create a dictionary of these dictionaries:

Its not a ton of code to do it line by line but I'm just learning Python and.... doing something repetitive typically tells me it can be done more efficiently in some other way.

p.s. I'm working with python 2.x but if using 3 for this example would be of some help, that's not a problem.

ldz's user avatar

  • "doing something repetitive typically tells me it can be done more efficiently in some other way". Correct. But loading up a lot of dictionaries with pre-cooked keys is probably wrong, also. Look at collections.defaultdict . –  S.Lott Apr 26, 2011 at 17:14
  • Create classes, dude. You're trying to re-invent the wheel with primitive types there. Encapsulate the details, and work on a higher level. OOP 101 –  Zoran Pavlovic Dec 27, 2012 at 13:18
  • 1 @ZoranPavlovic This was actually before I knew OOP 101 :) However, I've needed this type of behavior ('mass' variable assignment) in more situations, so the question probably still has some use on that ground. –  chris Dec 27, 2012 at 15:17

3 Answers 3

That's a little bit more concise way to do the initial declaration.

chisaipete's user avatar

Why do you want to month names as variables if you don't use them? You could simply write

Jochen Ritzel's user avatar

  • 1 Slightly more condensed: dict((%2d" % i, {}) for i in range(1, 13)) (and in 2.7+ you can use a dictionary comprehension, saving a few parens). –  user395760 Apr 26, 2011 at 15:37
  • Parsing a txt file with several thousand lines, each line set to a particular month (in no order) as '01', '02', etc. Was planning on using the above method to parse the month out of the line, then doing MONTHS_DICT[line[3:5]] to select the proper month dictionary (january, february), at which point I'll add the data of interest to it. –  chris Apr 26, 2011 at 15:41
  • lol...realized I just gave an answer to the wrong question. At end of program I'm writing data to a new file; was planning on using the name of dict as the Row name. Ended up just getting curious as to how batch variables can be assigned. –  chris Apr 26, 2011 at 15:58
  • @george: Yeah, that's why you don't need variables like March = {} - you can't turn a string like "03" into the variable March . With the dict I've given it is you can do what you want: this_lines_month = MONTHS_DICT[line[3:5]]; this_lines_month['data_of_interest'] = line . To turn the number of a month into it's name you just need another dictionary: {'01' : 'January', etc ...} - you can construct that dictionary in the same way as above. –  Jochen Ritzel Apr 26, 2011 at 15:59
  • Answers the question I asked and the one I didn't - kudos :) –  chris Apr 26, 2011 at 16:08

You can do:

But you can NOT do:

without having all of foo , bar and baz point to the same variable due to reference!

The most concise way to assign empty dictionaries to multiple variables is probably:

deed02392's user avatar

Your Answer

Reminder: Answers generated by artificial intelligence tools are not allowed on Stack Overflow. Learn more

Sign up or log in

Post as a guest.

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy .

Not the answer you're looking for? Browse other questions tagged python python-2.x or ask your own question .

  • The Overflow Blog
  • Introducing Staging Ground: The private space to get feedback on questions...
  • Featured on Meta
  • The return of Staging Ground to Stack Overflow
  • The [tax] tag is being burninated
  • The 2024 Developer Survey Is Live
  • Policy: Generative AI (e.g., ChatGPT) is banned

Hot Network Questions

  • How do you keep the horror spooky when your players are a bunch of goofballs?
  • Recovering a group using tannaka reconstruction
  • Can I paraphrase an conference paper I wrote in my dissertation?
  • Handling cases of "potential" ChatGPT-generated reviews in non-anonymous program committees (as a PC member)
  • Show this process is Brownian motion
  • Under physicalism, should I still be sad if my murdered wife is replaced with a perfect clone?
  • Asymptotic behavior of a ratio involving Bessel K functions
  • "Mandatory reservation" on Off-Peak ticket?
  • What legal reason, if any, does my bank have to know if I am a dual citizen of the US?
  • Effects if a human was shot by a femtosecond laser
  • Are your memories part of you?
  • What should I get paid for if I can't work due to circumstances outside of my control?
  • My vehicle shut off in traffic and will not turn on
  • On a planet with 6 moons, how often would all 6 be full at the same time?
  • Why does the proposed Lunar Crater Radio Telescope suggest an optimal latitude of 20 degrees North?
  • correct way to add explanatory text to (multiline) display equations in Lyx?
  • What is the frequentist's Bayesian prior for a coin with unknown bias
  • What is the U.N. list of shame and how does it affect Israel which was recently added?
  • Complexity of definable global choice functions
  • Is obeying the parallelogram law of vector addition sufficient to make a physical quantity qualify as a vector?
  • Moving after copying in assignment of conditional operator result
  • A man is kidnapped by his future descendants and isolated his whole life to prevent a bad thing; they accidentally undo their own births
  • Looping counter extended
  • Build the first 6 letters of an Italian codice fiscale (tax identification number)

assignment declaration python

IMAGES

  1. Declaration and initialisation variables in python : Py4ML Part 1

    assignment declaration python

  2. The right way to declare multiple variables in Python

    assignment declaration python

  3. Assignment Statement in Python

    assignment declaration python

  4. Assignment Operators in Python

    assignment declaration python

  5. Assignment Operator in Python

    assignment declaration python

  6. Variables Declaration, Assignment and Python Keywords (Lesson 3)

    assignment declaration python

VIDEO

  1. Assignment

  2. Variable Declaration in Python

  3. Variable Declaration in Python: x = 5

  4. 9618 Pseudocode to Python: 01. Literals, Constants, Assignment, Declaration, Data Types

  5. 4_Variable declaration in python

  6. Variables declaration in Python

COMMENTS

  1. Python's Assignment Operator: Write Robust Assignments

    Here, variable represents a generic Python variable, while expression represents any Python object that you can provide as a concrete value—also known as a literal—or an expression that evaluates to a value. To execute an assignment statement like the above, Python runs the following steps: Evaluate the right-hand expression to produce a concrete value or object.

  2. Is it possible only to declare a variable without assigning any value

    483. Why not just do this: var = None. Python is dynamic, so you don't need to declare things; they exist automatically in the first scope where they're assigned. So, all you need is a regular old assignment statement as above. This is nice, because you'll never end up with an uninitialized variable.

  3. 7. Simple statements

    An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right. Assignment is defined recursively depending on the form of the target (list).

  4. Different Forms of Assignment Statements in Python

    Multiple- target assignment: x = y = 75. print(x, y) In this form, Python assigns a reference to the same object (the object which is rightmost) to all the target on the left. OUTPUT. 75 75. 7. Augmented assignment : The augmented assignment is a shorthand assignment that combines an expression and an assignment.

  5. Variables and Assignment

    Variables and Assignment¶. When programming, it is useful to be able to store information in variables. A variable is a string of characters and numbers associated with a piece of information. The assignment operator, denoted by the "=" symbol, is the operator that is used to assign values to variables in Python.The line x=1 takes the known value, 1, and assigns that value to the variable ...

  6. 1.6. Variables and Assignment

    A variable is a name for a value. An assignment statement associates a variable name on the left of the equal sign with the value of an expression calculated from the right of the equal sign. Enter. width. Once a variable is assigned a value, the variable can be used in place of that value. The response to the expression width is the same as if ...

  7. Python Variables and Assignment

    A Python variable is a named bit of computer memory, keeping track of a value as the code runs. A variable is created with an "assignment" equal sign =, with the variable's name on the left and the value it should store on the right: x = 42. In the computer's memory, each variable is like a box, identified by the name of the variable.

  8. Python Variables

    Example Get your own Python Server. x = 5. y = "John". print(x) print(y) Try it Yourself ». Variables do not need to be declared with any particular type, and can even change type after they have been set.

  9. Assignment Operators in Python

    Assignment Operator. Assignment Operators are used to assign values to variables. This operator is used to assign the value of the right side of the expression to the left side operand. Python. # Assigning values using # Assignment Operator a = 3 b = 5 c = a + b # Output print(c) Output. 8.

  10. Python Variables

    Python Variable is containers that store values. Python is not "statically typed". We do not need to declare variables before using them or declare their type. A variable is created the moment we first assign a value to it. A Python variable is a name given to a memory location. It is the basic unit of storage in a program.

  11. Python Function Examples

    How to Define a Function in Python. The general syntax for creating a function in Python looks something like this: def function_name(parameters): function body. Let's break down what's happening here: def is a keyword that tells Python a new function is being defined. Next comes a valid function name of your choosing.

  12. Understanding Variables in Python: Declaration, Assignment, and Naming

    In Python, variables are declared by simply assigning a value to them. Unlike some other programming languages, Python does not require explicit declaration of variables or specifying their data types. the variable's data type is inferred based on the value assigned to it. this process is known as variable assignment, which can be done using the equals sign "=" (also known as the assignment ...

  13. typing

    The Python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers, IDEs, linters, etc. ... # Using ``TypeAlias`` tells the type checker that this is a type alias declaration, # not a variable assignment to a string. BoxOfStrings: TypeAlias = "Box[str]" class Box ...

  14. Python Assignment Operators

    Python Assignment Operators. Assignment operators are used to assign values to variables: Operator. Example. Same As. Try it. =. x = 5. x = 5.

  15. language design

    If you have closures, you need to precisely declare which scope a variable belongs to. Imagine a language without a declaration keyword, and implicit declaration through assignment (e.g. PHP or Python). Both of these are syntactically challenged with respect to closures, because they either ascribe a variable to the outermost or innermost ...

  16. Multiple assignment in Python: Assign multiple values or the same value

    None in Python; Create calendar as text, HTML, list in Python; NumPy: Insert elements, rows, and columns into an array with np.insert() Shuffle a list, string, tuple in Python (random.shuffle, sample) Add and update an item in a dictionary in Python; Cartesian product of lists in Python (itertools.product) Remove a substring from a string in Python

  17. Assign variable in while loop condition in Python?

    Starting Python 3.8, and the introduction of assignment expressions (PEP 572) ( := operator), it's now possible to capture the condition value ( data.readline()) of the while loop as a variable ( line) in order to re-use it within the body of the loop: while line := data.readline(): do_smthg(line)

  18. PEP 572

    An assignment expression does not introduce a new scope. In most cases the scope in which the target will be bound is self-explanatory: it is the current scope. If this scope contains a nonlocal or global declaration for the target, the assignment expression honors that. A lambda (being an explicit, if anonymous, function definition) counts as ...

  19. UnboundLocalError not triggered when using exec

    We can clearly see that x is still understood to mean a local variable - since there is still an x = 2 assignment, and no global declaration. And, of course, when the code print(x) ... The Python interpreter has a number of functions and types built into it that are always available. They are listed here in alphabetical order.,,,, Built-in ...

  20. Python Variable assignment in a for loop

    print(x) (Note I expanded x += 3 to x = x + 3 to increase visibility for the name accesses - read and write.) First, you bind the list [1, 2, 3] to the name a. Then, you iterate over the list. During each iteration, the value is bound to the name x in the current scope. Your assignment then assigns another value to x.

  21. Python Operators

    In Python programming, Operators in general are used to perform operations on values and variables. These are standard symbols used for logical and arithmetic operations. In this article, we will look into different types of Python operators. OPERATORS: These are the special symbols. Eg- + , * , /, etc.

  22. 30 Multiple-Choice Questions on Python Variables

    Explanation: The "global" keyword is used to declare a variable that can be accessed from anywhere in the program, not just within a specific function. Q13. What is the purpose of the "del" keyword in Python? a) To delete a variable from memory. b) To define a new variable. c) To initialize a variable. d) To declare a variable as global ...

  23. Mass variable declaration and assignment in Python

    Mass variable declaration and assignment in Python. Ask Question Asked 13 years, 1 month ago. ... Its not a ton of code to do it line by line but I'm just learning Python and.... doing something repetitive typically tells me it can be done more efficiently in some other way. ... I've needed this type of behavior ('mass' variable assignment) in ...