Every dunder method in Python

Trey Hunner smiling in a t-shirt against a yellow wall

  • Dunder Methods

You've just made a class. You made a __init__ method. Now what?

Python includes tons of dunder methods ("double underscore" methods) which allow us to deeply customize how our custom classes interact with Python's many features. What dunder methods could you add to your class to make it friendly for other Python programmers who use it?

Let's take a look at every dunder method in Python , with a focus on when each method is useful.

Note that the Python documentation refers to these as special methods and notes the synonym "magic method" but very rarely uses the term "dunder method". However, "dunder method" is a fairly common Python colloquialism, as noted in my unofficial Python glossary .

You can use the links scattered throughout this page for more details on any particular dunder method. For a list of all of them, see the cheat sheet in the final section.

The 3 essential dunder methods 🔑

There are 3 dunder methods that most classes should have: __init__ , __repr__ , and __eq__ .

Operation Dunder Method Call Returns
Typically

The __init__ method is the initializer (not to be confused with the constructor ), the __repr__ method customizes an object's string representation, and the __eq__ method customizes what it means for objects to be equal to one another.

The __repr__ method is particularly helpful at the the Python REPL and when debugging.

Equality and hashability 🟰

In addition to the __eq__ method, Python has 2 other dunder methods for determining the "value" of an object in relation to other objects.

Operation Dunder Method Call Returns
Typically
Typically

Python's __eq__ method typically returns True , False , or NotImplemented (if objects can't be compared). The default __eq__ implementation relies on the is operator, which checks for identity .

The default implementation of __ne__ calls __eq__ and negates any boolean return value given (or returns NotImplemented if __eq__ did). This default behavior is usually "good enough", so you'll almost never see __ne__ implemented .

Hashable objects can be used as keys in dictionaries or values in sets. All objects in Python are hashable by default, but if you've written a custom __eq__ method then your objects won't be hashable without a custom __hash__ method. But the hash value of an object must never change or bad things will happen so typically only immutable objects implement __hash__ .

For implementing equality checks, see __eq__ in Python . For implementing hashability, see making hashable objects in Python .

Orderability ⚖️

Python's comparison operators ( < , > , <= , >= ) can all be overloaded with dunder methods as well. The comparison operators also power functions that rely on the relative ordering of objects, like sorted , min , and max .

Operation Dunder Method Call Returns
Typically
Typically
Typically
Typically

If you plan to implement all of these operators in the typical way (where x < y would be the same as asking y > x ) then the total_ordering decorator from Python's functools module will come in handy.

Type conversions and string formatting ⚗️

Python has a number of dunder methods for converting objects to a different type.

Function Dunder Method Call Returns

The __bool__ function is used for truthiness checks, though __len__ is used as a fallback.

If you needed to make an object that acts like a number (like decimal.Decimal or fractions.Fraction ), you'll want to implement __int__ , __float__ , and __complex__ so your objects can be converted to other numbers. If you wanted to make an object that could be used in a memoryview or could otherwise be converted to bytes , you'll want a __bytes__ method.

The __format__ and __repr__ methods are different string conversion flavors. Most string conversions rely the __str__ method, but the default __str__ implementation simply calls __repr__ .

The __format__ method is used by all f-string conversions , by the str class's format method, and by the (rarely used) built-in format function. This method allows datetime objects to support custom format specifiers .

Context managers 🚪

A context manager is an object that can be used in a with block.

Use Dunder Method Call Returns
block enter A value given to
block exit Truthy/falsey value

For more on context managers see, what is a context manager and creating a context manager .

Containers and collections 🗃️

Collections (a.k.a. containers) are essentially data structures or objects that act like data stuctures. Lists, dictionaries, sets, strings, and tuples are all examples of collections.

Operation Dunder Method Call Return Type Implemented
integer Very common
iterator Very common
iterator Very common
any object Common
None Common
None Common
bool Common
iterator Common
any object Uncommon
any object Uncommon
integer Uncommon

The __iter__ method is used by the iter function and for all forms of iteration: for loops , comprehensions , tuple unpacking , and using * for iterable unpacking .

While the __iter__ method is necessary for creating a custom iterable, the __next__ method is necessary for creating a custom iterator (which is much less common). The __missing__ method is only ever called by the dict class on itself, unless another class decides to implement __missing__ . The __length_hint__ method supplies a length guess for structures which do not support __len__ so that lists or other structures can be pre-sized more efficiently.

Also see: the iterator protocol , implementing __len__ , and implementing __getitem__ .

Callability ☎️

Functions, classes, and all other callable objects rely on the __call__ method.

Operation Dunder Method Call Return Type
any object

When a class is called, its metaclass 's __call__ method is used. When a class instance is called, the class's __call__ method is used.

For more on callability, see Callables: Python's "functions" are sometimes classes .

Arithmetic operators ➗

Python's dunder methods are often described as a tool for "operator overloading". Most of this "operator overloading" comes in the form of Python's various arithmetic operators.

There are two ways to break down the arithmetic operators:

  • Mathematical (e.g. + , - , * , / , % ) versus bitwise (e.g. & , | , ^ , >> , ~ )
  • Binary (between 2 values, like x + y ) versus unary (before 1 value, like +x )

The mathematical operators are much more common than the bitwise ones and the binary ones are a bit more common than the unary ones.

These are the binary mathematical arithmetic operators:

Operation Left-Hand Method Right-Hand Method Description
Add / Concatenate
Subtract
Multiply
Divide
Modulo
Exponentiate
Matrix multiply

Each of these operators includes left-hand and right-hand methods. If x.__add__(y) returns NotImplemented , then y.__radd__(x) will be attempted. See arithmetic dunder methods for more.

These are the binary bitwise arithmetic operators:

Operation Left-Hand Method Right-Hand Method Description
AND
OR
XOR
Right-shift
Left-shift

These are Python's unary arithmetic operators:

Operation Dunder Method Variety Description
Mathematical Negate
Bitwise Affirm
Bitwise Invert

The unary + operator typically has no effect , though some objects use it for a specific operation. For example using + on collections.Counter objects will remove non-positive values.

Python's arithmetic operators are often used for non-arithmetic ends: sequences use + to concatenate and * to self-concatenate and sets use & for intersection, | for union, - for asymmetric difference, and ^ for symmetric difference. Arithmetic operators are sometimes overloaded for more creative uses too. For example, pathlib.Path objects use / to create child paths .

In-place arithmetic operations ♻️

Python includes many dunder methods for in-place operations. If you're making a mutable object that supports any of the arithmetic operations, you'll want to implement the related in-place dunder method(s) as well.

Operation Dunder Method Call Returns
Typically
Typically
Typically
Typically
Typically
Typically
Typically
Typically
Typically
Typically
Typically
Typically
Typically

All of Python's binary arithmetic operators work in augmented assignment statements , which involve using an operator followed by the = sign to assign to an object while performing an operation on it.

Augmented assignments on mutable objects are expected to mutate the original object , thanks to the mutable object implementing the appropriate dunder method for in-place arithmetic.

When no dunder method is found for an in-place operation, Python performs the operation followed by an assignment. Immutable objects typically do not implement dunder methods for in-place operations , since they should return a new object instead of changing the original.

Built-in math functions 🧮

Python also includes dunder methods for many math-related functions, both built-in functions and some functions in the math library.

Operation Dunder Method Call Returns
2-item tuple
2-item tuple
Number
Number
Number
Number

Python's divmod function performs integer division ( // ) and a modulo operation ( % ) at the same time. Note that, just like the many binary arithmetic operators, divmod will also check for an __rvidmod__ method if it needs to ask the second argument to handle the operation.

The __index__ method is for making integer-like objects. This method losslessly converts to an integer, unlike __int__ which may perform a "lossy" integer conversion (e.g. from float to int ). It's used by operations that require true integers, such as slicing , indexing, and bin , hex , and oct functions ( example ).

Attribute access 📜

Python even includes dunder methods for controlling what happens when you access, delete, or assign any attribute on an object!

Operation Dunder Method Call Returns
Attribute value
Attribute value
List of strings

The __getattribute__ method is called for every attribute access, while the __getattr__ method is only called after Python fails to find a given attribute. All method calls and attribute accesses call __getattribute__ so implementing it correctly is challenging (due to accidental recursion ). See __getattr__ versus __getattribute__ for examples demonstrating the difference between these two methods.

The __dir__ method should return an iterable of attribute names (as strings). When the dir function calls __dir__ , it converts the returned iterable into a sorted list (like sorted does).

The built-in getattr , setattr , and delattr functions correspond to the dunder methods of the same name, but they're only intended for dynamic attribute access (not all attribute accesses).

Metaprogramming 🪄

Now we're getting into the really unusual dunder methods. Python includes many dunder methods for metaprogramming-related features.

Implemented on Operation Dunder Method Call Returns
Metaclasses mapping
Metaclasses
Metaclasses
Any class
Any class (Called manually)
Any class
Any class an item

The __prepare__ method customizes the dictionary that's used for a class's initial namespace. This is used to pre-populate dictionary values or customize the dictionary type ( silly example ).

The __instancecheck__ and __subclasscheck__ methods override the functionality of isinstance and issubclass . Python's ABCs use these to practice goose typing ( duck typing while type checking).

The __init_subclass__ method allows classes to hook into subclass initialization ( example ). Classes also have a __subclasses__ method (on their metaclass ) but it's not typically overridden.

Python calls __mro_entries__ during class inheritance for any base classes that are not actually classes. The typing.NamedTuple function uses this to pretend it's a class ( see here ).

The __class_getitem__ method allows a class to be subscriptable ( without its metaclass needing a __getitem__ method). This is typically used for enabling fancy type annotations (e.g. list[int] ).

Descriptors 🏷️

Descriptors are objects that, when attached to a class, can hook into the access of the attribute name they're attached to on that class.

Operation Dunder Method Call Returns
The value

The descriptor protocol is mostly a feature that exists to make Python's property decorator work, though it is also used by a number of third-party libraries.

Implementing a low-level memory array? You need Python's buffer protocol .

Operation Dunder Method Call Returns

The __release_buffer__ method is called when the buffer that's returned from __buffer__ is deleted.

Python's buffer protocol is typically implemented in C, since it's meant for low level objects.

Asynchronous operations 🤹

Want to implement an asynchronous context manager? You need these dunder methods:

  • __aenter__ : just like __enter__ , but it returns an awaitable object
  • __aexit__ : just like __exit__ , but it returns an awaitable object

Need to support asynchronous iteration? You need these dunder methods:

  • __aiter__ : must return an asynchronous iterator
  • __anext__ : like __next__ or non-async iterators, but this must return an awaitable object and this should raise StopAsyncIteration instead of StopIteration

Need to make your own awaitable object? You need this dunder method:

  • __await__ : returns an iterator

I have little experience with custom asynchronous objects, so look elsewhere for more details.

Construction and finalizing 🏭

The last few dunder methods are related to object creation and destruction.

Operation Dunder Method Call Returns
New instance ( )

Calling a class returns a new class instance thanks to the __new__ method. The __new__ method is Python's constructor method , though unlike constructors in many programming languages, you should almost never define your own __new__ method. To control object creation, prefer the initializer ( __init__ ), not the constructor ( __new__ ). Here's an odd __new__ example .

You could think of __del__ as a "destructor" method, though it's actually called the finalizer method . Just before an object is deleted, its __del__ method is called ( example ). Files implement a __del__ method that closes the file and any binary file buffer that it may be linked to.

Library-specific dunder methods 🧰

Some standard library modules define custom dunder methods that aren't used anywhere else:

  • dataclasses support a __post_init__ method
  • abc.ABC classes have a __subclasshook__ method which abc.ABCMeta calls in its __subclasscheck__ method (more in goose typing )
  • Path-like objects have a __fspath__ method, which returns the file path as a string
  • Python's copy module will use the __copy__ and __deepcopy__ methods if present
  • Pickling relies on __getnewargs_ex__ or __getargs__ , though __getstate__ and __setstate__ can customize further and __reduce__ or __reduce_ex__ are even lower-level
  • sys.getsizeof relies on the __sizeof__ method to get an object's size (in bytes)

Dunder attributes 📇

In addition to dunder methods, Python has many non-method dunder attributes .

Here are some of the more common dunder attributes you'll see:

  • __name__ : name of a function, classes, or module
  • __module__ : module name for a function or class
  • __doc__ : docstring for a function, class, or module
  • __class__ : an object's class (call Python's type function instead)
  • __dict__ : most objects store their attributes here (see where are attributes stored? )
  • __slots__ : classes using this are more memory efficient than classes using __dict__
  • __match_args__ : classes can define a tuple noting the significance of positional attributes when the class is used in structural pattern matching ( match - case )
  • __mro__ : a class's method resolution order used when for attribute lookups and super() calls
  • __bases__ : the direct parent classes of a class
  • __file__ : the file that defined the module object (though not always present!)
  • __wrapped__ : functions decorated with functools.wraps use this to point to the original function
  • __version__ : commonly used for noting the version of a package
  • __all__ : modules can use this to customize the behavior of from my_module import *
  • __debug__ : running Python with -O sets this to False and disables Python's assert statements

Those are only the more commonly seen dunder attributes. Here are some more:

  • Functions have __defaults__ , __kwdefaults__ , __code__ , __globals__ , and __closure__
  • Both functions and classes have __qualname__ , __annotations__ , and __type_params__
  • Instance methods have __func__ and __self__
  • Modules may also have __loader__ , __package__ , __spec__ , and __cached__ attributes
  • Packages have a __path__ attribute
  • Exceptions have __traceback__ , __notes__ , __context__ , __cause__ , and __suppress_context__
  • Descriptors use __objclass__
  • Metaclasses use __classcell__
  • Python's weakref module uses __weakref__
  • Generic aliases have __origin__ , __args__ , __parameters__ , and __unpacked__
  • The sys module has __stdout__ and __stderr__ which point to the original stdout and stderr versions

Additionally, these dunder attributes are used by various standard library modules: __covariant__ , __contravariant__ , __infer_variance__ , __bound__ , __constraints__ . And Python includes a built-in __import__ function which you're not supposed to use ( importlib.import_module is preferred) and CPython has a __builtins__ variable that points to the builtins module (but this is an implementation detail and builtins should be explicitly imported when needed instead). Also importing from the __future__ module can enable specific Python feature flags and Python will look for a __main__ module within packages to make them runnable as CLI scripts.

And that's just most of the dunder attribute names you'll find floating around in Python. 😵

Every dunder method: a cheat sheet ⭐

This is every Python dunder method organized in categories and ordered very roughly by the most commonly seen methods first. Some caveats are noted below.

Category Operation Dunder Method Call Returns
Object Creation
Object Creation New instance ( )
Finalizer (ish)
Comparisons Typically
Comparisons Typically
Comparisons Typically
Comparisons Typically
Comparisons Typically
Comparisons Typically
Hashability
Conversions Always
Conversions Always
Conversions Always
Conversions Always
Conversions Always
Conversions Always
Conversions Always
Conversions Always
Context Managers The object
Context Managers Truthy/falsey value
Collections
Collections An iterator
Collections
Collections
Collections
Collections
Collections An iterator
Collections Next iterator item
Collections
Collections
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Arithmetic
Math functions 2-item tuple
Math functions
Math functions
Math functions Number
Math functions Number
Math functions Number
Math functions Number
Assignment Typically
Assignment Typically
Assignment Typically
Assignment Typically
Assignment Typically
Assignment Typically
Assignment Typically
Assignment Typically
Assignment Typically
Assignment Typically
Assignment Typically
Assignment Typically
Assignment Typically
Attributes
Attributes
Attributes
Attributes
Attributes An iterable
Descriptors
Descriptors
Descriptors
Descriptors
Class stuff
Class stuff
Class stuff
Metaclasses /mapping
Metaclasses
Metaclasses
Async (ish) An iterator
Async An awaitable
Async An awaitable
Async An awaitable
Async An awaitable
Buffers
Buffers

The above table has a slight but consistent untruth . Most of these dunder methods are not actually called on an object directly but are instead called on the type of that object: type(x).__add__(x, y) instead of x.__add__(y) . This distinction mostly matters with metaclass methods.

I've also purposely excluded library-specific dunder methods (like __post_init__ ) and dunder methods you're unlikely to ever define (like __subclasses__ ). See those below.

Category Operation Dunder Method Call Returns
Dataclasses
Copying New object
Copying New object
A 2-item tuple
Pickling A 2-item tuple
Pickling A meaningful state
Pickling A 2-6 item tuple
Pickling A 2-6 item tuple
Pickling
pathlib or
sys (size in bytes)
Class stuff None? Subclasses iterable
ABCs

So, Python includes 103 "normal" dunder methods, 12 library-specific dunder methods, and at least 52 other dunder attributes of various types. That's over 150 unique __dunder__ names! I do not recommend memorizing these: let Python do its job and look up the dunder method or attribute that you need to implement/find whenever you need it.

Keep in mind that you're not meant to invent your own dunder methods . Sometimes you'll see third-party libraries that do invent their own dunder method, but this isn't encouraged and it can be quite confusing for users who run across such methods and assume they're " real " dunder methods.

A Python tip every week

Need to fill-in gaps in your Python skills?

Sign up for my Python newsletter where I share one of my favorite Python tips every week .

Need to fill-in gaps in your Python skills ? I send weekly emails designed to do just that.

Sets and dictionaries are powered by hashability. And hashable objects tend to be immutable.

Sign in to record your progress

Sign in to your Python Morsels account to track your progress.

Don't have an account yet? Sign up here .

  • Python Course
  • 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

Assignment Operators in Python

The Python Operators are used to perform operations on values and variables. These are the special symbols that carry out arithmetic, logical, and bitwise computations. The value the operator operates on is known as the Operand. Here, we will cover Different Assignment operators in Python .

Operators

=

Assign the value of the right side of the expression to the left side operandc = a + b 


+=

Add right side operand with left side operand and then assign the result to left operanda += b   

-=

Subtract right side operand from left side operand and then assign the result to left operanda -= b  


*=

Multiply right operand with left operand and then assign the result to the left operanda *= b     


/=

Divide left operand with right operand and then assign the result to the left operanda /= b


%=

Divides the left operand with the right operand and then assign the remainder to the left operanda %= b  


//=

Divide left operand with right operand and then assign the value(floor) to left operanda //= b   


**=

Calculate exponent(raise power) value using operands and then assign the result to left operanda **= b     


&=

Performs Bitwise AND on operands and assign the result to left operanda &= b   


|=

Performs Bitwise OR on operands and assign the value to left operanda |= b    


^=

Performs Bitwise XOR on operands and assign the value to left operanda ^= b    


>>=

Performs Bitwise right shift on operands and assign the result to left operanda >>= b     


<<=

Performs Bitwise left shift on operands and assign the result to left operanda <<= b 


:=

Assign a value to a variable within an expression

a := exp

Here are the Assignment Operators in Python with examples.

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.

Addition Assignment Operator

The Addition Assignment Operator is used to add the right-hand side operand with the left-hand side operand and then assigning the result to the left operand.

Example: In this code we have two variables ‘a’ and ‘b’ and assigned them with some integer value. Then we have used the addition assignment operator which will first perform the addition operation and then assign the result to the variable on the left-hand side.

S ubtraction Assignment Operator

The Subtraction Assignment Operator is used to subtract the right-hand side operand from the left-hand side operand and then assigning the result to the left-hand side operand.

Example: In this code we have two variables ‘a’ and ‘b’ and assigned them with some integer value. Then we have used the subtraction assignment operator which will first perform the subtraction operation and then assign the result to the variable on the left-hand side.

M ultiplication Assignment Operator

The Multiplication Assignment Operator is used to multiply the right-hand side operand with the left-hand side operand and then assigning the result to the left-hand side operand.

Example: In this code we have two variables ‘a’ and ‘b’ and assigned them with some integer value. Then we have used the multiplication assignment operator which will first perform the multiplication operation and then assign the result to the variable on the left-hand side.

D ivision Assignment Operator

The Division Assignment Operator is used to divide the left-hand side operand with the right-hand side operand and then assigning the result to the left operand.

Example: In this code we have two variables ‘a’ and ‘b’ and assigned them with some integer value. Then we have used the division assignment operator which will first perform the division operation and then assign the result to the variable on the left-hand side.

M odulus Assignment Operator

The Modulus Assignment Operator is used to take the modulus, that is, it first divides the operands and then takes the remainder and assigns it to the left operand.

Example: In this code we have two variables ‘a’ and ‘b’ and assigned them with some integer value. Then we have used the modulus assignment operator which will first perform the modulus operation and then assign the result to the variable on the left-hand side.

F loor Division Assignment Operator

The Floor Division Assignment Operator is used to divide the left operand with the right operand and then assigs the result(floor value) to the left operand.

Example: In this code we have two variables ‘a’ and ‘b’ and assigned them with some integer value. Then we have used the floor division assignment operator which will first perform the floor division operation and then assign the result to the variable on the left-hand side.

Exponentiation Assignment Operator

The Exponentiation Assignment Operator is used to calculate the exponent(raise power) value using operands and then assigning the result to the left operand.

Example: In this code we have two variables ‘a’ and ‘b’ and assigned them with some integer value. Then we have used the exponentiation assignment operator which will first perform exponent operation and then assign the result to the variable on the left-hand side.

Bitwise AND Assignment Operator

The Bitwise AND Assignment Operator is used to perform Bitwise AND operation on both operands and then assigning the result to the left operand.

Example: In this code we have two variables ‘a’ and ‘b’ and assigned them with some integer value. Then we have used the bitwise AND assignment operator which will first perform Bitwise AND operation and then assign the result to the variable on the left-hand side.

Bitwise OR Assignment Operator

The Bitwise OR Assignment Operator is used to perform Bitwise OR operation on the operands and then assigning result to the left operand.

Example: In this code we have two variables ‘a’ and ‘b’ and assigned them with some integer value. Then we have used the bitwise OR assignment operator which will first perform bitwise OR operation and then assign the result to the variable on the left-hand side.

Bitwise XOR Assignment Operator 

The Bitwise XOR Assignment Operator is used to perform Bitwise XOR operation on the operands and then assigning result to the left operand.

Example: In this code we have two variables ‘a’ and ‘b’ and assigned them with some integer value. Then we have used the bitwise XOR assignment operator which will first perform bitwise XOR operation and then assign the result to the variable on the left-hand side.

Bitwise Right Shift Assignment Operator

The Bitwise Right Shift Assignment Operator is used to perform Bitwise Right Shift Operation on the operands and then assign result to the left operand.

Example: In this code we have two variables ‘a’ and ‘b’ and assigned them with some integer value. Then we have used the bitwise right shift assignment operator which will first perform bitwise right shift operation and then assign the result to the variable on the left-hand side.

Bitwise Left Shift Assignment Operator

The Bitwise Left Shift Assignment Operator is used to perform Bitwise Left Shift Opertator on the operands and then assign result to the left operand.

Example: In this code we have two variables ‘a’ and ‘b’ and assigned them with some integer value. Then we have used the bitwise left shift assignment operator which will first perform bitwise left shift operation and then assign the result to the variable on the left-hand side.

Walrus Operator

The Walrus Operator in Python is a new assignment operator which is introduced in Python version 3.8 and higher. This operator is used to assign a value to a variable within an expression.

Example: In this code, we have a Python list of integers. We have used Python Walrus assignment operator within the Python while loop . The operator will solve the expression on the right-hand side and assign the value to the left-hand side operand ‘x’ and then execute the remaining code.

Assignment Operators in Python – FAQs

What are assignment operators in python.

Assignment operators in Python are used to assign values to variables. These operators can also perform additional operations during the assignment. The basic assignment operator is = , which simply assigns the value of the right-hand operand to the left-hand operand. Other common assignment operators include += , -= , *= , /= , %= , and more, which perform an operation on the variable and then assign the result back to the variable.

What is the := Operator in Python?

The := operator, introduced in Python 3.8, is known as the “walrus operator”. It is an assignment expression, which means that it assigns values to variables as part of a larger expression. Its main benefit is that it allows you to assign values to variables within expressions, including within conditions of loops and if statements, thereby reducing the need for additional lines of code. Here’s an example: # Example of using the walrus operator in a while loop while (n := int(input("Enter a number (0 to stop): "))) != 0: print(f"You entered: {n}") This loop continues to prompt the user for input and immediately uses that input in both the condition check and the loop body.

What is the Assignment Operator in Structure?

In programming languages that use structures (like C or C++), the assignment operator = is used to copy values from one structure variable to another. Each member of the structure is copied from the source structure to the destination structure. Python, however, does not have a built-in concept of ‘structures’ as in C or C++; instead, similar functionality is achieved through classes or dictionaries.

What is the Assignment Operator in Python Dictionary?

In Python dictionaries, the assignment operator = is used to assign a new key-value pair to the dictionary or update the value of an existing key. Here’s how you might use it: my_dict = {} # Create an empty dictionary my_dict['key1'] = 'value1' # Assign a new key-value pair my_dict['key1'] = 'updated value' # Update the value of an existing key print(my_dict) # Output: {'key1': 'updated value'}

What is += and -= in Python?

The += and -= operators in Python are compound assignment operators. += adds the right-hand operand to the left-hand operand and assigns the result to the left-hand operand. Conversely, -= subtracts the right-hand operand from the left-hand operand and assigns the result to the left-hand operand. Here are examples of both: # Example of using += a = 5 a += 3 # Equivalent to a = a + 3 print(a) # Output: 8 # Example of using -= b = 10 b -= 4 # Equivalent to b = b - 4 print(b) # Output: 6 These operators make code more concise and are commonly used in loops and iterative data processing.

author

Please Login to comment...

Similar reads.

  • Python-Operators
  • How to Delete Discord Servers: Step by Step Guide
  • Google increases YouTube Premium price in India: Check our the latest plans
  • California Lawmakers Pass Bill to Limit AI Replicas
  • Best 10 IPTV Service Providers in Germany
  • 15 Most Important Aptitude Topics For Placements [2024]

Improve your Coding Skills with Practice

 alt=

What kind of Experience do you want to share?

Overloading arithmetic operators with dunder methods | Pydon't 🐍

This article shows you how to overload the arithmetic operators in Python with dunder methods.

python assignment dunder

Introduction

Python lets you override the arithmetic operators like + for addition or * for multiplication through dunder methods . Dunder methods are special methods whose name starts and ends with a double underscore (hence, “dunder”), and some dunder methods are specific to arithmetic operations.

In this Pydon't, you will learn:

  • negation ( -p );
  • absolute value ( abs(p) ); and
  • inverse ( ~ ).
  • addition ( + );
  • subtraction ( - );
  • multiplication ( * );
  • division ( / );
  • floor division ( // );
  • modulo ( % );
  • ( divmod ); and
  • exponentiation ( pow ).
  • left shift ( << );
  • right shift ( >> );
  • bitwise and ( & );
  • bitwise or ( | );
  • bitwise xor ( ^ );
  • what the reflected arithmetic dunder methods are;
  • what the augmented arithmetic dunder methods are;
  • what NotImplemented is and how it differs from NotImplementedError ; and
  • how Python determines which arithmetic dunder method to call.

We will start by explaining how dunder methods work and we will give a couple of examples by implementing the unary operators. Then, we introduce the mechanics behind the binary arithmetic operators, basing the examples on the binary operator + .

After we introduce all the concepts and mechanics that Python uses to handle binary arithmetic operators, we will provide an example of a class that implements all the arithmetic dunder methods that have been mentioned above.

You can now get your free copy of the ebook “Pydon'ts – Write elegant Python code” [on Gumroad][gumroad-pydonts] to help support the series of “Pydon't” articles 💪.

The example we will be using

The example we will be using throughout this article will be that of a Vector . A Vector will be a class for geometrical vectors, like vectors in 2D, or 3D, and it will provide operations to deal with vectors. For example, by the end of this article, you will have an implementation of Vector that lets you do things like these:

Let us go ahead and start!

This is the starting vector for our class Vector :

Running this code will show this output:

This starting vector also shows two dunder methods that we are using right off the bat:

  • we use the dunder method __init__ to initialise our Vector instance; and
  • we use the dunder method __repr__ to provide a string representation of our Vector objects.

This shows that dunder methods are not magical. They look funny because of the leading and trailing underscores in their names, but they are regular Python methods that Python calls automatically.

We will start by covering the unary arithmetic operations because those are simpler. Then, we will move along to the binary arithmetic operations. Good luck! 🐍🚀

Your first arithmetic dunder method

Take a look at this piece of code:

Does the result surprise you? Probably not!

Now, look at this:

Was this surprising? The method __neg__ is the dunder method that is responsible for implementing the unary operation of negation.

Currently, our class Vector does not have support for the operation of negation:

If we implement a method __neg__ in our class Vector , we can add support for this operation. The method __neg__ only accepts the argument self and should return the result of negating self . For illustrative purposes, we can start by implementing a method __neg__ that always returns "Hello, world!" :

Now, we can use the unary operation minus:

Of course, it doesn't make much sense for the negation of a vector to be the string "Hello, world!" .

The unary negation we will implement for real will negate each coordinate, one by one:

This is a more sensible operation, which enables this:

That's it! This was your first arithmetic dunder method! 🎉

Now, we'll implement the remaining unary arithmetic operations.

The dunder methods for the unary arithmetic operations

There are four unary arithmetic operations for a vector v :

  • -v is negation and is implemented via __neg__ ;
  • +v is implemented via __pos__ (I have no idea what it's called!);
  • abs(v) is the absolute coord and is implemented via __abs__ ; and
  • ~v is inversion and is implemented via __invert__ .

These four dunder methods are all unary, which means the only argument they take is self , which is going to be the instance of Vector that they need to operate on.

The dunder method __neg__ for negation

This one was already implemented above!

The dunder method __pos__

When used on integers and floats, __pos__ acts as a no-op:

So, we will do the same thing for vectors. However, because the other unary arithmetic operations return different instances, we will be sure to return a different instance that has the same coordinates:

The dunder method __abs__ for the absolute value

The dunder method __abs__ is called when we use the built-in abs .

For our class Vector , we will return the magnitude of the vector, which is the square root of the sum of the squares of all the coordinates.

The dunder method __invert__ for inversion

The dunder method __invert__ is called when the unary arithmetic operation ~ is used. For integers, this operation is based on binary. (Try to figure out what it does!)

For our class Vector , we can do whatever we want. The operation I'm implementing is inspired by geometry. It looks like this:

What does this do? Given a vector, it will look for two coordinates that are not zero and it will swap them out, while also flipping the sign in one of them. All other coordinates of the result will be 0.

Here are some small examples:

Here are some examples with longer vectors:

This is not a random operation I came up with, it is something from geometry. You can read this Wikipedia article to learn about "orthogonal vectors". This will also make more sense when we implement the dunder method __matmul__ , later.

Unary arithmetic operations summary

If you got to this point, it means you have implemented all unary arithmetic operations. Good job! 🚀

Here is all the code up until this point:

The dunder methods for binary arithmetic operations

Up until now, we dealt with unary operators. This means that the operator expected a single object to work with. As we delve into binary operators, the dunder methods we will implement will take two arguments: self and other .

This will be explained right away, as we start implementing addition.

Addition and the dunder method __add__

To implement addition between our Vector instances we need to implement the dunder method __add__ . When Python finds an expression like a + b , Python will try to run a.__add__(b) , which is why we can use the dunder method __add__ to implement addition for our objects.

Because addition is a binary operator (you add two things), the dunder method __add__ takes two arguments:

  • the other thing that is being added, that we typically call other .

Remember: a + b turns into a.__add__(b) , which is a regular method call! So, b will be the “ other ” thing that we want to add to self and will be passed in as an argument.

For our Vector class, the signature of __add__ looks like this:

Now, instead of ... we just need to provide the actual implementation. Adding Vector instances amounts to adding up all their respective coordinates:

I'm using a list comprehension and the built-in zip to go over the respective coordinates of each Vector instance.

This is all it takes to implement a dunder method.

Adding validation to your dunder methods

Now, the implementation we provided above is pretty barebones. For example, it is going to raise an interesting error if we try to add a vector to an integer:

We get an error because we assumed that other was going to be an instance of a Vector , but we tried to add a vector and an integer, and so our assumption didn't hold. In general, you will want to use isinstance to make sure you can do the operation you really want to do:

When we add this check, the error goes away entirely:

That is also not quite what we wanted. What we would like to see is one of those TypeError s that the language raises when we mix types in the wrong way:

How do we raise this error? You might think of actually raising the error yourself, with raise TypeError(...) , but there is a built-in mechanism that does this.

Using NotImplemented to flag operations you don't support

The built-in constant notimplemented.

When there is a combination of arguments that you do not have support for, you need to return the built-in constant NotImplemented .

The NotImplemented constant is like None in the sense that it is built-in and that there is only one. You don't instantiate None values, you just use the value None . Similarly, you don't instantiate NotImplemented values, you just use NotImplemented .

If you need to return NotImplemented if you do not know how to add the vector with the other argument, you need to modify the method __add__ like so:

You should return the value NotImplemented . Please, do not mistake this for returning the value NotImplementedError or for raising the exception NotImplementedError .

When you return NotImplemented , you are telling Python that a vector cannot be added with whatever type other was, so Python will take care of raising the appropriate TypeError for you:

You can even see this is what happens behind the curtains with some built-in types! For example, 3 + "hello" raises an error, but (3).__add__("hello") returns NotImplemented :

The difference between NotImplemented and NotImplementedError

The two built-ins NotImplemented and NotImplementedError may look similar, but they have very distinct use cases.

The built-in constant NotImplemented is used only in the context of arithmetic dunder methods to tell Python that a specific operation can't be handled by a specific class, whereas the built-in exception NotImplementedError is raised when you have defined the body of a function or a method to specify its signature, but you haven't implemented the behaviour yet.

This is useful, for example, when you use a class as an abstract base class and you specify the signatures of the methods that the subclasses will need, but you don't implement them because it is up to the subclasses to provide that behaviour. Here is a short Shape example:

The Shape specifies that all subclasses of the class Shape must implement the methods area and perimeter :

Python has more complete mechanisms to handle abstract base classes (called interfaces, in other languages), but this small example illustrates the point.

Extending your dunder method to more types

Before taking this little tangent about the difference between NotImplemented and NotImplementedError , we saw that our vectors cannot be added to integers. However, we wish to extend our implementation of Vector to handle integer and float addition. To add an integer or a float to a Vector means that all coordinates of the Vector get shifted by the given amount.

To implement that behaviour, we need to add an extra branch to our if statement inside __add__ :

Now, Vector instances can be added to numbers! It works with integers:

It works with floats:

And it even works backwards:

Huh? What do you mean? We just implemented addition between instances of Vector and int ...

Let us add a couple of print statements for debugging:

Now, if we re-run the file, we see... Nothing! This is the output:

Notice that we get the error without seeing the prints from within __add__ ... And you know why? Well, obviously because __add__ never got called.

Let me explain:

Reflected dunder methods

To be precise, when your arithmetic dunder method returns NotImplemented , it tells Python that that specific method call failed. For example, when Vector.__add__ returns NotImplemented , it tells Python that the class Vector does not know how to add vectors with whatever was in the argument other .

However, when Python sees the return value NotImplemented coming out of an arithmetic dunder method, Python does not raise the TypeError exception immediately! In fact, it will try to run a plan B, first.

When you write a + b , Python will start by trying to run a.__add__(b) . If that fails (that is, if it returns NotImplemented ), Python will then try to run b.__radd__(a) !

Notice that I wrote __radd__ with an extra r , and not just __add__ .

__radd__ is the “reflected dunder dunder __add__ ”, and it is like the plan B for addition.

So, when we wrote 3 + Vector(1, 2) , Python started by trying to run (3).__add__(Vector(1, 2)) , which returns NotImplemented :

Then, Python will try to run Vector(1, 2).__radd__(3) . Because we have not implemented that method, Python raises the exception TypeError .

All other arithmetic dunder methods also have a “reflected” version which has the same name but with the letter r prefixed. Some examples of reflected dunder methods include:

  • the method __rsub__ which is the reflected dunder method for subtraction;
  • the method __rmul__ which is the reflected dunder method for multiplication; or
  • the more exotic method __rpow__ which is the reflected dunder method for exponentiation.

So, all things considered, if we want to be able to write expressions like 3 + Vector(1, 2) , we need to implement the dunder method Vector.__radd__ .

Implementing reflected dunder methods

Commutative operations.

For our example, Vector(1, 2) + 3 is supposed to return the same value as 3 + Vector(1, 2) , so we can implement __radd__ in terms of __add__ :

If you run this code, it outputs the following:

In fact, because addition with instances of Vector is commutative (that is, the result does not depend on the order of the left and right operands), you could even say that __radd__ = __add__ :

This would still work. Give it a try.

Non-commutative operations

Not all arithmetic operations are commutative. In fact, even addition isn't always commutative! Addition of strings – which we call concatenation – isn't commutative because a + b is usually different from b + a :

When the operation isn't commutative, you have to implement the reflected dunder method like any other dunder method. You will see examples of this throughout this article.

NotImplemented in reflected dunder methods

Reverse dunder methods should also return NotImplemented when the operation isn't defined for certain types of other arguments.

For example, if we were to implement __radd__ explicitly for Vector , we would still return NotImplemented at the end of the method. Suppose that we didn't:

Can you guess what's going to happen now? What should be the result of running the code below?

Addition between lists and vectors is not defined, so this should result in a TypeError . However, because Vector.__radd__ does not return NotImplemented , Python actually thinks that this results in None . The output of running that code is:

What is happening here is that the method __radd__ has no explicit return at the end, which means the method returns None when using __radd__ to add a Vector to something else that isn't a Vector , an integer, or a float.

If we want to get the TypeError , we need to return NotImplemented :

Now, when we run this code, Python gives us a great error message:

Reflected dunder methods and subclasses

There is another situation in which reflected dunder methods come in handy, and that is when the right operand is from a subclass of the left operand. Let me explain.

You are writing some code and you implement a class S that just holds a string. Then, you implement addition between instances of the type S :

This works just fine:

Then, you decide to create a subclass of S , called E , which always holds the empty string. Something like this:

Because E is a subclass of S , you can add instances of S and E without a problem:

Everything is fine, right? However, E is always the empty string, which means that when you add an instance of E to another instance of S , the result is always the string saved in the other instance, right?

So, you could optimise addition with instances of the type E by saying that you only need to return the string from the other instance. Something like this:

Because this behaviour is more specialised and because it comes from a subclass of S , Python will give priority to E 's way of adding things together if we try to add an instance of S with an instance of E :

Notice that we didn't see the print from S.__add__ because E.__radd__ has priority over S.__add__ . That priority comes from the fact that E is a subclass of S .

To conclude, in an expression a + b , the call b.__radd__(a) will happen if:

  • the call a.__add__(b) returned NotImplemented ; or
  • the type of b is a subclass of the type of a , in which case b.__radd__(a) is called before a.__add__(b) .

Augmented arithmetic assignment

In Python, we can write things like counter += 1 and multiplier *= 2 . This is called augmented assignment and there are dunder methods used to implement this behaviour.

The dunder methods that are used for augmented assignment start with an “i”, which I am guessing stands for “in-place”. The rationale for these methods is that they should try to do the operation in-place. If it makes sense for your object to be modified in place, then you should implement the augmented arithmetic assignment dunder methods. For example, for addition that is going to be __iadd__ .

Of course, augmented assignment works even without implementing the dunder method __iadd__ . Try running this code:

The output should be the following:

Notice two things:

  • We see the helper print from the dunder method __add__ ; and
  • The ID of the vector changed.

That is because, when running v += ... , Python wants to do v.__iadd__(...) but it can't, because we haven't implemented that method. So, Python unrolls the augmented assignment and tries to evaluate v = v + ... instead, which is why we saw that __add__ was called.

To provide a true augmented assignment implementation, we could write something like this:

If you run this, the output you get is

As you can see, the ID of v doesn't change because we implemented a true in-place dunder method. Another thing to note is that even though we are modifying self , we still need to return the result. (Which is self .)

Full implementation of all arithmetic dunder methods

In this section, we will take all of the knowledge from the previous sections and provide an example class that provides a full implementation of all the arithmetic operators and respective dunder methods:

  • subtraction -
  • multiplication *
  • floor division //
  • exponentiation ** / pow
  • matrix multiplication @
  • bitwise left shift <<
  • bitwise right shift >>
  • bitwise and &
  • bitwise exclusive or ^
  • bitwise or |

Subtraction and the operator -

The binary operator - is the binary operator for subtraction. It is common for addition and subtraction to be closely related and, when they are, you can exploit those relationships.

Example of the dunder methods __sub__ , __rsub__ , and __isub__ for subtraction

For plain subtraction, we can realise that a - b is just a + (-b) , and we already implemented the unary negation operator (with the dunder method __neg__ ), so we can use that shortcut:

This code produces the following output:

Of course, because we implemented subtraction in terms of addition and negation, we get a bunch of prints from those dunder methods.

To implement reflected subtraction in terms of addition and negation, we need to be careful! In Vector.__rsub__ , we will have self and other and we will be trying to compute other - self , so we need to return other + (-self) :

Finally, to implement augmented subtraction, we can do it in terms of augmented addition:

This produces the following output:

Multiplication and the operator *

The binary operator * is the operator for multiplication. Multiplying a vector with another number will produce a second vector whose coordinates have all been multiplied by that single number.

Example of the dunder methods __mul__ , __rmul__ , and __imul__ for multiplication

Multiplication between vectors and numbers is commutative, so we implement __rmul__ in terms of __mul__ :

Augmented multiplication is very similar to regular multiplication, although we return self instead of a new object:

Division and the operator /

Given that addition, subtraction, and multiplication, are called __add__ , __sub__ , and __mul__ , respectively, one might assume that division is called __div__ . However, / is called __truediv__ . That is to disambiguate from // , which is then called __floordiv__ .

For division, we will say that a vector can be divided by a number or vice-versa. In both cases, we will just take the number and map the division out across all coordinates of the vector.

Example of the dunder methods __truediv__ , __rtruediv__ , and __itruediv__ for division

The code above produces the following output:

Floor division and the operator //

Floor division is // and its dunder method is __floordiv__ , not to be confused with __truediv__ for the operation of division / .

Much like with regular division, we will say that a vector can be divided by a number or vice-versa. In both cases, we will just take the number and map the division out across all coordinates of the vector.

Example of the dunder methods __floordiv__ , __rfloordiv__ , and __ifloordiv__ for division

The implementation below was essentially copied and pasted from the implementation of __truediv__ above, except I replaced the operation / with // ...

Modulo and the operator %

The binary operator % is the operator for modulo. To keep in line with other operators, using modulo between a number and a vector will apply the operation element-wise.

Example of the dunder methods __mod__ , __rmod__ , and __imod__ for modulo

Divmod and the built-in function divmod.

The built-in function divmod puts together the operators / (division) and % (modulo). The function call divmod(x, y) should be equivalent to (x / y, x % y) , so that is the behaviour we implement.

Example of the dunder methods __divmod__ and __rdivmod__ for divmod

Notice that there is no in-place/augmented operator __idivmod__ for us to implement because we cannot write the augmented operator divmod= . That does not make any sense in Python.

Exponentation and the operator ** and built-in function pow

The operation of exponentiation can be expressed both via the binary operator ** and the built-in function pow . The operator ** takes the left operand and raises it to the power of the right operand. The built-in function pow does a similar thing, except that pow takes an optional third argument that is the modulo under which the exponentiation is computed.

For the implementation, we will allow either the left or right arguments of ** to be vectors, but the other one must be a number. Furthermore, the only value that is acceptable as the optional third argument is a number.

Example of the dunder methods __pow__ , __rpow__ , and __ipow__ for modulo

Matrix multiplication and the operator @.

The binary operator @ is the operator for matrix multiplication. At the time of writing, @ isn't used for any operations in vanilla Python but it is used in places like NumPy, where matrix multiplication is a common operation.

In our example, @ between two vectors will implement dot product, an operation that only works if the two vectors have the same length. First, we multiply the corresponding coordinates of the two vectors together and then we sum those values.

Notice that @ between two vectors will produce a single number. Thus, v1 @= v2 is an operation that may look like it does not make sense because v1 will cease to be a vector and it will become a number. However, this behaviour is in line with vanilla Python:

Example of the dunder methods __matmul__ , __rmatmul__ , and __imatmul__ for modulo

For matrix multiplication, the only thing we support is matrix multiplication between two vectors. Because of that, we do not need to implement __rmatmul__ . So, we can either leave __rmatmul__ out, or we define it but the only statement we include is return NotImplemented .

On a different note, because matrix multiplication between two vectors returns a number, there is also no point in defining __imatmul__ because the statement v1 @= v2 will not modify v1 in place. Instead, it will replace v1 with the number result of the expression v1 @ v2 . So, we could implement __imatmul__ to be equal to __matmul__ , but there is no point.

Thus, for matrix multiplication, we can boil our code down to:

Bitwise left shift and the operator <<

The binary operator << is the bitewise left shift operator. (It is called bitewise because it operates on bits of integers.)

For vectors, we will implement the left shift operator with an integer argument and a vector argument, and it will rotate the coordinates of the vector the amount of times specified by the integer. We will do the same thing, regardless of whether the integer shows up on the left or on the right.

Here are some examples:

Example of the dunder methods __lshift__ , __rlshift__ , and __ilshift__ for bitwise left shift

Bitwise right shift and the operator >>.

The binary operator >> is the bitewise right shift operator.

Our implementation for the right shift operator will match the implementation for the left shift operator, seen above, but it will work in the other direction.

Example of the dunder methods __rshift__ , __rrshift__ , and __irshift__ for bitwise right shift

The implementation of the bitwise right shift is very similar to the implementation of the bitwise left shift and we use negative indices and slicing to get the shifting behaviour.

You can read this article about slicing to learn about the idiomatic slicing patterns being used below.

Bitwise and and the operator &

The binary operator & is the bitewise "and" operator, not to be confused with the keyword and that operates on Boolean values. We will use the bitwise and operator to implement concatenation of vectors, like so:

Example of the dunder methods __and__ , __rand__ , and __iand__ for bitwise and

Because we only defined the operator & between instances of vectors, there is nothing we need to do inside __rand__ . So, we provide an empty implementation that just returns NotImplemented so that we can show we didn't forget __rand__ , but at the same time to say that it won't do anything for an expression x & v , where x is of a type other than Vector .

Bitwise exclusive or (xor) and the operator ^

The binary operator ^ is the bitewise exclusive "or" operator. We will use the bitwise exclusive or operator to implement an operation between numbers and vectors. Given a number and a vector, we will create a vector of zeroes and ones:

  • coordinates that have the same sign as the number operator get a 1; and
  • coordinates that have a different sign get a 0.

(This operation has no particular meaning that I am aware of, it is just an example operation that we will implement here.)

Example of the dunder methods __xor__ , __rxor__ , and __ixor__ for bitwise exclusive or (xor)

Bitwise or and the operator |.

The binary operator | is the bitewise "or" operator. We will use the bitwise or operator to determine whether the left vector operand is a multiple of the right vector operand. In other words, v1 | v2 will check if there is a number x such that v1 == x * v2 . If there isn't, we will return None .

(This operation also has no particular meaning that I am aware of, it is just an example operation that we will implement here.)

Example of the dunder methods __or__ , __ror__ , and __ior__ for bitwise or

The bitwise or only operates between vectors, so there is no behaviour that we can implement inside __ror__ . For that reason, we just return NotImplemented . Leaving out __ror__ would have the same effect.

Because the operator | produces numbers when applied to two vectors, it also doesn't make sense to implement __ior__ , although we could implement __ior__ to be exactly the same as __or__ .

Full implementation of the class Vector

If you want to see the full implementation of the class Vector , go ahead and take a look below.

  • Modify the implementations of + and - so that a ValueError is raised when the two vectors being added/subtracted do not have the same length.
  • Modify the implementation of the operator * so that we can also multiply vectors together. In order to be able to multiply two vectors together, the two vectors need to have the same length and then you'll multiply corresponding coordinates together.
  • Modify the implementation of the operators / and // so that we can also divide vectors together. In order to be able to divide two vectors together, the two vectors need to have the same length and then you'll divide corresponding coordinates.
  • Write down a paragraph explaining why it is that we don't need to implement methods like __ror__ and __rmatmul__ .
  • Experiment with implementing __imatmul__ and __ior__ and see if the fact that those two methods return numbers instead of vectors breaks expressions like v1 |= v2 and v1 @= v2 .

Here's the main takeaway of this Pydon't, for you, on a silver platter:

“ The behaviour of arithmetic operators in Python is implemented via their respective dunder methods (and the reversed and in-place variants) and the singleton value NotImplemented . ”

This Pydon't showed you that:

  • the arithmetic operators are implemented through special methods called dunder methods;
  • the arithmetic dunder methods are called automatically by Python behind the scenes;
  • custom objects can interact with the standard arithmetic operators via those same dunder methods;
  • binary arithmetic operators correspond to dunder methods with two parameters;
  • the singleton value NotImplemented is used behind the scenes to flag operator/argument(s) combinations that cannot be handled;
  • you need to use NotImplemented so that Python knows what methods to call;
  • the singleton value NotImplemented is distinct from the exception NotImplementedError ;
  • binary arithmetic operators have a reflected variant, with an r prepended to the name;
  • the original call wasn't handled by the left operand of the operator (that is, it returned NotImplemented ); or
  • when the right operand is from a subclass of the left operand.
  • binary arithmetic operators have an in-place variant, with an i prepended to the name;
  • the in-place variants are called by the augmented assignment operators, like += and -= ; and
  • if the in-place variants are not available, Python unfolds the augmented assignment naturally.

Additionally, we also provided a custom class that implements virtually every single arithmetic dunder method (reversed and in-place variants included) and we provided a couple of exercises for you to practise.

Become a better Python 🐍 developer 🚀

+35 chapters. +400 pages. Hundreds of examples. Over 30,000 readers!

My book “Pydon'ts” teaches you how to write elegant, expressive, and Pythonic code, to help you become a better developer. >>> Download it here 🐍🚀 .

  • Python 3 Docs, Data Model, https://docs.python.org/3/reference/datamodel.html [last accessed 27-03-2023];

Previous Post

Random Article

Stay in the loop, popular tags.

  • 3 August 2024
  • 8 July 2024
  • 1 June 2024
  • 6 April 2024
  • 5 March 2024
  • 2 February 2024
  • 8 January 2024
  • 1 December 2023
  • 22 November 2023
  • 4 October 2023
  • 6 September 2023

At codegolf.stackexchange.com , smaller is better.

mathspp

Python In-Place Assignment Operators

In-place assignment operators (also called compound assignment operators) perform an operation in-place on a variable provided as first operand. They overwrite the value of the first operand variable with the result of the operation when performing the operator without assignment. For example, x += 3 is the same as x = x + 3 of first calculating the result of x + 3 and then assigning it to the variable x.

Operator Short ExampleEquivalent Long Example
<<=

You can watch me go over all of these operators in the following video:

We’ll rush over all in-place operators one-by-one next!

Python In-Place Addition

Python provides the operator x += y to add two objects in-place by calculating the sum x + y and assigning the result to the first operands variable name x . You can set up the in-place addition behavior for your own class by overriding the magic “dunder” method __iadd__(self, other) in your class definition.

The expression x += y is syntactical sugar for the longer-form x = x + y :

Related Tutorial: Python In-Place Addition

Python In-Place Subtraction

Python provides the operator x -= y to subtract two objects in-place by calculating the difference x - y and assigning the result to the first operands variable name x . You can set up the in-place subtraction behavior for your own class by overriding the magic “dunder” method __isub__(self, other) in your class definition.

The expression x -= y is syntactical sugar for the longer-form x = x - y :

Related Tutorial: Python In-Place Subtraction

Python In-Place Multiplication

Python provides the operator x *= y to multiply two objects in-place by calculating the product x * y and assigning the result to the first operands variable name x . You can set up the in-place multiplication behavior for your own class by overriding the magic “dunder” method __imul__(self, other) in your class definition.

The expression x *= y is syntactical sugar for the longer-form x = x * y :

Related Tutorial: Python In-Place Multiplication

Python In-Place Division

Python’s in-place division operator x /= y divides two objects in-place by calculating x / y and assigning the result to the first operands variable name x . Set up in-place division for your own class by overriding the magic “dunder” method __truediv__(self, other) in your class definition.

The expression x /= y is syntactical sugar for the longer-form x = x / y :

Related Tutorial: Python In-Place Division

Python In-Place Modulo

Python provides the operator x %= y to calculate the modulo operation x % y , and assign the result in-place to the first operands variable x . You can set up the in-place modulo behavior for your own class by overriding the magic “dunder” method __imod__(self, other) in your class definition.

The expression x %= y is syntactical sugar for the longer-form x = x % y :

Related Tutorial: Python In-Place Modulo

Python In-Place Integer Division

Python’s in-place integer division operator x //= y divides two objects in-place by calculating x // y and assigning the result to the first operands variable name x . Set up in-place integer (or floor) division for your own class by overriding the magic “dunder” method __floordiv__(self, other) in your class definition.

Related Tutorial: Python In-Place Integer Division

Python In-Place Exponentiation

Python provides the in-place exponentiation operator x **= y that raises x to the power of y using x ** y and assigns the result to the first operands variable name x . You can set up the in-place exponentiation behavior for your own class by overriding the magic “dunder” method __ipow__(self, other) in your class definition.

The expression x **= y is syntactical sugar for the longer-form x = x ** y :

Related Tutorial: Python In-Place Exponentiation

Python In-Place Bitwise AND

Python’s in-place bitwise AND operator x &= y calcualtes bitwise-and x & y and assigns the result to the first operand x . To set it up for your own class, override the magic “dunder” method __iand__(self, other) in your class definition.

The expression x &= y is syntactical sugar for the longer-form x = x & y :

Related Tutorial: Python In-Place Bitwise AND

Python In-Place Bitwise OR

Python’s A |= B applies the | operator in place. Thus, it is semantically identical to the longer-form version A = A | B of first performing the operation A | B and then assigning the result to the variable A .

The following minimal example creates two Boolean variables A and B and performs the in-place B |= A operation to perform a logical OR operation B | A and assigning the result to the first operand B that becomes True :

In this example, you’ve seen this in-place operation on Boolean operands. But the | operator is overloaded in Python. The three most frequent use cases for the | and |= operators are the following:

  • Python Sets : set union operator
  • Python Dictionaries : dictionary update operator
  • Python Booleans : logical OR operator

Related Tutorial: Python In-Place Bitwise OR

Python In-Place Bitwise XOR

Python’s in-place bitwise XOR operator x ^= y calcualtes bitwise XOR x ^ y and assigns the result to the first operand x . To set this up for your own class, override the magic “dunder” method __ixor__(self, other) in your class definition.

The expression x ^ = y is syntactical sugar for the longer-form x = x ^ y :

Related Tutorial: Python In-Place Bitwise XOR

Python In-Place Bitwise Right-Shift

Python’s in-place bitwise right-shift operator x >>= y calculates the right-shift operation x >> y , and assigns the result to the first operands variable name x . You can set up the in-place right-shift behavior in your own class by overriding the magic “dunder” method __irshift__(self, other) in your class definition.

The expression x >>= y is syntactical sugar for the longer-form x = x >> y :

Related Tutorial: Python In-Place Bitwise Right-Shift

Python In-Place Bitwise Left-Shift

Python’s in-place bitwise left-shift operator x <<= y calculates the left-shift operation x << y , and assigns the result to the first operands variable name x . You can set up the in-place left-shift behavior in your own class by overriding the magic “dunder” method __ilshift__(self, other) in your class definition.

The expression x <<= y is syntactical sugar for the longer-form x = x << y :

Related Tutorial: Python In-Place Bitwise Left-Shift

Python In-Place Magic Methods

The following table provides the names of the magic methods you need to define to enable in-place operators on your custom class:

Method NameDescription

In the following code example, we create a custom class Data and define our “magic” double-underscore methods so that we can perform in-place computations on objects of this class.

Let’s try these out!

python assignment dunder

17 Pythonic OOP: Properties and Dunder Methods

python assignment dunder

Many languages have OOP features, but Python has some unique OOP features, including properties and dunder methods. Learning how to use these Pythonic techniques can help you write concise and readable code.

Properties allow you to run some specific code each time an object’s attribute is read, modified, or deleted to ensure the object isn’t put into an invalid state. In other languages, this code is often called getters or setters . Dunder methods allow you to use your objects with Python’s built-in operators, such as the + operator. This is how you can combine two datetime.timedelta objects, such as datetime.timedelta(days=2) and datetime.timedelta(days=3) , to create a new datetime.timedelta(days=5) object.

In addition to using other examples, we’ll continue to expand the WizCoin class we started in Chapter 15 by adding properties and overloading operators with dunder methods. These features will make WizCoin objects more expressive and easier to use in any application that imports the wizcoin module.

The BankAccount class that we used in Chapter 15 marked its _balance attribute as private by placing an underscore at the start of its name. But remember that designating an attribute as private is only a convention: all attributes in Python are technically public, meaning they’re accessible to code outside the class. There’s nothing to prevent code from intentionally or maliciously changing the _balance attribute to an invalid value.

But you can prevent accidental invalid changes to these private attributes with properties. In Python, properties are attributes that have specially assigned getter , setter , and deleter methods that can regulate how the attribute is read, changed, and deleted. For example, if the attribute is only supposed to have integer values, setting it to the string '42' will likely cause bugs. A property would call the setter method to run code that fixes, or at least provides early detection of, setting an invalid value. If you’ve thought, “I wish I could run some code each time this attribute was accessed, modified with an assignment statement, or deleted with a del statement,” then you want to use properties.

Turning an Attribute into a Property

First, let’s create a simple class that has a regular attribute instead of a property. Open a new file editor window and enter the following code, saving it as regularAttributeExample.py :

This ClassWithRegularAttributes class has a regular attribute named someAttribute . The __init__() method sets someAttribute to 'some initial value' , but we then directly change the attribute’s value to 'changed value' . When you run this program, the output looks like this:

This output indicates that code can easily change someAttribute to any value. The downside of using regular attributes is that your code can set the someAttribute attribute to invalid values. This flexibility is simple and convenient, but it also means someAttribute could be set to some invalid value that causes bugs.

Let’s rewrite this class using properties by following these steps to do this for an attribute named someAttribute :

  • Rename the attribute with an underscore prefix: _someAttribute .
  • Create a method named someAttribute with the @property decorator. This getter method has the self parameter that all methods have.
  • Create another method named someAttribute with the @someAttribute.setter decorator. This setter method has parameters named self and value .
  • Create another method named someAttribute with the @someAttribute.deleter decorator. This deleter method has the self parameter that all methods have.

Open a new file editor window and enter the following code, saving it as propertiesExample.py :

This program’s output is the same as the regularAttributeExample.py code, because they effectively do the same task: they print an object’s initial attribute and then update that attribute and print it again.

But notice that the code outside the class never directly accesses the _someAttribute attribute (it’s private, after all). Instead, the outside code accesses the someAttribute property. What this property actually consists of is a bit abstract: the getter, setter, and deleter methods combined make up the property. When we rename an attribute named someAttribute to _someAttribute while creating getter, setter, and deleter methods for it, we call this the someAttribute property.

In this context, the _someAttribute attribute is called a backing field or backing variable and is the attribute on which the property is based. Most, but not all, properties use a backing variable. We’ll create a property without a backing variable in “Read-Only Properties” later in this chapter.

You never call the getter, setter, and deleter methods in your code because Python does it for you under the following circumstances:

  • When Python runs code that accesses a property, such as print(obj.someAttribute) , behind the scenes, it calls the getter method and uses the returned value.
  • When Python runs an assignment statement with a property, such as obj.someAttribute = 'changed value' , behind the scenes, it calls the setter method, passing the 'changed value' string for the value parameter.
  • When Python runs a del statement with a property, such as del obj.someAttribute , behind the scenes, it calls the deleter method.

The code in the property’s getter, setter, and deleter methods acts on the backing variable directly. You don’t want the getter, setter, or deleter methods to act on the property, because this could cause errors. In one possible example, the getter method would access the property, causing the getter method to call itself, which makes it access the property again, causing it to call itself again, and so on until the program crashes. Open a new file editor window and enter the following code, saving it as badPropertyExample.py :

When you run this code, the getter continually calls itself until Python raises a RecursionError exception:

To prevent this recursion, the code inside your getter, setter, and deleter methods should always act on the backing variable (which should have an underscore prefix in its name), never the property. Code outside these methods should use the property, although as with the private access underscore prefix convention, nothing prevents you from writing code on the backing variable anyway.

Using Setters to Validate Data

The most common need for using properties is to validate data or to make sure it’s in the format you want it to be in. You might not want code outside the class to be able to set an attribute to just any value; this could lead to bugs. You can use properties to add checks that ensure only valid values are assigned to an attribute. These checks let you catch bugs earlier in code development, because they raise an exception as soon as an invalid value is set.

Let’s update the wizcoin.py file from Chapter 15 to turn the galleons , sickles , and knuts attributes into properties. We’ll change the setter for these properties so only positive integers are valid. Our WizCoin objects represent an amount of coins, and you can’t have half a coin or an amount of coins less than zero. If code outside the class tries to set the galleons , sickles , or knuts properties to an invalid value, we’ll raise a WizCoinException exception.

Open the wizcoin.py file that you saved in Chapter 15 and modify it to look like the following:

The new changes add a WizCoinException class 1 that inherits from Python’s built-in Exception class. The class’s docstring describes how the wizcoin module 2 uses it. This is a best practice for Python modules: the WizCoin class’s objects can raise this when they’re misused. That way, if a WizCoin object raises other exception classes, like ValueError or TypeError , this will mostly likely signify that it’s a bug in the WizCoin class.

In the __init__() method, we set the self.galleons , self.sickles , and self.knuts properties 3 to the corresponding parameters.

At the bottom of the file, after the total() and weight() methods, we add a getter 4 and setter method 5 for the self._galleons attribute. The getter simply returns the value in self._galleons . The setter checks whether the value being assigned to the galleons property is an integer 6 and positive 8 . If either check fails, WizCoinException is raised with an error message. This check prevents _galleons from ever being set with an invalid value as long as code always uses the galleons property.

All Python objects automatically have a __class__ attribute, which refers to the object’s class object. In other words, value.__class__ is the same class object that type(value) returns. This class object has an attribute named __qualname__ that is a string of the class’s name. (Specifically, it’s the qualified name of the class, which includes the names of any classes the class object is nested in. Nested classes are of limited use and beyond the scope of this book.) For example, if value stored the date object returned by datetime.date(2021, 1, 1) , then value.__class__.__qualname__ would be the string 'date' . The exception messages use value.__class__.__qualname__ 7 to get a string of the value object’s name. The class name makes the error message more useful to the programmer reading it, because it identifies not only that the value argument was not the right type, but what type it was and what type it should be.

You’ll need to copy the code for the getter and setter for _galleons to use for the _sickles and _knuts attributes as well. Their code is identical except they use the _sickles and _knuts attributes, instead of _galleons , as backing variables.

Read-Only Properties

Your objects might need some read-only properties that can’t be set with the assignment operator = . You can make a property read-only by omitting the setter and deleter methods.

For example, the total() method in the WizCoin class returns the value of the object in knuts. We could change this from a regular method to a read-only property, because there is no reasonable way to set the total of a WizCoin object. After all, if you set total to the integer 1000 , does this mean 1,000 knuts? Or does it mean 1 galleon and 493 knuts? Or does it mean some other combination? For this reason, we’ll make total a read-only property by adding the code in bold to the wizcoin.py file:

After you add the @property function decorator in front of total() , Python will call the total() method whenever total is accessed. Because there is no setter or deleter method, Python raises AttributeError if any code attempts to modify or delete total by using it in an assignment or del statement, respectively. Notice that the value of the total property depends on the value in the galleons , sickles , and knuts properties: this property isn’t based on a backing variable named _total . Enter the following into the interactive shell:

You might not like that your program immediately crashes when you attempt to change a read-only property, but this behavior is preferable to allowing a change to a read-only property. Your program being able to modify a read-only property would certainly cause a bug at some point while the program runs. If this bug happens much later after you modify the read-only property, it would be hard to track down the original cause. Crashing immediately allows you to notice the problem sooner.

Don’t confuse read-only properties with constant variables. Constant variables are written in all uppercase and rely on the programmer to not modify them. Their value is supposed to remain constant and unchanging for the duration of a program’s run. A read-only property is, as with any attribute, associated with an object. A read-only property cannot be directly set or deleted. But it might evaluate to a changing value. Our WizCoin class’s total property changes as its galleons , sickles , and knuts properties change.

When to Use Properties

As you saw in the previous sections, properties provide more control over how we can use a class’s attributes, and they’re a Pythonic way to write code. Methods with names like getSomeAttribute() or setSomeAttribute() signal that you should probably use properties instead.

This isn’t to say that every instance of a method beginning with get or set should immediately be replaced with a property. There are situations in which you should use a method, even if its name begins with get or set . Here are some examples:

  • For slow operations that take more than a second or two—for example, downloading or uploading a file
  • For operations that have side effects, such as changes to other attributes or objects
  • For operations that require additional arguments to be passed to the get or set operation—for example, in a method call like emailObj.getFileAttachment(filename)

Programmers often think of methods as verbs (in the sense that methods perform some action), and they think of attributes and properties as nouns (in the sense that they represent some item or object). If your code seems to be performing more of an action of getting or setting rather than getting or setting an item, it might be best to use a getter or setter method. Ultimately, this decision depends on what sounds right to you as the programmer.

The great advantage of using Python’s properties is that you don’t have to use them when you first create your class. You can use regular attributes, and if you need properties later, you can convert the attributes to properties without breaking any code outside the class. When we make a property with the attribute’s name, we can rename the attribute using a prefix underscore and our program will still work as it did before.

Python’s Dunder Methods

Python has several special method names that begin and end with double underscores, abbreviated as dunder . These methods are called dunder methods , special methods , or magic methods . You’re already familiar with the __init__() dunder method name, but Python has several more. We often use them for operator overloading —that is, adding custom behaviors that allow us to use objects of our classes with Python operators, such as + or >= . Other dunder methods let objects of our classes work with Python’s built-in functions, such as len() or repr() .

As with __init__() or the getter, setter, and deleter methods for properties, you almost never call dunder methods directly. Python calls them behind the scenes when you use the objects with operators or built-in functions. For example, if you create a method named __len__() or __repr__() for your class, they’ll be called behind the scenes when an object of that class is passed to the len() or repr() function, respectively. These methods are documented online in the official Python documentation at https://docs.python.org/3/reference/datamodel.html .

As we explore the many different types of dunder methods, we’ll expand our WizCoin class to take advantage of them.

String Representation Dunder Methods

You can use the __repr_() and __str__() dunder methods to create string representations of objects that Python typically doesn’t know how to handle. Usually, Python creates string representations of objects in two ways. The repr (pronounced “repper”) string is a string of Python code that, when run, creates a copy of the object. The str (pronounced “stir”) string is a human-readable string that provides clear, useful information about the object. The repr and str strings are returned by the repr() and str() built-in functions, respectively. For example, enter the following into the interactive shell to see a datetime.date object’s repr and str strings:

In this example, the 'datetime.date(2021, 1, 1)' repr string of the datetime.date object 2 is literally a string of Python code that creates a copy of that object 1 . This copy provides a precise representation of the object. On the other hand, the '2021-01-01' str string of the datetime.date object 3 is a string representing the object’s value in a way that’s easy for humans to read. If we simply enter the object into the interactive shell 4 , it displays the repr string. An object’s str string is often displayed to users, whereas an object’s repr string is used in technical contexts, such as error messages and logfiles.

Python knows how to display objects of its built-in types, such as integers and strings. But it can’t know how to display objects of the classes we create. If repr() doesn’t know how to create a repr or str string for an object, by convention the string will be enclosed in angle brackets and contain the object’s memory address and class name: '<wizcoin.WizCoin object at 0x00000212B4148EE0>' . To create this kind of string for a WizCoin object, enter the following into the interactive shell:

These strings aren’t very readable or useful, so we can tell Python what strings to use by implementing the __repr__() and __str__() dunder methods. The __repr__() method specifies what string Python should return when the object is passed to the repr() built-in function, and the __str__() method specifies what string Python should return when the object is passed to the str() built-in function. Add the following to the end of the wizcoin.py file:

When we pass purse to repr() and str() , Python calls the __repr__() and __str__() dunder methods. We don’t call the dunder methods in our code.

Note that f-strings that include the object in braces will implicitly call str() to get an object’s str string. For example, enter the following into the interactive shell:

When we pass the WizCoin object in purse to the repr() and str() functions, behind the scenes Python calls the WizCoin class’s __repr__() and __str__() methods. We programmed these methods to return more readable and useful strings. If you entered the text of the 'WizCoin(2, 5, 10)' repr string into the interactive shell, it would create a WizCoin object that has the same attributes as the object in purse . The str string is a more human-readable representation of the object’s value: '2g, 5s, 10k' . If you use a WizCoin object in an f-string, Python uses the object’s str string.

If WizCoin objects were so complex that it would be impossible to create a copy of them with a single constructor function call, we would enclose the repr string in angle brackets to denote that it’s not meant to be Python code. This is what the generic representation strings, such as '<wizcoin.WizCoin object at 0x00000212B4148EE0>' , do. Typing this string into the interactive shell would raise a SyntaxError , so it couldn’t possibly be confused for Python code that creates a copy of the object.

Inside the __repr__() method, we use self.__class__.__qualname__ instead of hardcoding the string 'WizCoin' ; so if we subclass WizCoin , the inherited __repr__() method will use the subclass’s name instead of 'WizCoin' . In addition, if we rename the WizCoin class, the __repr__() method will automatically use the updated name.

But the WizCoin object’s str string shows us the attribute values in a neat, concise form. I highly recommended you implement __repr__() and __str__() in all your classes.

Numeric Dunder Methods

The numeric dunder methods , also called the math dunder methods , overload Python’s mathematical operators, such as + , - , * , / , and so on. Currently, we can’t perform an operation like adding two WizCoin objects together with the + operator. If we try to do so, Python will raise a TypeError exception, because it doesn’t know how to add WizCoin objects. To see this error, enter the following into the interactive shell:

Instead of writing an addWizCoin() method for the WizCoin class, you can use the __add__() dunder method so WizCoin objects work with the + operator. Add the following to the end of the wizcoin.py file:

When a WizCoin object is on the left side of the + operator, Python calls the __add__() method 1 and passes in the value on the right side of the + operator for the other parameter. (The parameter can be named anything, but other is the convention.)

Keep in mind that you can pass any type of object to the __add__() method, so the method must include type checks 2 . For example, it doesn’t make sense to add an integer or a float to a WizCoin object, because we don’t know whether it should be added to the galleons , sickles , or knuts amount.

The __add__() method creates a new WizCoin object with amounts equal to the sum of the galleons , sickles , and knuts attributes of self and other 3 . Because these three attributes contain integers, we can use the + operator on them. Now that we’ve overloaded the + operator for the WizCoin class, we can use the + operator on WizCoin objects.

Overloading the + operator like this allows us to write more readable code. For example, enter the following into the interactive shell:

If the wrong type of object is passed for other , the dunder method shouldn’t raise an exception but rather return the built-in value NotImplemented . For example, in the following code, other is an integer:

Returning NotImplemented signals Python to try calling other methods to perform this operation. (See “Reflected Numeric Dunder Methods” later in this chapter for more details.) Behind the scenes, Python calls the __add__() method with 42 for the other parameter, which also returns NotImplemented , causing Python to raise a TypeError .

Although we shouldn’t be able to add integers to or subtract them from WizCoin objects, it would make sense to allow code to multiply WizCoin objects by positive integer amounts by defining a __mul__() dunder method. Add the following to the end of wizcoin.py :

This __mul__() method lets you multiply WizCoin objects by positive integers. If other is an integer, it’s the data type the __mul__() method is expecting and we shouldn’t return NotImplemented . But if this integer is negative, multiplying the WizCoin object by it would result in negative amounts of coins in our WizCoin object. Because this goes against our design for this class, we raise a WizCoinException with a descriptive error message.

Enter the following into the interactive shell to see the __mul__() dunder method in action:

Table 17-1 shows the full list of numeric dunder methods. You don’t always need to implement all of them for your class. It’s up to you to decide which methods are relevant.

Table 17-1: Numeric Dunder Methods

Addition
Subtraction
Multiplication
Matrix multiplication (new in Python 3.5)
Division
Integer division
Modulus
Division and modulus
__pow__()Exponentiation ,
Left shift
Right shift
Bitwise and
Bitwise or
Bitwise exclusive or
NegationUnary , as in
IdentityUnary , as in
Absolute value
Bitwise inversion
Complex number form
Integer number form
Floating-point number form
Boolean form
Rounding
Truncation
Rounding down
Rounding up

Some of these methods are relevant to our WizCoin class. Try writing your own implementation of the __sub__() , __pow__() , __int__() , __float__() , and __bool__() methods. You can see an example of an implementation at https://autbor.com/wizcoinfull . The full documentation for the numeric dunder methods is in the Python documentation at https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types .

The numeric dunder methods allow objects of your classes to use Python’s built-in math operators. If you’re writing methods with names like multiplyBy() , convertToInt() , or something similar that describes a task typically done by an existing operator or built-in function, use the numeric dunder methods (as well as the reflected and in-place dunder methods described in the next two sections).

Reflected Numeric Dunder Methods

Python calls the numeric dunder methods when the object is on the left side of a math operator. But it calls the reflected numeric dunder methods (also called the reverse or right-hand dunder methods) when the object is on the right side of a math operator.

Reflected numeric dunder methods are useful because programmers using your class won’t always write the object on the left side of the operator, which could lead to unexpected behavior. For example, let’s consider what happens when purse contains a WizCoin object, and Python evaluates the expression 2 * purse , where purse is on the right side of the operator:

  • Because 2 is an integer, the int class’s __mul__() method is called with purse passed for the other parameter.
  • The int class’s __mul__() method doesn’t know how to handle WizCoin objects, so it returns NotImplemented .
  • Python doesn’t raise a TypeError just yet. Because purse contains a WizCoin object, the WizCoin class’s __rmul__() method is called with 2 passed for the other parameter.
  • If __rmul__() returns NotImplemented , Python raises a TypeError .

Otherwise, the returned object from __rmul__() is what the 2 * purse expression evaluates to.

But the expression purse * 2 , where purse is on the left side of the operator, works differently:

  • Because purse contains a WizCoin object, the WizCoin class’s __mul__() method is called with 2 passed for the other parameter.
  • The __mul__() method creates a new WizCoin object and returns it.
  • This returned object is what the purse * 2 expression evaluates to.

Numeric dunder methods and reflected numeric dunder methods have identical code if they are commutative . Commutative operations, like addition, have the same result backward and forward: 3 + 2 is the same as 2 + 3. But other operations aren’t commutative: 3 – 2 is not the same as 2 – 3. Any commutative operation can just call the original numeric dunder method whenever the reflected numeric dunder method is called. For example, add the following to the end of the wizcoin.py file to define a reflected numeric dunder method for the multiplication operation:

Multiplying an integer and a WizCoin object is commutative: 2 * purse is the same as purse * 2 . Instead of copying and pasting the code from __mul__() , we just call self.__mul__() and pass it the other parameter.

After updating wizcoin.py , practice using the reflected multiplication dunder method by entering the following into the interactive shell:

Keep in mind that in the expression 10 * purse , Python first calls the int class’s __mul__() method to see whether integers can be multiplied with WizCoin objects. Of course, Python’s built-in int class doesn’t know anything about the classes we create, so it returns NotImplemented . This signals to Python to next call WizCoin class’s __rmul__() , and if it exists, to handle this operation. If the calls to the int class’s __mul__() and WizCoin class’s __rmul__() both return NotImplemented , Python raises a TypeError exception.

Only WizCoin objects can be added to each other. This guarantees that the first WizCoin object’s __add__() method will handle the operation, so we don’t need to implement __radd__() . For example, in the expression purse + tipJar , the __add__() method for the purse object is called with tipJar passed for the other parameter. Because this call won’t return NotImplemented , Python doesn’t try to call the tipJar object’s __radd__() method with purse as the other parameter.

Table 17-2 contains a full listing of the available reflected dunder methods.

Table 17-2: Reflected Numeric Dunder Methods

Addition
Subtraction
Multiplication
Matrix multiplication (new in Python 3.5)
Division
Integer division
Modulus
Division and modulus
Exponentiation ,
Left shift
Right shift
Bitwise and
Bitwise or
Bitwise exclusive or

The full documentation for the reflected dunder methods is in the Python documentation at https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types .

In-Place Augmented Assignment Dunder Methods

The numeric and reflected dunder methods always create new objects rather than modifying the object in-place. The in-place dunder methods , called by the augmented assignment operators, such as += and *= , modify the object in-place rather than creating new objects. (There is an exception to this, which I’ll explain at the end of this section.) These dunder method names begin with an i , such as __iadd__() and __imul__() for the += and *= operators, respectively.

For example, when Python runs the code purse *= 2 , the expected behavior isn’t that the WizCoin class’s __imul__() method creates and returns a new WizCoin object with twice as many coins, and then assigns it the purse variable. Instead, the __imul__() method modifies the existing WizCoin object in purse so it has twice as many coins. This is a subtle but important difference if you want your classes to overload the augmented assignment operators.

Our WizCoin objects already overload the + and * operators, so let’s define the __iadd__() and __imul__() dunder methods so they overload the += and *= operators as well. In the expressions purse += tipJar and purse *= 2 , we call the __iadd__() and __ imul__() methods, respectively, with tipJar and 2 passed for the other parameter, respectively. Add the following to the end of the wizcoin.py file:

The WizCoin objects can use the += operator with other WizCoin objects and the *= operator with positive integers. Notice that after ensuring that the other parameter is valid, the in-place methods modify the self object in-place rather than creating a new WizCoin object. Enter the following into the interactive shell to see how the augmented assignment operators modify the WizCoin objects in-place:

The + operator 1 calls the __add__() or __radd__() dunder methods to create and return new objects 2 . The original objects operated on by the + operator remain unmodified. The in-place dunder methods 3 4 should modify the object in-place as long as the object is mutable (that is, it’s an object whose value can change). The exception is for immutable objects: because an immutable object can’t be modified, it’s impossible to modify it in-place. In that case, the in-place dunder methods should create and return a new object, just like the numeric and reflected numeric dunder methods.

We didn’t make the galleons , sickles , and knuts attributes read-only, which means they can change. So WizCoin objects are mutable. Most of the classes you write will create mutable objects, so you should design your in-place dunder methods to modify the object in-place.

If you don’t implement an in-place dunder method, Python will instead call the numeric dunder method. For example, if the WizCoin class had no __imul__() method, the expression purse *= 10 will call __mul__() instead and assign its return value to purse. Because WizCoin objects are mutable, this is unexpected behavior that could lead to subtle bugs.

Comparison Dunder Methods

Python’s sort() method and sorted() function contain an efficient sorting algorithm that you can access with a simple call. But if you want to compare and sort objects of the classes you make, you’ll need to tell Python how to compare two of these objects by implementing the comparison dunder methods. Python calls the comparison dunder methods behind the scenes whenever your objects are used in an expression with the < , > , <= , >= , == , and != comparison operators.

Before we explore the comparison dunder methods, let’s examine six functions in the operator module that perform the same operations as the six comparison operators. Our comparison dunder methods will be calling these functions. Enter the following into the interactive shell.

The operator module gives us function versions of the comparison operators. Their implementations are simple. For example, we could write our own operator.eq() function in two lines:

It’s useful to have a function form of the comparison operators because, unlike operators, functions can be passed as arguments to function calls. We’ll be doing this to implement a helper method for our comparison dunder methods.

First, add the following to the start of wizcoin.py . These imports give us access to the functions in the operator module and allow us to check whether the other argument in our method is a sequence by comparing it to collections.abc.Sequence :

Then add the following to the end of the wizcoin.py file:

Our comparison dunder methods call the _comparisonOperatorHelper() method 1 and pass the appropriate function from the operator module for the operatorFunc parameter. When we call operatorFunc() , we’re calling the function that was passed for the operatorFunc parameter— eq() 5 , ne() 6 , lt() 7 , le() 8 , gt() 9 , or ge() a —from the operator module. Otherwise, we’d have to duplicate the code in _comparisonOperatorHelper() in each of our six comparison dunder methods.

Our WizCoin objects can now be compared with other WizCoin objects 2 , integers and floats 3 , and sequence values of three number values that represent the galleons, sickles, and knuts 4 . Enter the following into the interactive shell to see this in action:

Our helper method calls isinstance(other, collections.abc.Sequence) to see whether other is a sequence data type, such as a tuple or list. By making WizCoin objects comparable with sequences, we can write code such as purse >= [2, 5, 10] for a quick comparison.

There are no reflected comparison dunder methods, such as __req__() or __rne__() , that you’ll need to implement. Instead, __lt__() and __gt__() reflect each other, __le__() and __ge__() reflect each other, and __eq__() and __ne__() reflect themselves. The reason is that the following relationships hold true no matter what the values on the left or right side of the operator are:

  • purse > [2, 5, 10] is the same as [2, 5, 10] < purse
  • purse >= [2, 5, 10] is the same as [2, 5, 10] <= purse
  • purse == [2, 5, 10] is the same as [2, 5, 10] == purse
  • purse != [2, 5, 10] is the same as [2, 5, 10] != purse

Once you’ve implemented the comparison dunder methods, Python’s sort() function will automatically use them to sort your objects. Enter the following into the interactive shell:

Table 17-3 contains a full listing of the available comparison dunder methods and operator functions.

Table 17-3: Comparison Dunder Methods and operator Module Functions

module
ual
ot qual
__lt__() ess han
ess than or qual
reater han
reater than or qual

You can see the implementation for these methods at https://autbor.com/wizcoinfull . The full documentation for the comparison dunder methods is in the Python documentation at https://docs.python.org/3/reference/datamodel.html#object.__lt__ .

The comparison dunder methods let objects of your classes use Python’s comparison operators rather than forcing you to create your own methods. If you’re creating methods named equals() or isGreaterThan() , they’re not Pythonic, and they’re a sign that you should use comparison dunder methods.

Python implements object-oriented features differently than other OOP languages, such as Java or C++. Instead of explicit getter and setter methods, Python has properties that allow you to validate attributes or make attributes read-only.

Python also lets you overload its operators via its dunder methods, which begin and end with double underscore characters. We overload common mathematical operators using the numeric and reflected numeric dunder methods. These methods provide a way for Python’s built-in operators to work with objects of the classes you create. If they’re unable to handle the data type of the object on the other side of the operator, they’ll return the built-in NotImplemented value. These dunder methods create and return new objects, whereas the in-place dunder methods (which overload the augmented assignment operators) modify the object in-place. The comparison dunder methods not only implement the six Python comparison operators for objects, but also allow Python’s sort() function to sort objects of your classes. You might want to use the eq() , ne() , lt() , le() , gt() , and ge() functions in the operator module to help you implement these dunder methods.

Properties and dunder methods allow you to write classes that are consistent and readable. They let you avoid much of the boilerplate code that other languages, such as Java, require you to write. To learn more about writing Pythonic code, two PyCon talks by Raymond Hettinger expand on these ideas: “Transforming Code into Beautiful, Idiomatic Python” at https://youtu.be/OSGv2VnC0go/ and “Beyond PEP 8—Best Practices for Beautiful, Intelligible Code” at https://youtu.be/wf-BqAjZb8M/ cover some of the concepts in this chapter and beyond.

There’s much more to learn about how to use Python effectively. The books Fluent Python (O’Reilly Media, 2021) by Luciano Ramalho and Effective Python (Addison-Wesley Professional, 2019) by Brett Slatkin provide more in-depth information about Python’s syntax and best practices, and are must-reads for anyone who wants to continue to learn more about Python.

5 Python Magic Methods or Dunder Methods to Know

Also called magic methods, dunder methods are necessary to understand Python. Here’s a guide to getting started with them.

Rahul Agarwal

In a piece I wrote on Object-Oriented Programming (OOP), I specifically addressed a single Python magic method, __init__, which is also called as a constructor method in OOP terminology. The magic part of __init__ is that it automatically gets called whenever an object is created. But the good news is that it’s not the only method that does so. Python provides users with many other magic methods that you have probably used without even knowing about them. Ever used len(), print() or the [] operator on a list? If so, you have been using Python dunder methods.

5 Python Magic Methods to Know

5 python dunder methods to know.

  • Operator Dunder Methods
  • Assignment Dunder Methods
  • __getitem__

1. Operator Magic Methods

Everything in Python is an object, ranging from the data types like int, str and float to the models we use in data science .  We can call methods on an object, like this str object:

Now, we can use various methods defined in the string class using the below syntax.

But, as we know, we can also use the + operator to concatenate multiple strings.

So, why does the addition operator work? How does the string object know what to do when it encounters the plus sign? How do you write it in the str class? And the same + operation happens differently in the case of integer objects. Thus, the operator + behaves differently in the case of string and integer. Fancy people call this  process  operator overloading .

So, can we add any two objects? Let’s try to add two objects from our elementary account class.

Doing so fails, as expected, since the operand + is not supported for an object of type account. But we can add the support of + to our account class using our magic method __add__.

Here, we added a magic method __add__ to our class, which takes two arguments  —  self and acc. We first need to check if acc is of class account. If it is, we return the sum of balances when we add these accounts. If we add anything else to an account other than an object from the account class, we would get a descriptive error. Let’s try it:

So, we can add any two objects. In fact, we also have different Python magic methods for a variety of other operators.

  • __sub__ for subtraction(-)  
  • __mul__ for multiplication(*)  
  • __truediv__ for division(/)  
  • __eq__ for equality (==)  
  • __lt__ for less than(<)  
  • __gt__ for greater than(>)  
  • __le__ for less than or equal to (≤)  
  • __ge__ for greater than or equal to (≥)

As a running example, I will try to explain all these concepts by creating a class called complex to handle complex numbers. Don’t worry, though: Complex is just the class ’  name, and I will keep the example as simple as possible.

Below, I have created a simple method called __add__ that adds two complex numbers or a complex number and an int/float. It first checks if the number being added is of type int or float or complex. Based on the type of number, we then do the required addition. We also use the isinstance function to check the type of the other object. Do read the hashtagged comments in the code box below.

It can be used as:

You would now be able to understand the following code, which allows us to add, subtract, multiply and divide complex numbers with themselves as well as scalars like float, int and so on. You can see how these methods then return a complex number. This code also provides the functionality to compare two complex numbers using __eq__,__lt__,__gt__.

You don’t necessarily need to understand all of the complex number math, but I have tried to use most of these Python magic methods in this particular class.

Now we can use our complex class as:

Read More Python Cheat Sheet: A Handy Guide to Python

2. Python __str__ Method for Strings

Why does the complex number print as a random string?

Ahh! You got me. This brings us to another Python dunder method called __str__ that lets us use the print method on our object. Here, the main idea is again that when we call print(object), it calls the __str__ method in the object. Here is how we can use that method with our complex class.

We can now recheck the output:

Now our object gets printed in a better way. But still, if we try to do the process in our notebook, the __str__ method is not called:

This happens because we aren’t printing in the above code, and thus the __str__ method doesn’t get called. In this case, another magic method called __repr__ gets called instead. As a result, we can just add this in our class to get the same result as a print. It ’ s a dunder method inside a dunder method. Pretty nice!

3. Python Len Dunder Method

len() is another function that works with strings, lists and matrices, among others. To use this function with our complex numbers class, we can use the __len__ magic method, even though this is really not a valid use case for complex numbers as the return type of __len__ needs to be an int, as per the documentation.

Here is its usage:

4. Python Assignment Operators

We know how the + operator works with an object. But have you wondered why the += operator works? For example:

This brings us to another set of Python dunder methods called assignment methods that include __iadd__, __isub__, __imul__, __itruediv__, and many others.

So, if we just add the method __iadd__ to our class, we would be able to make assignment-based additions too.

And use it as:

Further Reading 14 Best Data Science Books in 2022, According to Experts

5. Python ___getitem__ Dunder Method

Sometimes, objects might contain lists, and we might need to index the object to get the value from the list. To understand this, let ’ s take a different example. Imagine you work for a company that helps users trade stock. Each user will have a daily transaction book that will contain information about the user ’ s trades/transactions over the course of the day. We can implement such a class by:

Do you notice the __getitem__ here? This actually allows us to use indexing on objects of this particular class using this:

We can get the first trade done by the user or the second one based on the index we use. This is just a simple example, but you can set your object up to get a lot more information when you use indexing.

Don’t Forget Python Dunder Methods

Python is a magical language, and there are many constructs in Python that even advanced users may not know about. Dunder methods might be very well one of them. I hope with this post, you get a good glimpse of various dunder methods that Python offers and also understand how to implement them yourself.

Recent Python Algorithms Articles

Understanding Duck Typing in Python

Learning Materials

  • Business Studies
  • Combined Science
  • Computer Science
  • Engineering
  • English Literature
  • Environmental Science
  • Human Geography
  • Macroeconomics
  • Microeconomics
  • Python Assignment Operator

Dive into the world of Python programming and explore the essential role that Python Assignment Operators play in simplifying and optimising your code. This comprehensive guide will help you understand the basics of Python Assignment Operators, and learn about the different types available, followed by detailed explanations and examples to solidify your grasp. Furthermore, you will discover the concept of overloading in the context of Python Assignment Operators, and find out how it can be applied in custom classes for enhanced functionality. Lastly, you will gain invaluable insights into Python Assignment Operator precedence, along with useful tips to manage precedence effectively and streamline your coding experience. Embark on this fascinating journey to expand your Python knowledge and enrich your programming skills.

Python Assignment Operator

Create learning materials about Python Assignment Operator with our free learning app!

  • Instand access to millions of learning materials
  • Flashcards, notes, mock-exams and more
  • Everything you need to ace your exams

Millions of flashcards designed to help you ace your studies

  • Cell Biology

How are Python assignment operators evaluated in an expression with multiple operators?

What is the correct evaluation order for the following expression in Python? `x = 10 + 5 * 2`

What is one tip for managing Python assignment operator precedence effectively?

What advantage do compound assignment operators provide in Python?

What are the advantages of Python Assignment Operator Overloading?

What is the most basic and commonly used assignment operator in Python?

What are the two categories of Python assignment operators?

How do compound assignment operators work in Python?

What does the += assignment operator do in Python?

What does the **= assignment operator do in Python?

What does the //= assignment operator do in Python?

Review generated flashcards

to start learning or create your own AI flashcards

Start learning or create your own AI flashcards

  • Algorithms in Computer Science
  • Computer Network
  • Computer Organisation and Architecture
  • Computer Programming
  • 2d Array in C
  • AND Operator in C
  • Access Modifiers
  • Actor Model
  • Algorithm in C
  • Array as function argument in c
  • Assignment Operator in C
  • Automatically Creating Arrays in Python
  • Bitwise Operators in C
  • C Arithmetic Operations
  • C Array of Structures
  • C Functions
  • C Math Functions
  • C Memory Address
  • C Plus Plus
  • C Program to Find Roots of Quadratic Equation
  • C Programming Language
  • Change Data Type in Python
  • Classes in Python
  • Comments in C
  • Common Errors in C Programming
  • Compound Statement in C
  • Concurrency Vs Parallelism
  • Concurrent Programming
  • Conditional Statement
  • Critical Section
  • Data Types in Programming
  • Declarative Programming
  • Decorator Pattern
  • Distributed Programming
  • Do While Loop in C
  • Dynamic allocation of array in c
  • Encapsulation programming
  • Event Driven Programming
  • Exception Handling
  • Executable File
  • Factory Pattern
  • For Loop in C
  • Formatted Output in C
  • Functions in Python
  • How to return multiple values from a function in C
  • Identity Operator in Python
  • Imperative programming
  • Increment and Decrement Operators in C
  • Inheritance in Oops
  • Insertion Sort Python
  • Instantiation
  • Integrated Development Environments
  • Integration in C
  • Interpreter Informatics
  • Java Abstraction
  • Java Annotations
  • Java Arithmetic Operators
  • Java Arraylist
  • Java Arrays
  • Java Assignment Operators
  • Java Bitwise Operators
  • Java Classes And Objects
  • Java Collections Framework
  • Java Constructors
  • Java Data Types
  • Java Do While Loop
  • Java Enhanced For Loop
  • Java Expection Handling
  • Java File Class
  • Java File Handling
  • Java Finally
  • Java For Loop
  • Java Function
  • Java Generics
  • Java IO Package
  • Java If Else Statements
  • Java If Statements
  • Java Inheritance
  • Java Interfaces
  • Java List Interface
  • Java Logical Operators
  • Java Map Interface
  • Java Method Overloading
  • Java Method Overriding
  • Java Multidimensional Arrays
  • Java Multiple Catch Blocks
  • Java Nested If
  • Java Nested Try
  • Java Non Primitive Data Types
  • Java Operators
  • Java Polymorphism
  • Java Primitive Data Types
  • Java Queue Interface
  • Java Recursion
  • Java Reflection
  • Java Relational Operators
  • Java Set Interface
  • Java Single Dimensional Arrays
  • Java Statements
  • Java Static Keywords
  • Java Switch Statement
  • Java Syntax
  • Java This Keyword
  • Java Try Catch
  • Java Type Casting
  • Java Virtual Machine
  • Java While Loop
  • Javascript Anonymous Functions
  • Javascript Arithmetic Operators
  • Javascript Array Methods
  • Javascript Array Sort
  • Javascript Arrays
  • Javascript Arrow Functions
  • Javascript Assignment Operators
  • Javascript Async
  • Javascript Asynchronous Programming
  • Javascript Await
  • Javascript Bitwise Operators
  • Javascript Callback
  • Javascript Callback Functions
  • Javascript Changing Elements
  • Javascript Classes
  • Javascript Closures
  • Javascript Comparison Operators
  • Javascript DOM Events
  • Javascript DOM Manipulation
  • Javascript Data Types
  • Javascript Do While Loop
  • Javascript Document Object
  • Javascript Event Loop
  • Javascript For In Loop
  • Javascript For Loop
  • Javascript For Of Loop
  • Javascript Function
  • Javascript Function Expressions
  • Javascript Hoisting
  • Javascript If Else Statement
  • Javascript If Statement
  • Javascript Immediately Invoked Function Expressions
  • Javascript Inheritance
  • Javascript Interating Arrays
  • Javascript Logical Operators
  • Javascript Loops
  • Javascript Multidimensional Arrays
  • Javascript Object Creation
  • Javascript Object Prototypes
  • Javascript Objects
  • Javascript Operators
  • Javascript Primitive Data Types
  • Javascript Promises
  • Javascript Reference Data Types
  • Javascript Scopes
  • Javascript Selecting Elements
  • Javascript Spread And Rest
  • Javascript Statements
  • Javascript Strict Mode
  • Javascript Switch Statement
  • Javascript Syntax
  • Javascript Ternary Operator
  • Javascript This Keyword
  • Javascript Type Conversion
  • Javascript While Loop
  • Linear Equations in C
  • Log Plot Python
  • Logical Error
  • Logical Operators in C
  • Loop in programming
  • Matrix Operations in C
  • Membership Operator in Python
  • Model View Controller
  • Nested Loops in C
  • Nested if in C
  • Numerical Methods in C
  • OR Operator in C
  • Object orientated programming
  • Observer Pattern
  • One Dimensional Arrays in C
  • Oops concepts
  • Operators in Python
  • Parameter Passing
  • Pascal Programming Language
  • Plot in Python
  • Plotting In Python
  • Pointer Array C
  • Pointers and Arrays
  • Pointers in C
  • Polymorphism programming
  • Procedural Programming
  • Programming Control Structures
  • Programming Language PHP
  • Programming Languages
  • Programming Paradigms
  • Programming Tools
  • Python Arithmetic Operators
  • Python Array Operations
  • Python Arrays
  • Python Bar Chart
  • Python Bitwise Operators
  • Python Bubble Sort
  • Python Comparison Operators
  • Python Data Types
  • Python Indexing
  • Python Infinite Loop
  • Python Loops
  • Python Multi Input
  • Python Range Function
  • Python Sequence
  • Python Sorting
  • Python Subplots
  • Python while else
  • Quicksort Python
  • R Programming Language
  • Race Condition
  • Ruby programming language
  • Runtime System
  • Scatter Chart Python
  • Secant Method
  • Shift Operator C
  • Single Structures In C
  • Singleton Pattern
  • Software Design Patterns
  • Statements in C
  • Storage Classes in C
  • String Formatting C
  • String in C
  • Strings in Python
  • Structures in C
  • Swift programming language
  • Syntax Errors
  • Threading In Computer Science
  • Variable Informatics
  • Variable Program
  • Variables in C
  • Version Control Systems
  • While Loop in C
  • Write Functions in C
  • exclusive or operation
  • for Loop in Python
  • if else in C
  • if else in Python
  • scanf Function with Buffered Input
  • switch Statement in C
  • while Loop in Python
  • Computer Systems
  • Data Representation in Computer Science
  • Data Structures
  • Functional Programming
  • Issues in Computer Science
  • Problem Solving Techniques
  • Theory of Computation

The Basics of Python Assignment Operators

Assignment operators in Python are used to assign values to variables. They allow programmers to perform different types of operations and store the results in variables.

Different types of Python Assignment Operators

=Assigns a value to a variable
+=Addition assignment
-=Subtraction assignment
*=Multiplication assignment
/=Division assignment
%=Modulus assignment
//=Floor division assignment
**=Exponentiation assignment
&=Bitwise AND assignment
|=Bitwise OR assignment
^=Bitwise XOR assignment
>>=Bitwise right shift assignment
<<=Bitwise left shift assignment

How Python Assignment Operators work

Addition assignment operator example: x = 10 # x is assigned the value 10 x += 5 # x is updated to x + 5, which is 15 print(x) # The output will be 15

Python assignment operators not only save time by making code shorter but also offer the advantage of improved performance. Compound assignment operators can lead to faster execution as the interpreted language like Python can optimize such operations more effectively than separate operations.

Python Assignment Operator List and Examples

As we delve deeper into the world of Python assignment operators, we can see that each of these operators serves a distinct purpose and offers unique advantages. To gain a comprehensive understanding of their functionality, let's explore some common Python assignment operators in great detail with the help of practical examples.

+=, -=, *=, and **= Assignment Operators in Python

  • += (Addition assignment): This operator is used to add the value on the right to the variable on the left and then update the value of the variable.
  • -= (Subtraction assignment): This operator is used to subtract the right side value from the left side variable and then update the value of the variable.
  • *= (Multiplication assignment): This operator is used to multiply the value on the right with the variable on the left and then update the value of the variable.
  • **= (Exponentiation assignment): This operator is used to raise the variable on the left to the power of the value on the right and then update the value of the variable.

x = 10 # x is assigned the value 10 x += 5 # x is updated to x + 5, which is 15 x -= 3 # x is updated to x - 3, which is 12 x *= 2 # x is updated to x * 2, which is 24 x **= 2 # x is updated to x ** 2, which is 576 print(x) # The output will be 576

/=, //=, %=, and &= Assignment Operators in Python

  • /= (Division assignment): This operator is used to divide the variable on the left by the value on the right and then update the value of the variable.
  • //= (Floor division assignment): This operator performs floor division on the left side variable by the right side value and then updates the value of the variable.
  • %= (Modulus assignment): This operator calculates the modulus of the left side variable divided by the right side value and then updates the value of the variable.
  • &= (Bitwise AND assignment): This operator performs a bitwise AND operation between the left side variable and the right side value, and then updates the value of the variable.

x = 50 # x is assigned the value 50 x /= 2 # x is updated to x / 2, which is 25 x //= 3 # x is updated to x // 3, which is 8 x %= 5 # x is updated to x % 5, which is 3 x &= 2 # x is updated to x & 2, which is 2 print(x) # The output will be 2

Overloading Python Assignment Operators

What is python assignment operator overloading, using python assignment operator overloading in custom classes.

  • __add__(): Corresponds to the '+=' operator
  • __sub__(): Corresponds to the '-=' operator
  • __mul__(): Corresponds to the '*=' operator
  • __truediv__(): Corresponds to the '/=' operator
  • __floordiv__(): Corresponds to the '//=' operator
  • __mod__(): Corresponds to the '%=' operator
  • __pow__(): Corresponds to the '**=' operator
  • __and__(): Corresponds to the '&=' operator
  • __or__(): Corresponds to the '|=' operator
  • __xor__(): Corresponds to the '^=' operator
  • __lshift__(): Corresponds to the '<<=' operator
  • __rshift__(): Corresponds to the '>>=' operator

Advantages of Python Assignment Operator Overloading

  • Improved readability: By overloading assignment operators, you can make your code more self-explanatory and easier to understand since the operators are more intuitive than function calls.
  • Enhanced expressiveness: Operator overloading enables custom classes to provide a natural syntax that closely resembles the native data types, allowing programmers to write more concise and efficient code.
  • Increased consistency: When operators are consistently represented across user-defined and built-in classes, the language semantics remain uniform, resulting in improved consistency.
  • Greater abstraction: Overloading assignment operators allows you to hide the implementation details from the users, providing them with a straightforward and consistent interface for interacting with custom objects.

Python Assignment Operator Precedence

Understanding operator precedence in python, precedence rules for python assignment operators.

  • Assignment operators (=, +=, -=, *=, etc.) are evaluated from right to left.
  • Assignment operators have a lower precedence compared to all arithmetic, relational, and logical operators.
  • When multiple assignment operators are present in an expression, Python evaluates the rightmost assignment operator first and proceeds from right to left.

To illustrate these precedence rules, consider the following example: ```python x = 10 + 5 * 2 ``` In this example, the multiplication (*) operator takes precedence over the addition (+) and assignment (=) operators. Therefore, the expression will be evaluated as follows: `x = 10 + (5 * 2)`. The final value of x will be 20.

Tips for Managing Python Assignment Operator Precedence

  • Always use parentheses to group operations explicitly and avoid ambiguities. Using parentheses not only ensures that the expression is evaluated in the correct order but also improves the readability of your code.
  • When evaluating complex expressions, break them down into smaller, simpler expressions and assign intermediate results to temporary variables. This approach not only makes your code more readable but also minimizes the chance of encountering precedence issues.
  • If you are uncertain about the precedence levels of the operators involved in an expression, consult the Python documentation for clarification. Familiarizing yourself with the precedence rules will significantly enhance your ability to write accurate and reliable code.
  • Always test your code thoroughly to ensure that the operator precedence does not introduce any unintended outcomes. Adequate testing will help you identify and correct potential errors and ambiguities caused by operator precedence.

Python Assignment Operator - Key takeaways

Python Assignment Operator - used to assign values to variables and perform operations while updating variable values.

Python assignment operator overload - using 'magic methods' to extend functionality of assignment operators for custom classes.

Python assignment operator list - Basic (=) and compound (+=, -=, *=, etc.) assignment operators available in Python for different types of operations.

What is the assignment operator in python? - '=' is the basic assignment operator used for assigning values to variables.

Python assignment operator precedence - Set of rules governing the order in which assignment operators are executed in an expression, assignment operators have the lowest precedence level.

Flashcards in Python Assignment Operator 31

Assignment operators generally have the lowest precedence level and are evaluated from right to left after all arithmetic, relational, and logical operators have been executed.

The correct evaluation order is: `x = 10 + (5 * 2)`, with the multiplication operator taking precedence over the addition and assignment operators.

One tip is to use parentheses to group operations explicitly and avoid ambiguities, ensuring the expression is evaluated in the correct order while improving code readability.

Compound assignment operators provide improved performance and make the code more concise and readable by performing operations and updating variables concurrently.

The advantages of Python Assignment Operator Overloading include improved readability, enhanced expressiveness, increased consistency, and greater abstraction.

The equal sign (=) is the most basic and commonly used assignment operator, which assigns a value to a variable.

Python Assignment Operator

Learn with 31 Python Assignment Operator flashcards in the free Vaia app

We have 14,000 flashcards about Dynamic Landscapes.

Already have an account? Log in

Frequently Asked Questions about Python Assignment Operator

Test your knowledge with multiple choice flashcards.

Python Assignment Operator

Join the Vaia App and learn efficiently with millions of flashcards and more!

Keep learning, you are doing great.

Discover learning materials with the free Vaia app

1

Vaia is a globally recognized educational technology company, offering a holistic learning platform designed for students of all ages and educational levels. Our platform provides learning support for a wide range of subjects, including STEM, Social Sciences, and Languages and also helps students to successfully master various tests and exams worldwide, such as GCSE, A Level, SAT, ACT, Abitur, and more. We offer an extensive library of learning materials, including interactive flashcards, comprehensive textbook solutions, and detailed explanations. The cutting-edge technology and tools we provide help students create their own learning materials. StudySmarter’s content is not only expert-verified but also regularly updated to ensure accuracy and relevance.

Python Assignment Operator

Vaia Editorial Team

Team Computer Science Teachers

  • 12 minutes reading time
  • Checked by Vaia Editorial Team

Study anywhere. Anytime.Across all devices.

Create a free account to save this explanation..

Save explanations to your personalised space and access them anytime, anywhere!

By signing up, you agree to the Terms and Conditions and the Privacy Policy of Vaia.

Sign up to highlight and take notes. It’s 100% free.

Join over 22 million students in learning with our Vaia App

The first learning app that truly has everything you need to ace your exams in one place

  • Flashcards & Quizzes
  • AI Study Assistant
  • Study Planner
  • Smart Note-Taking

Join over 22 million students in learning with our Vaia App

Privacy Overview

Operator and Function Overloading in Python

Operator and Function Overloading in Custom Python Classes

Table of Contents

The Python Data Model

The internals of operations like len() and [], giving a length to your objects using len(), making your objects work with abs(), printing your objects prettily using str(), representing your objects using repr(), making your objects truthy or falsey using bool(), making your objects capable of being added using +, shortcuts: the += operator, indexing and slicing your objects using [], reverse operators: making your classes mathematically correct, a complete example, recap and resources.

If you’ve used the + or * operator on a str object in Python, you must have noticed its different behavior when compared to int or float objects:

You might have wondered how the same built-in operator or function shows different behavior for objects of different classes. This is called operator overloading or function overloading respectively. This article will help you understand this mechanism, so that you can do the same in your own Python classes and make your objects more Pythonic.

You’ll learn the following:

  • The API that handles operators and built-ins in Python
  • The “secret” behind len() and other built-ins
  • How to make your classes capable of using operators
  • How to make your classes compatible with Python’s built-in functions

Free Bonus: Click here to get access to a free Python OOP Cheat Sheet that points you to the best tutorials, videos, and books to learn more about Object-Oriented Programming with Python.

As a bonus, you’ll also see an example class, objects of which will be compatible with many of these operators and functions. Let’s get started!

Say you have a class representing an online order having a cart (a list ) and a customer (a str or instance of another class which represents a customer).

Note: If you need a refresher on OOP in Python, check out this tutorial on Real Python: Object-Oriented Programming (OOP) in Python

In such a case, it is quite natural to want to obtain the length of the cart list. Someone new to Python might decide to implement a method called get_cart_len() in their class to do this. But you can configure the built-in len() in such a way that it returns the length of the cart list when given our object.

In another case, we might want to append something to the cart. Again, someone new to Python would think of implementing a method called append_to_cart() that takes an item and appends it to the cart list. But you can configure the + operator in such a way that it appends a new item to the cart.

Python does all this using special methods . These special methods have a naming convention, where the name starts with two underscores , followed by an identifier and ends with another pair of underscores.

Essentially, each built-in function or operator has a special method corresponding to it. For example, there’s __len__(), corresponding to len() , and __add__() , corresponding to the + operator.

By default, most of the built-ins and operators will not work with objects of your classes. You must add the corresponding special methods in your class definition to make your object compatible with built-ins and operators.

When you do this, the behavior of the function or operator associated with it changes according to that defined in the method.

This is exactly what the Data Model (Section 3 of the Python documentation) helps you accomplish. It lists all the special methods available and provides you with the means of overloading built-in functions and operators so that you can use them on your own objects.

Let’s see what this means.

Fun fact: Due to the naming convention used for these methods, they are also called dunder methods which is a shorthand for d ouble under score methods . Sometimes they’re also referred to as special methods or magic methods . We prefer dunder methods though!

Every class in Python defines its own behavior for built-in functions and methods. When you pass an instance of some class to a built-in function or use an operator on the instance, it is actually equivalent to calling a special method with relevant arguments.

If there is a built-in function, func() , and the corresponding special method for the function is __func__() , Python interprets a call to the function as obj.__func__() , where obj is the object. In the case of operators, if you have an operator opr and the corresponding special method for it is __opr__() , Python interprets something like obj1 <opr> obj2 as obj1.__opr__(obj2) .

So, when you’re calling len() on an object, Python handles the call as obj.__len__() . When you use the [] operator on an iterable to obtain the value at an index, Python handles it as itr.__getitem__(index) , where itr is the iterable object and index is the index you want to obtain.

Therefore, when you define these special methods in your own class, you override the behavior of the function or operator associated with them because, behind the scenes, Python is calling your method. Let’s get a better understanding of this:

As you can see, when you use the function or its corresponding special method, you get the same result. In fact, when you obtain the list of attributes and methods of a str object using dir() , you’ll see these special methods in the list in addition to the usual methods available on str objects:

If the behavior of a built-in function or operator is not defined in the class by the special method, then you will get a TypeError .

So, how can you use special methods in your classes?

Overloading Built-in Functions

Many of the special methods defined in the Data Model can be used to change the behavior of functions such as len , abs , hash , divmod , and so on. To do this, you only need to define the corresponding special method in your class. Let’s look at a few examples:

To change the behavior of len() , you need to define the __len__() special method in your class. Whenever you pass an object of your class to len() , your custom definition of __len__() will be used to obtain the result. Let’s implement len() for the order class we talked about in the beginning:

As you can see, you can now use len() to directly obtain the length of the cart. Moreover, it makes more intuitive sense to say “length of order” rather than calling something like order.get_cart_len() . Your call is both Pythonic and more intuitive. When you don’t have the __len__() method defined but still call len() on your object, you get a TypeError :

But, when overloading len() , you should keep in mind that Python requires the function to return an integer. If your method were to return anything other than an integer, you would get a TypeError . This, most probably, is to keep it consistent with the fact that len() is generally used to obtain the length of a sequence, which can only be an integer:

You can dictate the behavior of the abs() built-in for instances of your class by defining the __abs__() special method in the class. There are no restrictions on the return value of abs() , and you get a TypeError when the special method is absent in your class definition.

In a class representing a vector in a two-dimensional space, abs() can be used to get the length of the vector. Let’s see it in action:

It makes more intuitive sense to say “absolute value of vector” rather than calling something like vector.get_mag() .

The str() built-in is used to cast an instance of a class to a str object, or more appropriately, to obtain a user-friendly string representation of the object which can be read by a normal user rather than the programmer. You can define the string format your object should be displayed in when passed to str() by defining the __str__() method in your class. Moreover, __str__() is the method that is used by Python when you call print() on your object.

Let’s implement this in the Vector class to format Vector objects as xi+yj . A negative y-component will be handled using the format mini-language :

It is necessary that __str__() returns a str object, and we get a TypeError if the return type is non-string.

The repr() built-in is used to obtain the parsable string representation of an object. If an object is parsable, that means that Python should be able to recreate the object from the representation when repr is used in conjunction with functions like eval() . To define the behavior of repr() , you can use the __repr__() special method.

This is also the method Python uses to display the object in a REPL session. If the __repr__() method is not defined, you will get something like <__main__.Vector object at 0x...> trying to look at the object in the REPL session. Let’s see it in action in the Vector class:

Note: In cases where the __str__() method is not defined, Python uses the __repr__() method to print the object, as well as to represent the object when str() is called on it. If both the methods are missing, it defaults to <__main__.Vector ...> . But __repr__() is the only method that is used to display the object in an interactive session. Absence of it in the class yields <__main__.Vector ...> .

Also, while this distinction between __str__() and __repr__() is the recommended behavior, many of the popular libraries ignore this distinction and use the two methods interchangeably.

Here’s a recommended article on __repr__() and __str__() by our very own Dan Bader: Python String Conversion 101: Why Every Class Needs a “repr” .

The bool() built-in can be used to obtain the truth value of an object. To define its behavior, you can use the __bool__() ( __nonzero__() in Python 2.x) special method.

The behavior defined here will determine the truth value of an instance in all contexts that require obtaining a truth value such as in if statements.

As an example, for the Order class that was defined above, an instance can be considered to be truthy if the length of the cart list is non-zero. This can be used to check whether an order should be processed or not:

Note: When the __bool__() special method is not implemented in a class, the value returned by __len__() is used as the truth value, where a non-zero value indicates True and a zero value indicates False . In case both the methods are not implemented, all instances of the class are considered to be True .

There are many more special methods that overload built-in functions. You can find them in the documentation . Having discussed some of them, let’s move to operators.

Overloading Built-in Operators

Changing the behavior of operators is just as simple as changing the behavior of functions. You define their corresponding special methods in your class, and the operators work according to the behavior defined in these methods.

These are different from the above special methods in the sense that they need to accept another argument in the definition other than self , generally referred to by the name other . Let’s look at a few examples.

The special method corresponding to the + operator is the __add__() method. Adding a custom definition of __add__() changes the behavior of the operator. It is recommended that __add__() returns a new instance of the class instead of modifying the calling instance itself. You’ll see this behavior quite commonly in Python:

You can see above that using the + operator on a str object actually returns a new str instance, keeping the value of the calling instance ( a ) unmodified. To change it, we need to explicitly assign the new instance to a .

Let’s implement the ability to append new items to our cart in the Order class using the operator. We’ll follow the recommended practice and make the operator return a new Order instance that has our required changes instead of making the changes directly to our instance:

Similarly, you have the __sub__() , __mul__() , and other special methods which define the behavior of - , * , and so on. These methods should return a new instance of the class as well.

The += operator stands as a shortcut to the expression obj1 = obj1 + obj2 . The special method corresponding to it is __iadd__() . The __iadd__() method should make changes directly to the self argument and return the result, which may or may not be self . This behavior is quite different from __add__() since the latter creates a new object and returns that, as you saw above.

Roughly, any += use on two objects is equivalent to this:

Here, result is the value returned by __iadd__() . The second assignment is taken care of automatically by Python, meaning that you do not need to explicitly assign obj1 to the result as in the case of obj1 = obj1 + obj2 .

Let’s make this possible for the Order class so that new items can be appended to the cart using += :

As can be seen, any change is made directly to self and it is then returned. What happens when you return some random value, like a string or an integer?

Even though the relevant item was appended to the cart, the value of order changed to what was returned by __iadd__() . Python implicitly handled the assignment for you. This can lead to surprising behavior if you forget to return something in your implementation:

Since all Python functions (or methods) return None implicitly, order is reassigned to None and the REPL session doesn’t show any output when order is inspected. Looking at the type of order , you see that it is now NoneType . Therefore, always make sure that you’re returning something in your implementation of __iadd__() and that it is the result of the operation and not anything else.

Similar to __iadd__() , you have __isub__() , __imul__() , __idiv__() and other special methods which define the behavior of -= , *= , /= , and others alike.

Note: When __iadd__() or its friends are missing from your class definition but you still use their operators on your objects, Python uses __add__() and its friends to get the result of the operation and assigns that to the calling instance. Generally speaking, it is safe to not implement __iadd__() and its friends in your classes as long as __add__() and its friends work properly (return something which is the result of the operation).

The Python documentation has a good explanation of these methods. Also, take a look at this example which shows the caveats involved with += and the others when working with immutable types.

The [] operator is called the indexing operator and is used in various contexts in Python such as getting the value at an index in sequences, getting the value associated with a key in dictionaries, or obtaining a part of a sequence through slicing. You can change its behavior using the __getitem__() special method.

Let’s configure our Order class so that we can directly use the object and obtain an item from the cart:

You’ll notice that above, the name of the argument to __getitem__() is not index but key . This is because the argument can be of mainly three forms: an integer value , in which case it is either an index or a dictionary key, a string value , in which case it is a dictionary key, and a slice object , in which case it will slice the sequence used by the class. While there are other possibilities, these are the ones most commonly encountered.

Since our internal data structure is a list, we can use the [] operator to slice the list, as in this case, the key argument will be a slice object. This is one of the biggest advantages of having a __getitem__() definition in your class. As long as you’re using data structures that support slicing (lists, tuples, strings, and so on), you can configure your objects to directly slice the structure:

Note: There is a similar __setitem__() special method that is used to define the behavior of obj[x] = y . This method takes two arguments in addition to self , generally called key and value , and can be used to change the value at key to value .

While defining the __add__() , __sub__() , __mul__() , and similar special methods allows you to use the operators when your class instance is the left-hand side operand, the operator will not work if the class instance is the right-hand side operand:

If your class represents a mathematical entity like a vector, a coordinate, or a complex number , applying the operators should work in both the cases since it is a valid mathematical operation.

Moreover, if the operators work only when the instance is the left operand, we are violating the fundamental principle of commutativity in many cases. Therefore, to help you make your classes mathematically correct, Python provides you with reverse special methods such as __radd__() , __rsub__() , __rmul__() , and so on.

These handle calls such as x + obj , x - obj , and x * obj , where x is not an instance of the concerned class. Just like __add__() and the others, these reverse special methods should return a new instance of class with the changes of the operation rather than modifying the calling instance itself.

Let’s configure __radd__() in the Order class in such a way that it will append something at the front of the cart. This can be used in cases where the cart is organized in terms of the priority of the orders:

To drive all these points home, it’s better to look at an example class which implements these operators together.

Let’s reinvent the wheel and implement our own class to represent complex numbers, CustomComplex . Objects of our class will support a variety of built-in functions and operators, making them behave very similar to the built-in complex numbers class:

The constructor handles only one kind of call, CustomComplex(a, b) . It takes positional arguments, representing the real and imaginary parts of the complex number.

Let’s define two methods inside the class, conjugate() and argz() , which will give us the complex conjugate and the argument of a complex number respectively:

Note: __class__ is not a special method but a class attribute which is present by default. It has a reference to the class. By using it here, we are obtaining that and then calling the constructor in the usual manner. In other words, this is equivalent to CustomComplex(real, imag) . This is done here to avoid refactoring the code if the name of the class changes someday.

Next, we configure abs() to return the modulus of a complex number:

We will follow the recommended distinction between __repr__() and __str__() and use the first for the parsable string representation and the second for a “pretty” representation.

The __repr__() method will simply return CustomComplex(a, b) in a string so that we can call eval() to recreate the object, while the __str__() method will return the complex number in brackets, as (a+bj) :

Mathematically, it is possible to add any two complex numbers or add a real number to a complex number. Let’s configure the + operator in such a way that it works for both cases.

The method will check the type of the right-hand side operator. In case it is an int or a float , it will increment only the real part (since any real number, a , is equivalent to a+0j ), while in the case of another complex number, it will change both the parts:

Similarly, we define the behavior for - and * :

Since both addition and multiplication are commutative, we can define their reverse operators by calling __add__() and __mul__() in __radd__() and __rmul__() respectively. On the other hand, the behavior of __rsub__() needs to be defined since subtraction is not commutative:

Note: You might have noticed that we didn’t add a construct to handle a CustomComplex instance here. This is because, in such a case, both the operands are instances of our class, and __rsub__() won’t be responsible for handling the operation. Instead, __sub__() will be called. This is a subtle but important detail.

Now, we take care of the two operators, == and != . The special methods used for them are __eq__() and __ne__() , respectively. Two complex numbers are said to be equal if their corresponding real and imaginary parts are both equal. They are said to be unequal when either one of these are unequal:

Note: The Floating-Point Guide is an article that talks about comparing floats and floating-point precision. It highlights the caveats involved in comparing floats directly, which is something we’re doing here.

It is also possible to raise a complex number to any power using a simple formula . We configure the behavior for both the built-in pow() and the ** operator using the __pow__() special method:

Note: Take a close look at the definition of the method. We are calling abs() to obtain the modulus of the complex number. So, once you’ve defined the special method for a particular function or operator in your class, it can be used in other methods of the same class.

Let’s create two instances of this class, one having a positive imaginary part and one having a negative imaginary part:

String representations:

Recreating the object using eval() with repr() :

Addition, subtraction, and multiplication:

Equality and inequality checks:

Finally, raising a complex number to some power:

As you can see, objects of our custom class behave and look like those of a built-in class and are very Pythonic. The full example code for this class is embedded below.

Solution: "A Complete Example" Show/Hide

In this tutorial, you learned about the Python Data Model and how the Data Model can be used to build Pythonic classes. You learned about changing the behavior of built-in functions such as len() , abs() , str() , bool() , and so on. You also learned about changing the behavior of built-in operators like + , - , * , ** , and so forth.

After reading this, you can confidently create classes that make use of the best idiomatic features of Python and make your objects Pythonic!

For more information on the Data Model, and function and operator overloading, take a look at these resources:

  • Section 3.3, Special Method Names of the Data Model section in the Python documentation
  • Fluent Python by Luciano Ramalho
  • Python Tricks: The Book

🐍 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 Malay Agarwal

Malay Agarwal

A tech geek with a philosophical mind and a hand that can wield a pen.

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 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 OOP Cheat Sheet

Object-Oriented Programming in Python: The 7 Best Resources (A Free PDF Cheat Sheet)

🔒 No spam. We take your privacy seriously.

python assignment dunder

  • Python »
  • 3.12.5 Documentation »
  • The Python Language Reference »
  • 3. Data model
  • Theme Auto Light Dark |

3. Data model ¶

3.1. objects, values and types ¶.

Objects are Python’s abstraction for data. All data in a Python program is represented by objects or by relations between objects. (In a sense, and in conformance to Von Neumann’s model of a “stored program computer”, code is also represented by objects.)

Every object has an identity, a type and a value. An object’s identity never changes once it has been created; you may think of it as the object’s address in memory. The is operator compares the identity of two objects; the id() function returns an integer representing its identity.

CPython implementation detail: For CPython, id(x) is the memory address where x is stored.

An object’s type determines the operations that the object supports (e.g., “does it have a length?”) and also defines the possible values for objects of that type. The type() function returns an object’s type (which is an object itself). Like its identity, an object’s type is also unchangeable. [ 1 ]

The value of some objects can change. Objects whose value can change are said to be mutable ; objects whose value is unchangeable once they are created are called immutable . (The value of an immutable container object that contains a reference to a mutable object can change when the latter’s value is changed; however the container is still considered immutable, because the collection of objects it contains cannot be changed. So, immutability is not strictly the same as having an unchangeable value, it is more subtle.) An object’s mutability is determined by its type; for instance, numbers, strings and tuples are immutable, while dictionaries and lists are mutable.

Objects are never explicitly destroyed; however, when they become unreachable they may be garbage-collected. An implementation is allowed to postpone garbage collection or omit it altogether — it is a matter of implementation quality how garbage collection is implemented, as long as no objects are collected that are still reachable.

CPython implementation detail: CPython currently uses a reference-counting scheme with (optional) delayed detection of cyclically linked garbage, which collects most objects as soon as they become unreachable, but is not guaranteed to collect garbage containing circular references. See the documentation of the gc module for information on controlling the collection of cyclic garbage. Other implementations act differently and CPython may change. Do not depend on immediate finalization of objects when they become unreachable (so you should always close files explicitly).

Note that the use of the implementation’s tracing or debugging facilities may keep objects alive that would normally be collectable. Also note that catching an exception with a try … except statement may keep objects alive.

Some objects contain references to “external” resources such as open files or windows. It is understood that these resources are freed when the object is garbage-collected, but since garbage collection is not guaranteed to happen, such objects also provide an explicit way to release the external resource, usually a close() method. Programs are strongly recommended to explicitly close such objects. The try … finally statement and the with statement provide convenient ways to do this.

Some objects contain references to other objects; these are called containers . Examples of containers are tuples, lists and dictionaries. The references are part of a container’s value. In most cases, when we talk about the value of a container, we imply the values, not the identities of the contained objects; however, when we talk about the mutability of a container, only the identities of the immediately contained objects are implied. So, if an immutable container (like a tuple) contains a reference to a mutable object, its value changes if that mutable object is changed.

Types affect almost all aspects of object behavior. Even the importance of object identity is affected in some sense: for immutable types, operations that compute new values may actually return a reference to any existing object with the same type and value, while for mutable objects this is not allowed. For example, after a = 1; b = 1 , a and b may or may not refer to the same object with the value one, depending on the implementation. This is because int is an immutable type, so the reference to 1 can be reused. This behaviour depends on the implementation used, so should not be relied upon, but is something to be aware of when making use of object identity tests. However, after c = []; d = [] , c and d are guaranteed to refer to two different, unique, newly created empty lists. (Note that e = f = [] assigns the same object to both e and f .)

3.2. The standard type hierarchy ¶

Below is a list of the types that are built into Python. Extension modules (written in C, Java, or other languages, depending on the implementation) can define additional types. Future versions of Python may add types to the type hierarchy (e.g., rational numbers, efficiently stored arrays of integers, etc.), although such additions will often be provided via the standard library instead.

Some of the type descriptions below contain a paragraph listing ‘special attributes.’ These are attributes that provide access to the implementation and are not intended for general use. Their definition may change in the future.

3.2.1. None ¶

This type has a single value. There is a single object with this value. This object is accessed through the built-in name None . It is used to signify the absence of a value in many situations, e.g., it is returned from functions that don’t explicitly return anything. Its truth value is false.

3.2.2. NotImplemented ¶

This type has a single value. There is a single object with this value. This object is accessed through the built-in name NotImplemented . Numeric methods and rich comparison methods should return this value if they do not implement the operation for the operands provided. (The interpreter will then try the reflected operation, or some other fallback, depending on the operator.) It should not be evaluated in a boolean context.

See Implementing the arithmetic operations for more details.

Changed in version 3.9: Evaluating NotImplemented in a boolean context is deprecated. While it currently evaluates as true, it will emit a DeprecationWarning . It will raise a TypeError in a future version of Python.

3.2.3. Ellipsis ¶

This type has a single value. There is a single object with this value. This object is accessed through the literal ... or the built-in name Ellipsis . Its truth value is true.

3.2.4. numbers.Number ¶

These are created by numeric literals and returned as results by arithmetic operators and arithmetic built-in functions. Numeric objects are immutable; once created their value never changes. Python numbers are of course strongly related to mathematical numbers, but subject to the limitations of numerical representation in computers.

The string representations of the numeric classes, computed by __repr__() and __str__() , have the following properties:

They are valid numeric literals which, when passed to their class constructor, produce an object having the value of the original numeric.

The representation is in base 10, when possible.

Leading zeros, possibly excepting a single zero before a decimal point, are not shown.

Trailing zeros, possibly excepting a single zero after a decimal point, are not shown.

A sign is shown only when the number is negative.

Python distinguishes between integers, floating-point numbers, and complex numbers:

3.2.4.1. numbers.Integral ¶

These represent elements from the mathematical set of integers (positive and negative).

The rules for integer representation are intended to give the most meaningful interpretation of shift and mask operations involving negative integers.

There are two types of integers:

These represent numbers in an unlimited range, subject to available (virtual) memory only. For the purpose of shift and mask operations, a binary representation is assumed, and negative numbers are represented in a variant of 2’s complement which gives the illusion of an infinite string of sign bits extending to the left.

These represent the truth values False and True. The two objects representing the values False and True are the only Boolean objects. The Boolean type is a subtype of the integer type, and Boolean values behave like the values 0 and 1, respectively, in almost all contexts, the exception being that when converted to a string, the strings "False" or "True" are returned, respectively.

3.2.4.2. numbers.Real ( float ) ¶

These represent machine-level double precision floating-point numbers. You are at the mercy of the underlying machine architecture (and C or Java implementation) for the accepted range and handling of overflow. Python does not support single-precision floating-point numbers; the savings in processor and memory usage that are usually the reason for using these are dwarfed by the overhead of using objects in Python, so there is no reason to complicate the language with two kinds of floating-point numbers.

3.2.4.3. numbers.Complex ( complex ) ¶

These represent complex numbers as a pair of machine-level double precision floating-point numbers. The same caveats apply as for floating-point numbers. The real and imaginary parts of a complex number z can be retrieved through the read-only attributes z.real and z.imag .

3.2.5. Sequences ¶

These represent finite ordered sets indexed by non-negative numbers. The built-in function len() returns the number of items of a sequence. When the length of a sequence is n , the index set contains the numbers 0, 1, …, n -1. Item i of sequence a is selected by a[i] . Some sequences, including built-in sequences, interpret negative subscripts by adding the sequence length. For example, a[-2] equals a[n-2] , the second to last item of sequence a with length n .

Sequences also support slicing: a[i:j] selects all items with index k such that i <= k < j . When used as an expression, a slice is a sequence of the same type. The comment above about negative indexes also applies to negative slice positions.

Some sequences also support “extended slicing” with a third “step” parameter: a[i:j:k] selects all items of a with index x where x = i + n*k , n >= 0 and i <= x < j .

Sequences are distinguished according to their mutability:

3.2.5.1. Immutable sequences ¶

An object of an immutable sequence type cannot change once it is created. (If the object contains references to other objects, these other objects may be mutable and may be changed; however, the collection of objects directly referenced by an immutable object cannot change.)

The following types are immutable sequences:

A string is a sequence of values that represent Unicode code points. All the code points in the range U+0000 - U+10FFFF can be represented in a string. Python doesn’t have a char type; instead, every code point in the string is represented as a string object with length 1 . The built-in function ord() converts a code point from its string form to an integer in the range 0 - 10FFFF ; chr() converts an integer in the range 0 - 10FFFF to the corresponding length 1 string object. str.encode() can be used to convert a str to bytes using the given text encoding, and bytes.decode() can be used to achieve the opposite.

The items of a tuple are arbitrary Python objects. Tuples of two or more items are formed by comma-separated lists of expressions. A tuple of one item (a ‘singleton’) can be formed by affixing a comma to an expression (an expression by itself does not create a tuple, since parentheses must be usable for grouping of expressions). An empty tuple can be formed by an empty pair of parentheses.

A bytes object is an immutable array. The items are 8-bit bytes, represented by integers in the range 0 <= x < 256. Bytes literals (like b'abc' ) and the built-in bytes() constructor can be used to create bytes objects. Also, bytes objects can be decoded to strings via the decode() method.

3.2.5.2. Mutable sequences ¶

Mutable sequences can be changed after they are created. The subscription and slicing notations can be used as the target of assignment and del (delete) statements.

The collections and array module provide additional examples of mutable sequence types.

There are currently two intrinsic mutable sequence types:

The items of a list are arbitrary Python objects. Lists are formed by placing a comma-separated list of expressions in square brackets. (Note that there are no special cases needed to form lists of length 0 or 1.)

A bytearray object is a mutable array. They are created by the built-in bytearray() constructor. Aside from being mutable (and hence unhashable), byte arrays otherwise provide the same interface and functionality as immutable bytes objects.

3.2.6. Set types ¶

These represent unordered, finite sets of unique, immutable objects. As such, they cannot be indexed by any subscript. However, they can be iterated over, and the built-in function len() returns the number of items in a set. Common uses for sets are fast membership testing, removing duplicates from a sequence, and computing mathematical operations such as intersection, union, difference, and symmetric difference.

For set elements, the same immutability rules apply as for dictionary keys. Note that numeric types obey the normal rules for numeric comparison: if two numbers compare equal (e.g., 1 and 1.0 ), only one of them can be contained in a set.

There are currently two intrinsic set types:

These represent a mutable set. They are created by the built-in set() constructor and can be modified afterwards by several methods, such as add() .

These represent an immutable set. They are created by the built-in frozenset() constructor. As a frozenset is immutable and hashable , it can be used again as an element of another set, or as a dictionary key.

3.2.7. Mappings ¶

These represent finite sets of objects indexed by arbitrary index sets. The subscript notation a[k] selects the item indexed by k from the mapping a ; this can be used in expressions and as the target of assignments or del statements. The built-in function len() returns the number of items in a mapping.

There is currently a single intrinsic mapping type:

3.2.7.1. Dictionaries ¶

These represent finite sets of objects indexed by nearly arbitrary values. The only types of values not acceptable as keys are values containing lists or dictionaries or other mutable types that are compared by value rather than by object identity, the reason being that the efficient implementation of dictionaries requires a key’s hash value to remain constant. Numeric types used for keys obey the normal rules for numeric comparison: if two numbers compare equal (e.g., 1 and 1.0 ) then they can be used interchangeably to index the same dictionary entry.

Dictionaries preserve insertion order, meaning that keys will be produced in the same order they were added sequentially over the dictionary. Replacing an existing key does not change the order, however removing a key and re-inserting it will add it to the end instead of keeping its old place.

Dictionaries are mutable; they can be created by the {...} notation (see section Dictionary displays ).

The extension modules dbm.ndbm and dbm.gnu provide additional examples of mapping types, as does the collections module.

Changed in version 3.7: Dictionaries did not preserve insertion order in versions of Python before 3.6. In CPython 3.6, insertion order was preserved, but it was considered an implementation detail at that time rather than a language guarantee.

3.2.8. Callable types ¶

These are the types to which the function call operation (see section Calls ) can be applied:

3.2.8.1. User-defined functions ¶

A user-defined function object is created by a function definition (see section Function definitions ). It should be called with an argument list containing the same number of items as the function’s formal parameter list.

3.2.8.1.1. Special read-only attributes ¶

Attribute

Meaning

__globals__

A reference to the that holds the function’s – the global namespace of the module in which the function was defined.

__closure__

or a of cells that contain bindings for the function’s free variables.

A cell object has the attribute . This can be used to get the value of the cell, as well as set the value.

3.2.8.1.2. Special writable attributes ¶

Most of these attributes check the type of the assigned value:

Attribute

Meaning

__doc__

The function’s documentation string, or if unavailable. Not inherited by subclasses.

__name__

The function’s name. See also: attributes.

__qualname__

The function’s . See also: attributes.

__module__

The name of the module the function was defined in, or if unavailable.

__defaults__

A containing default values for those parameters that have defaults, or if no parameters have a default value.

__code__

The representing the compiled function body.

__dict__

The namespace supporting arbitrary function attributes. See also: attributes.

__annotations__

A containing annotations of . The keys of the dictionary are the parameter names, and for the return annotation, if provided. See also: .

__kwdefaults__

A containing defaults for keyword-only .

__type_params__

A containing the of a .

Function objects also support getting and setting arbitrary attributes, which can be used, for example, to attach metadata to functions. Regular attribute dot-notation is used to get and set such attributes.

CPython implementation detail: CPython’s current implementation only supports function attributes on user-defined functions. Function attributes on built-in functions may be supported in the future.

Additional information about a function’s definition can be retrieved from its code object (accessible via the __code__ attribute).

3.2.8.2. Instance methods ¶

An instance method object combines a class, a class instance and any callable object (normally a user-defined function).

Special read-only attributes:

__self__

Refers to the class instance object to which the method is

__func__

Refers to the original

__doc__

The method’s documentation (same as ). A if the original function had a docstring, else .

__name__

The name of the method (same as )

__module__

The name of the module the method was defined in, or if unavailable.

Methods also support accessing (but not setting) the arbitrary function attributes on the underlying function object .

User-defined method objects may be created when getting an attribute of a class (perhaps via an instance of that class), if that attribute is a user-defined function object or a classmethod object.

When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound . The new method’s __func__ attribute is the original function object.

When an instance method object is created by retrieving a classmethod object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method.

When an instance method object is called, the underlying function ( __func__ ) is called, inserting the class instance ( __self__ ) in front of the argument list. For instance, when C is a class which contains a definition for a function f() , and x is an instance of C , calling x.f(1) is equivalent to calling C.f(x, 1) .

When an instance method object is derived from a classmethod object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function.

It is important to note that user-defined functions which are attributes of a class instance are not converted to bound methods; this only happens when the function is an attribute of the class.

3.2.8.3. Generator functions ¶

A function or method which uses the yield statement (see section The yield statement ) is called a generator function . Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterator’s iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned.

3.2.8.4. Coroutine functions ¶

A function or method which is defined using async def is called a coroutine function . Such a function, when called, returns a coroutine object. It may contain await expressions, as well as async with and async for statements. See also the Coroutine Objects section.

3.2.8.5. Asynchronous generator functions ¶

A function or method which is defined using async def and which uses the yield statement is called a asynchronous generator function . Such a function, when called, returns an asynchronous iterator object which can be used in an async for statement to execute the body of the function.

Calling the asynchronous iterator’s aiterator.__anext__ method will return an awaitable which when awaited will execute until it provides a value using the yield expression. When the function executes an empty return statement or falls off the end, a StopAsyncIteration exception is raised and the asynchronous iterator will have reached the end of the set of values to be yielded.

3.2.8.6. Built-in functions ¶

A built-in function object is a wrapper around a C function. Examples of built-in functions are len() and math.sin() ( math is a standard built-in module). The number and type of the arguments are determined by the C function. Special read-only attributes:

__doc__ is the function’s documentation string, or None if unavailable. See function.__doc__ .

__name__ is the function’s name. See function.__name__ .

__self__ is set to None (but see the next item).

__module__ is the name of the module the function was defined in or None if unavailable. See function.__module__ .

3.2.8.7. Built-in methods ¶

This is really a different disguise of a built-in function, this time containing an object passed to the C function as an implicit extra argument. An example of a built-in method is alist.append() , assuming alist is a list object. In this case, the special read-only attribute __self__ is set to the object denoted by alist . (The attribute has the same semantics as it does with other instance methods .)

3.2.8.8. Classes ¶

Classes are callable. These objects normally act as factories for new instances of themselves, but variations are possible for class types that override __new__() . The arguments of the call are passed to __new__() and, in the typical case, to __init__() to initialize the new instance.

3.2.8.9. Class Instances ¶

Instances of arbitrary classes can be made callable by defining a __call__() method in their class.

3.2.9. Modules ¶

Modules are a basic organizational unit of Python code, and are created by the import system as invoked either by the import statement, or by calling functions such as importlib.import_module() and built-in __import__() . A module object has a namespace implemented by a dictionary object (this is the dictionary referenced by the __globals__ attribute of functions defined in the module). Attribute references are translated to lookups in this dictionary, e.g., m.x is equivalent to m.__dict__["x"] . A module object does not contain the code object used to initialize the module (since it isn’t needed once the initialization is done).

Attribute assignment updates the module’s namespace dictionary, e.g., m.x = 1 is equivalent to m.__dict__["x"] = 1 .

Predefined (writable) attributes:

__name__ The module’s name. __doc__ The module’s documentation string, or None if unavailable. __file__ The pathname of the file from which the module was loaded, if it was loaded from a file. The __file__ attribute may be missing for certain types of modules, such as C modules that are statically linked into the interpreter. For extension modules loaded dynamically from a shared library, it’s the pathname of the shared library file. __annotations__ A dictionary containing variable annotations collected during module body execution. For best practices on working with __annotations__ , please see Annotations Best Practices .

Special read-only attribute: __dict__ is the module’s namespace as a dictionary object.

CPython implementation detail: Because of the way CPython clears module dictionaries, the module dictionary will be cleared when the module falls out of scope even if the dictionary still has live references. To avoid this, copy the dictionary or keep the module around while using its dictionary directly.

3.2.10. Custom classes ¶

Custom class types are typically created by class definitions (see section Class definitions ). A class has a namespace implemented by a dictionary object. Class attribute references are translated to lookups in this dictionary, e.g., C.x is translated to C.__dict__["x"] (although there are a number of hooks which allow for other means of locating attributes). When the attribute name is not found there, the attribute search continues in the base classes. This search of the base classes uses the C3 method resolution order which behaves correctly even in the presence of ‘diamond’ inheritance structures where there are multiple inheritance paths leading back to a common ancestor. Additional details on the C3 MRO used by Python can be found at The Python 2.3 Method Resolution Order .

When a class attribute reference (for class C , say) would yield a class method object, it is transformed into an instance method object whose __self__ attribute is C . When it would yield a staticmethod object, it is transformed into the object wrapped by the static method object. See section Implementing Descriptors for another way in which attributes retrieved from a class may differ from those actually contained in its __dict__ .

Class attribute assignments update the class’s dictionary, never the dictionary of a base class.

A class object can be called (see above) to yield a class instance (see below).

Special attributes:

__name__ The class name. __module__ The name of the module in which the class was defined. __dict__ The dictionary containing the class’s namespace. __bases__ A tuple containing the base classes, in the order of their occurrence in the base class list. __doc__ The class’s documentation string, or None if undefined. __annotations__ A dictionary containing variable annotations collected during class body execution. For best practices on working with __annotations__ , please see Annotations Best Practices . __type_params__ A tuple containing the type parameters of a generic class .

3.2.11. Class instances ¶

A class instance is created by calling a class object (see above). A class instance has a namespace implemented as a dictionary which is the first place in which attribute references are searched. When an attribute is not found there, and the instance’s class has an attribute by that name, the search continues with the class attributes. If a class attribute is found that is a user-defined function object, it is transformed into an instance method object whose __self__ attribute is the instance. Static method and class method objects are also transformed; see above under “Classes”. See section Implementing Descriptors for another way in which attributes of a class retrieved via its instances may differ from the objects actually stored in the class’s __dict__ . If no class attribute is found, and the object’s class has a __getattr__() method, that is called to satisfy the lookup.

Attribute assignments and deletions update the instance’s dictionary, never a class’s dictionary. If the class has a __setattr__() or __delattr__() method, this is called instead of updating the instance dictionary directly.

Class instances can pretend to be numbers, sequences, or mappings if they have methods with certain special names. See section Special method names .

Special attributes: __dict__ is the attribute dictionary; __class__ is the instance’s class.

3.2.12. I/O objects (also known as file objects) ¶

A file object represents an open file. Various shortcuts are available to create file objects: the open() built-in function, and also os.popen() , os.fdopen() , and the makefile() method of socket objects (and perhaps by other functions or methods provided by extension modules).

The objects sys.stdin , sys.stdout and sys.stderr are initialized to file objects corresponding to the interpreter’s standard input, output and error streams; they are all open in text mode and therefore follow the interface defined by the io.TextIOBase abstract class.

3.2.13. Internal types ¶

A few types used internally by the interpreter are exposed to the user. Their definitions may change with future versions of the interpreter, but they are mentioned here for completeness.

3.2.13.1. Code objects ¶

Code objects represent byte-compiled executable Python code, or bytecode . The difference between a code object and a function object is that the function object contains an explicit reference to the function’s globals (the module in which it was defined), while a code object contains no context; also the default argument values are stored in the function object, not in the code object (because they represent values calculated at run-time). Unlike function objects, code objects are immutable and contain no references (directly or indirectly) to mutable objects.

3.2.13.1.1. Special read-only attributes ¶

co_name

The function name

co_qualname

The fully qualified function name

co_argcount

The total number of positional (including positional-only parameters and parameters with default values) that the function has

co_posonlyargcount

The number of positional-only (including arguments with default values) that the function has

co_kwonlyargcount

The number of keyword-only (including arguments with default values) that the function has

co_nlocals

The number of used by the function (including parameters)

co_varnames

A containing the names of the local variables in the function (starting with the parameter names)

co_cellvars

A containing the names of that are referenced by nested functions inside the function

co_freevars

A containing the names of free variables in the function

co_code

A string representing the sequence of instructions in the function

co_consts

A containing the literals used by the in the function

co_names

A containing the names used by the in the function

co_filename

The name of the file from which the code was compiled

co_firstlineno

The line number of the first line of the function

co_lnotab

A string encoding the mapping from offsets to line numbers. For details, see the source code of the interpreter.

This attribute of code objects is deprecated, and may be removed in Python 3.14.

co_stacksize

The required stack size of the code object

co_flags

An encoding a number of flags for the interpreter.

The following flag bits are defined for co_flags : bit 0x04 is set if the function uses the *arguments syntax to accept an arbitrary number of positional arguments; bit 0x08 is set if the function uses the **keywords syntax to accept arbitrary keyword arguments; bit 0x20 is set if the function is a generator. See Code Objects Bit Flags for details on the semantics of each flags that might be present.

Future feature declarations ( from __future__ import division ) also use bits in co_flags to indicate whether a code object was compiled with a particular feature enabled: bit 0x2000 is set if the function was compiled with future division enabled; bits 0x10 and 0x1000 were used in earlier versions of Python.

Other bits in co_flags are reserved for internal use.

If a code object represents a function, the first item in co_consts is the documentation string of the function, or None if undefined.

3.2.13.1.2. Methods on code objects ¶

Returns an iterable over the source code positions of each bytecode instruction in the code object.

The iterator returns tuple s containing the (start_line, end_line, start_column, end_column) . The i-th tuple corresponds to the position of the source code that compiled to the i-th code unit. Column information is 0-indexed utf-8 byte offsets on the given source line.

This positional information can be missing. A non-exhaustive lists of cases where this may happen:

Running the interpreter with -X no_debug_ranges .

Loading a pyc file compiled while using -X no_debug_ranges .

Position tuples corresponding to artificial instructions.

Line and column numbers that can’t be represented due to implementation specific limitations.

When this occurs, some or all of the tuple elements can be None .

Added in version 3.11.

This feature requires storing column positions in code objects which may result in a small increase of disk usage of compiled Python files or interpreter memory usage. To avoid storing the extra information and/or deactivate printing the extra traceback information, the -X no_debug_ranges command line flag or the PYTHONNODEBUGRANGES environment variable can be used.

Returns an iterator that yields information about successive ranges of bytecode s. Each item yielded is a (start, end, lineno) tuple :

start (an int ) represents the offset (inclusive) of the start of the bytecode range

end (an int ) represents the offset (exclusive) of the end of the bytecode range

lineno is an int representing the line number of the bytecode range, or None if the bytecodes in the given range have no line number

The items yielded will have the following properties:

The first range yielded will have a start of 0.

The (start, end) ranges will be non-decreasing and consecutive. That is, for any pair of tuple s, the start of the second will be equal to the end of the first.

No range will be backwards: end >= start for all triples.

The last tuple yielded will have end equal to the size of the bytecode .

Zero-width ranges, where start == end , are allowed. Zero-width ranges are used for lines that are present in the source code, but have been eliminated by the bytecode compiler.

Added in version 3.10.

The PEP that introduced the co_lines() method.

Return a copy of the code object with new values for the specified fields.

Added in version 3.8.

3.2.13.2. Frame objects ¶

Frame objects represent execution frames. They may occur in traceback objects , and are also passed to registered trace functions.

3.2.13.2.1. Special read-only attributes ¶

f_back

Points to the previous stack frame (towards the caller), or if this is the bottom stack frame

f_code

The being executed in this frame. Accessing this attribute raises an with arguments and .

f_locals

The dictionary used by the frame to look up

f_globals

The dictionary used by the frame to look up

f_builtins

The dictionary used by the frame to look up

f_lasti

The “precise instruction” of the frame object (this is an index into the string of the )

3.2.13.2.2. Special writable attributes ¶

f_trace

If not , this is a function called for various events during code execution (this is used by debuggers). Normally an event is triggered for each new source line (see ).

f_trace_lines

Set this attribute to to disable triggering a tracing event for each source line.

f_trace_opcodes

Set this attribute to to allow per-opcode events to be requested. Note that this may lead to undefined interpreter behaviour if exceptions raised by the trace function escape to the function being traced.

f_lineno

The current line number of the frame – writing to this from within a trace function jumps to the given line (only for the bottom-most frame). A debugger can implement a Jump command (aka Set Next Statement) by writing to this attribute.

3.2.13.2.3. Frame object methods ¶

Frame objects support one method:

This method clears all references to local variables held by the frame. Also, if the frame belonged to a generator , the generator is finalized. This helps break reference cycles involving frame objects (for example when catching an exception and storing its traceback for later use).

RuntimeError is raised if the frame is currently executing.

Added in version 3.4.

3.2.13.3. Traceback objects ¶

Traceback objects represent the stack trace of an exception . A traceback object is implicitly created when an exception occurs, and may also be explicitly created by calling types.TracebackType .

Changed in version 3.7: Traceback objects can now be explicitly instantiated from Python code.

For implicitly created tracebacks, when the search for an exception handler unwinds the execution stack, at each unwound level a traceback object is inserted in front of the current traceback. When an exception handler is entered, the stack trace is made available to the program. (See section The try statement .) It is accessible as the third item of the tuple returned by sys.exc_info() , and as the __traceback__ attribute of the caught exception.

When the program contains no suitable handler, the stack trace is written (nicely formatted) to the standard error stream; if the interpreter is interactive, it is also made available to the user as sys.last_traceback .

For explicitly created tracebacks, it is up to the creator of the traceback to determine how the tb_next attributes should be linked to form a full stack trace.

tb_frame

Points to the execution of the current level.

Accessing this attribute raises an with arguments and .

tb_lineno

Gives the line number where the exception occurred

tb_lasti

Indicates the “precise instruction”.

The line number and last instruction in the traceback may differ from the line number of its frame object if the exception occurred in a try statement with no matching except clause or with a finally clause.

The special writable attribute tb_next is the next level in the stack trace (towards the frame where the exception occurred), or None if there is no next level.

Changed in version 3.7: This attribute is now writable

3.2.13.4. Slice objects ¶

Slice objects are used to represent slices for __getitem__() methods. They are also created by the built-in slice() function.

Special read-only attributes: start is the lower bound; stop is the upper bound; step is the step value; each is None if omitted. These attributes can have any type.

Slice objects support one method:

This method takes a single integer argument length and computes information about the slice that the slice object would describe if applied to a sequence of length items. It returns a tuple of three integers; respectively these are the start and stop indices and the step or stride length of the slice. Missing or out-of-bounds indices are handled in a manner consistent with regular slices.

3.2.13.5. Static method objects ¶

Static method objects provide a way of defeating the transformation of function objects to method objects described above. A static method object is a wrapper around any other object, usually a user-defined method object. When a static method object is retrieved from a class or a class instance, the object actually returned is the wrapped object, which is not subject to any further transformation. Static method objects are also callable. Static method objects are created by the built-in staticmethod() constructor.

3.2.13.6. Class method objects ¶

A class method object, like a static method object, is a wrapper around another object that alters the way in which that object is retrieved from classes and class instances. The behaviour of class method objects upon such retrieval is described above, under “instance methods” . Class method objects are created by the built-in classmethod() constructor.

3.3. Special method names ¶

A class can implement certain operations that are invoked by special syntax (such as arithmetic operations or subscripting and slicing) by defining methods with special names. This is Python’s approach to operator overloading , allowing classes to define their own behavior with respect to language operators. For instance, if a class defines a method named __getitem__() , and x is an instance of this class, then x[i] is roughly equivalent to type(x).__getitem__(x, i) . Except where mentioned, attempts to execute an operation raise an exception when no appropriate method is defined (typically AttributeError or TypeError ).

Setting a special method to None indicates that the corresponding operation is not available. For example, if a class sets __iter__() to None , the class is not iterable, so calling iter() on its instances will raise a TypeError (without falling back to __getitem__() ). [ 2 ]

When implementing a class that emulates any built-in type, it is important that the emulation only be implemented to the degree that it makes sense for the object being modelled. For example, some sequences may work well with retrieval of individual elements, but extracting a slice may not make sense. (One example of this is the NodeList interface in the W3C’s Document Object Model.)

3.3.1. Basic customization ¶

Called to create a new instance of class cls . __new__() is a static method (special-cased so you need not declare it as such) that takes the class of which an instance was requested as its first argument. The remaining arguments are those passed to the object constructor expression (the call to the class). The return value of __new__() should be the new object instance (usually an instance of cls ).

Typical implementations create a new instance of the class by invoking the superclass’s __new__() method using super().__new__(cls[, ...]) with appropriate arguments and then modifying the newly created instance as necessary before returning it.

If __new__() is invoked during object construction and it returns an instance of cls , then the new instance’s __init__() method will be invoked like __init__(self[, ...]) , where self is the new instance and the remaining arguments are the same as were passed to the object constructor.

If __new__() does not return an instance of cls , then the new instance’s __init__() method will not be invoked.

__new__() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.

Called after the instance has been created (by __new__() ), but before it is returned to the caller. The arguments are those passed to the class constructor expression. If a base class has an __init__() method, the derived class’s __init__() method, if any, must explicitly call it to ensure proper initialization of the base class part of the instance; for example: super().__init__([args...]) .

Because __new__() and __init__() work together in constructing objects ( __new__() to create it, and __init__() to customize it), no non- None value may be returned by __init__() ; doing so will cause a TypeError to be raised at runtime.

Called when the instance is about to be destroyed. This is also called a finalizer or (improperly) a destructor. If a base class has a __del__() method, the derived class’s __del__() method, if any, must explicitly call it to ensure proper deletion of the base class part of the instance.

It is possible (though not recommended!) for the __del__() method to postpone destruction of the instance by creating a new reference to it. This is called object resurrection . It is implementation-dependent whether __del__() is called a second time when a resurrected object is about to be destroyed; the current CPython implementation only calls it once.

It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits. weakref.finalize provides a straightforward way to register a cleanup function to be called when an object is garbage collected.

del x doesn’t directly call x.__del__() — the former decrements the reference count for x by one, and the latter is only called when x ’s reference count reaches zero.

CPython implementation detail: It is possible for a reference cycle to prevent the reference count of an object from going to zero. In this case, the cycle will be later detected and deleted by the cyclic garbage collector . A common cause of reference cycles is when an exception has been caught in a local variable. The frame’s locals then reference the exception, which references its own traceback, which references the locals of all frames caught in the traceback.

Documentation for the gc module.

Due to the precarious circumstances under which __del__() methods are invoked, exceptions that occur during their execution are ignored, and a warning is printed to sys.stderr instead. In particular:

__del__() can be invoked when arbitrary code is being executed, including from any arbitrary thread. If __del__() needs to take a lock or invoke any other blocking resource, it may deadlock as the resource may already be taken by the code that gets interrupted to execute __del__() .

__del__() can be executed during interpreter shutdown. As a consequence, the global variables it needs to access (including other modules) may already have been deleted or set to None . Python guarantees that globals whose name begins with a single underscore are deleted from their module before other globals are deleted; if no other references to such globals exist, this may help in assuring that imported modules are still available at the time when the __del__() method is called.

Called by the repr() built-in function to compute the “official” string representation of an object. If at all possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment). If this is not possible, a string of the form <...some useful description...> should be returned. The return value must be a string object. If a class defines __repr__() but not __str__() , then __repr__() is also used when an “informal” string representation of instances of that class is required.

This is typically used for debugging, so it is important that the representation is information-rich and unambiguous.

Called by str(object) and the built-in functions format() and print() to compute the “informal” or nicely printable string representation of an object. The return value must be a string object.

This method differs from object.__repr__() in that there is no expectation that __str__() return a valid Python expression: a more convenient or concise representation can be used.

The default implementation defined by the built-in type object calls object.__repr__() .

Called by bytes to compute a byte-string representation of an object. This should return a bytes object.

Called by the format() built-in function, and by extension, evaluation of formatted string literals and the str.format() method, to produce a “formatted” string representation of an object. The format_spec argument is a string that contains a description of the formatting options desired. The interpretation of the format_spec argument is up to the type implementing __format__() , however most classes will either delegate formatting to one of the built-in types, or use a similar formatting option syntax.

See Format Specification Mini-Language for a description of the standard formatting syntax.

The return value must be a string object.

Changed in version 3.4: The __format__ method of object itself raises a TypeError if passed any non-empty string.

Changed in version 3.7: object.__format__(x, '') is now equivalent to str(x) rather than format(str(x), '') .

These are the so-called “rich comparison” methods. The correspondence between operator symbols and method names is as follows: x<y calls x.__lt__(y) , x<=y calls x.__le__(y) , x==y calls x.__eq__(y) , x!=y calls x.__ne__(y) , x>y calls x.__gt__(y) , and x>=y calls x.__ge__(y) .

A rich comparison method may return the singleton NotImplemented if it does not implement the operation for a given pair of arguments. By convention, False and True are returned for a successful comparison. However, these methods can return any value, so if the comparison operator is used in a Boolean context (e.g., in the condition of an if statement), Python will call bool() on the value to determine if the result is true or false.

By default, object implements __eq__() by using is , returning NotImplemented in the case of a false comparison: True if x is y else NotImplemented . For __ne__() , by default it delegates to __eq__() and inverts the result unless it is NotImplemented . There are no other implied relationships among the comparison operators or default implementations; for example, the truth of (x<y or x==y) does not imply x<=y . To automatically generate ordering operations from a single root operation, see functools.total_ordering() .

See the paragraph on __hash__() for some important notes on creating hashable objects which support custom comparison operations and are usable as dictionary keys.

There are no swapped-argument versions of these methods (to be used when the left argument does not support the operation but the right argument does); rather, __lt__() and __gt__() are each other’s reflection, __le__() and __ge__() are each other’s reflection, and __eq__() and __ne__() are their own reflection. If the operands are of different types, and the right operand’s type is a direct or indirect subclass of the left operand’s type, the reflected method of the right operand has priority, otherwise the left operand’s method has priority. Virtual subclassing is not considered.

When no appropriate method returns any value other than NotImplemented , the == and != operators will fall back to is and is not , respectively.

Called by built-in function hash() and for operations on members of hashed collections including set , frozenset , and dict . The __hash__() method should return an integer. The only required property is that objects which compare equal have the same hash value; it is advised to mix together the hash values of the components of the object that also play a part in comparison of objects by packing them into a tuple and hashing the tuple. Example:

hash() truncates the value returned from an object’s custom __hash__() method to the size of a Py_ssize_t . This is typically 8 bytes on 64-bit builds and 4 bytes on 32-bit builds. If an object’s __hash__() must interoperate on builds of different bit sizes, be sure to check the width on all supported builds. An easy way to do this is with python -c "import sys; print(sys.hash_info.width)" .

If a class does not define an __eq__() method it should not define a __hash__() operation either; if it defines __eq__() but not __hash__() , its instances will not be usable as items in hashable collections. If a class defines mutable objects and implements an __eq__() method, it should not implement __hash__() , since the implementation of hashable collections requires that a key’s hash value is immutable (if the object’s hash value changes, it will be in the wrong hash bucket).

User-defined classes have __eq__() and __hash__() methods by default; with them, all objects compare unequal (except with themselves) and x.__hash__() returns an appropriate value such that x == y implies both that x is y and hash(x) == hash(y) .

A class that overrides __eq__() and does not define __hash__() will have its __hash__() implicitly set to None . When the __hash__() method of a class is None , instances of the class will raise an appropriate TypeError when a program attempts to retrieve their hash value, and will also be correctly identified as unhashable when checking isinstance(obj, collections.abc.Hashable) .

If a class that overrides __eq__() needs to retain the implementation of __hash__() from a parent class, the interpreter must be told this explicitly by setting __hash__ = <ParentClass>.__hash__ .

If a class that does not override __eq__() wishes to suppress hash support, it should include __hash__ = None in the class definition. A class which defines its own __hash__() that explicitly raises a TypeError would be incorrectly identified as hashable by an isinstance(obj, collections.abc.Hashable) call.

By default, the __hash__() values of str and bytes objects are “salted” with an unpredictable random value. Although they remain constant within an individual Python process, they are not predictable between repeated invocations of Python.

This is intended to provide protection against a denial-of-service caused by carefully chosen inputs that exploit the worst case performance of a dict insertion, O ( n 2 ) complexity. See http://ocert.org/advisories/ocert-2011-003.html for details.

Changing hash values affects the iteration order of sets. Python has never made guarantees about this ordering (and it typically varies between 32-bit and 64-bit builds).

See also PYTHONHASHSEED .

Changed in version 3.3: Hash randomization is enabled by default.

Called to implement truth value testing and the built-in operation bool() ; should return False or True . When this method is not defined, __len__() is called, if it is defined, and the object is considered true if its result is nonzero. If a class defines neither __len__() nor __bool__() , all its instances are considered true.

3.3.2. Customizing attribute access ¶

The following methods can be defined to customize the meaning of attribute access (use of, assignment to, or deletion of x.name ) for class instances.

Called when the default attribute access fails with an AttributeError (either __getattribute__() raises an AttributeError because name is not an instance attribute or an attribute in the class tree for self ; or __get__() of a name property raises AttributeError ). This method should either return the (computed) attribute value or raise an AttributeError exception.

Note that if the attribute is found through the normal mechanism, __getattr__() is not called. (This is an intentional asymmetry between __getattr__() and __setattr__() .) This is done both for efficiency reasons and because otherwise __getattr__() would have no way to access other attributes of the instance. Note that at least for instance variables, you can fake total control by not inserting any values in the instance attribute dictionary (but instead inserting them in another object). See the __getattribute__() method below for a way to actually get total control over attribute access.

Called unconditionally to implement attribute accesses for instances of the class. If the class also defines __getattr__() , the latter will not be called unless __getattribute__() either calls it explicitly or raises an AttributeError . This method should return the (computed) attribute value or raise an AttributeError exception. In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, object.__getattribute__(self, name) .

This method may still be bypassed when looking up special methods as the result of implicit invocation via language syntax or built-in functions . See Special method lookup .

For certain sensitive attribute accesses, raises an auditing event object.__getattr__ with arguments obj and name .

Called when an attribute assignment is attempted. This is called instead of the normal mechanism (i.e. store the value in the instance dictionary). name is the attribute name, value is the value to be assigned to it.

If __setattr__() wants to assign to an instance attribute, it should call the base class method with the same name, for example, object.__setattr__(self, name, value) .

For certain sensitive attribute assignments, raises an auditing event object.__setattr__ with arguments obj , name , value .

Like __setattr__() but for attribute deletion instead of assignment. This should only be implemented if del obj.name is meaningful for the object.

For certain sensitive attribute deletions, raises an auditing event object.__delattr__ with arguments obj and name .

Called when dir() is called on the object. An iterable must be returned. dir() converts the returned iterable to a list and sorts it.

3.3.2.1. Customizing module attribute access ¶

Special names __getattr__ and __dir__ can be also used to customize access to module attributes. The __getattr__ function at the module level should accept one argument which is the name of an attribute and return the computed value or raise an AttributeError . If an attribute is not found on a module object through the normal lookup, i.e. object.__getattribute__() , then __getattr__ is searched in the module __dict__ before raising an AttributeError . If found, it is called with the attribute name and the result is returned.

The __dir__ function should accept no arguments, and return an iterable of strings that represents the names accessible on module. If present, this function overrides the standard dir() search on a module.

For a more fine grained customization of the module behavior (setting attributes, properties, etc.), one can set the __class__ attribute of a module object to a subclass of types.ModuleType . For example:

Defining module __getattr__ and setting module __class__ only affect lookups made using the attribute access syntax – directly accessing the module globals (whether by code within the module, or via a reference to the module’s globals dictionary) is unaffected.

Changed in version 3.5: __class__ module attribute is now writable.

Added in version 3.7: __getattr__ and __dir__ module attributes.

Describes the __getattr__ and __dir__ functions on modules.

3.3.2.2. Implementing Descriptors ¶

The following methods only apply when an instance of the class containing the method (a so-called descriptor class) appears in an owner class (the descriptor must be in either the owner’s class dictionary or in the class dictionary for one of its parents). In the examples below, “the attribute” refers to the attribute whose name is the key of the property in the owner class’ __dict__ .

Called to get the attribute of the owner class (class attribute access) or of an instance of that class (instance attribute access). The optional owner argument is the owner class, while instance is the instance that the attribute was accessed through, or None when the attribute is accessed through the owner .

This method should return the computed attribute value or raise an AttributeError exception.

PEP 252 specifies that __get__() is callable with one or two arguments. Python’s own built-in descriptors support this specification; however, it is likely that some third-party tools have descriptors that require both arguments. Python’s own __getattribute__() implementation always passes in both arguments whether they are required or not.

Called to set the attribute on an instance instance of the owner class to a new value, value .

Note, adding __set__() or __delete__() changes the kind of descriptor to a “data descriptor”. See Invoking Descriptors for more details.

Called to delete the attribute on an instance instance of the owner class.

Instances of descriptors may also have the __objclass__ attribute present:

The attribute __objclass__ is interpreted by the inspect module as specifying the class where this object was defined (setting this appropriately can assist in runtime introspection of dynamic class attributes). For callables, it may indicate that an instance of the given type (or a subclass) is expected or required as the first positional argument (for example, CPython sets this attribute for unbound methods that are implemented in C).

3.3.2.3. Invoking Descriptors ¶

In general, a descriptor is an object attribute with “binding behavior”, one whose attribute access has been overridden by methods in the descriptor protocol: __get__() , __set__() , and __delete__() . If any of those methods are defined for an object, it is said to be a descriptor.

The default behavior for attribute access is to get, set, or delete the attribute from an object’s dictionary. For instance, a.x has a lookup chain starting with a.__dict__['x'] , then type(a).__dict__['x'] , and continuing through the base classes of type(a) excluding metaclasses.

However, if the looked-up value is an object defining one of the descriptor methods, then Python may override the default behavior and invoke the descriptor method instead. Where this occurs in the precedence chain depends on which descriptor methods were defined and how they were called.

The starting point for descriptor invocation is a binding, a.x . How the arguments are assembled depends on a :

The simplest and least common call is when user code directly invokes a descriptor method: x.__get__(a) .

If binding to an object instance, a.x is transformed into the call: type(a).__dict__['x'].__get__(a, type(a)) .

If binding to a class, A.x is transformed into the call: A.__dict__['x'].__get__(None, A) .

A dotted lookup such as super(A, a).x searches a.__class__.__mro__ for a base class B following A and then returns B.__dict__['x'].__get__(a, A) . If not a descriptor, x is returned unchanged.

For instance bindings, the precedence of descriptor invocation depends on which descriptor methods are defined. A descriptor can define any combination of __get__() , __set__() and __delete__() . If it does not define __get__() , then accessing the attribute will return the descriptor object itself unless there is a value in the object’s instance dictionary. If the descriptor defines __set__() and/or __delete__() , it is a data descriptor; if it defines neither, it is a non-data descriptor. Normally, data descriptors define both __get__() and __set__() , while non-data descriptors have just the __get__() method. Data descriptors with __get__() and __set__() (and/or __delete__() ) defined always override a redefinition in an instance dictionary. In contrast, non-data descriptors can be overridden by instances.

Python methods (including those decorated with @staticmethod and @classmethod ) are implemented as non-data descriptors. Accordingly, instances can redefine and override methods. This allows individual instances to acquire behaviors that differ from other instances of the same class.

The property() function is implemented as a data descriptor. Accordingly, instances cannot override the behavior of a property.

3.3.2.4. __slots__ ¶

__slots__ allow us to explicitly declare data members (like properties) and deny the creation of __dict__ and __weakref__ (unless explicitly declared in __slots__ or available in a parent.)

The space saved over using __dict__ can be significant. Attribute lookup speed can be significantly improved as well.

This class variable can be assigned a string, iterable, or sequence of strings with variable names used by instances. __slots__ reserves space for the declared variables and prevents the automatic creation of __dict__ and __weakref__ for each instance.

Notes on using __slots__ :

When inheriting from a class without __slots__ , the __dict__ and __weakref__ attribute of the instances will always be accessible.

Without a __dict__ variable, instances cannot be assigned new variables not listed in the __slots__ definition. Attempts to assign to an unlisted variable name raises AttributeError . If dynamic assignment of new variables is desired, then add '__dict__' to the sequence of strings in the __slots__ declaration.

Without a __weakref__ variable for each instance, classes defining __slots__ do not support weak references to its instances. If weak reference support is needed, then add '__weakref__' to the sequence of strings in the __slots__ declaration.

__slots__ are implemented at the class level by creating descriptors for each variable name. As a result, class attributes cannot be used to set default values for instance variables defined by __slots__ ; otherwise, the class attribute would overwrite the descriptor assignment.

The action of a __slots__ declaration is not limited to the class where it is defined. __slots__ declared in parents are available in child classes. However, child subclasses will get a __dict__ and __weakref__ unless they also define __slots__ (which should only contain names of any additional slots).

If a class defines a slot also defined in a base class, the instance variable defined by the base class slot is inaccessible (except by retrieving its descriptor directly from the base class). This renders the meaning of the program undefined. In the future, a check may be added to prevent this.

TypeError will be raised if nonempty __slots__ are defined for a class derived from a "variable-length" built-in type such as int , bytes , and tuple .

Any non-string iterable may be assigned to __slots__ .

If a dictionary is used to assign __slots__ , the dictionary keys will be used as the slot names. The values of the dictionary can be used to provide per-attribute docstrings that will be recognised by inspect.getdoc() and displayed in the output of help() .

__class__ assignment works only if both classes have the same __slots__ .

Multiple inheritance with multiple slotted parent classes can be used, but only one parent is allowed to have attributes created by slots (the other bases must have empty slot layouts) - violations raise TypeError .

If an iterator is used for __slots__ then a descriptor is created for each of the iterator’s values. However, the __slots__ attribute will be an empty iterator.

3.3.3. Customizing class creation ¶

Whenever a class inherits from another class, __init_subclass__() is called on the parent class. This way, it is possible to write classes which change the behavior of subclasses. This is closely related to class decorators, but where class decorators only affect the specific class they’re applied to, __init_subclass__ solely applies to future subclasses of the class defining the method.

This method is called whenever the containing class is subclassed. cls is then the new subclass. If defined as a normal instance method, this method is implicitly converted to a class method.

Keyword arguments which are given to a new class are passed to the parent class’s __init_subclass__ . For compatibility with other classes using __init_subclass__ , one should take out the needed keyword arguments and pass the others over to the base class, as in:

The default implementation object.__init_subclass__ does nothing, but raises an error if it is called with any arguments.

The metaclass hint metaclass is consumed by the rest of the type machinery, and is never passed to __init_subclass__ implementations. The actual metaclass (rather than the explicit hint) can be accessed as type(cls) .

Added in version 3.6.

When a class is created, type.__new__() scans the class variables and makes callbacks to those with a __set_name__() hook.

Automatically called at the time the owning class owner is created. The object has been assigned to name in that class:

If the class variable is assigned after the class is created, __set_name__() will not be called automatically. If needed, __set_name__() can be called directly:

See Creating the class object for more details.

3.3.3.1. Metaclasses ¶

By default, classes are constructed using type() . The class body is executed in a new namespace and the class name is bound locally to the result of type(name, bases, namespace) .

The class creation process can be customized by passing the metaclass keyword argument in the class definition line, or by inheriting from an existing class that included such an argument. In the following example, both MyClass and MySubclass are instances of Meta :

Any other keyword arguments that are specified in the class definition are passed through to all metaclass operations described below.

When a class definition is executed, the following steps occur:

MRO entries are resolved;

the appropriate metaclass is determined;

the class namespace is prepared;

the class body is executed;

the class object is created.

3.3.3.2. Resolving MRO entries ¶

If a base that appears in a class definition is not an instance of type , then an __mro_entries__() method is searched on the base. If an __mro_entries__() method is found, the base is substituted with the result of a call to __mro_entries__() when creating the class. The method is called with the original bases tuple passed to the bases parameter, and must return a tuple of classes that will be used instead of the base. The returned tuple may be empty: in these cases, the original base is ignored.

Dynamically resolve bases that are not instances of type .

Retrieve a class’s “original bases” prior to modifications by __mro_entries__() .

Core support for typing module and generic types.

3.3.3.3. Determining the appropriate metaclass ¶

The appropriate metaclass for a class definition is determined as follows:

if no bases and no explicit metaclass are given, then type() is used;

if an explicit metaclass is given and it is not an instance of type() , then it is used directly as the metaclass;

if an instance of type() is given as the explicit metaclass, or bases are defined, then the most derived metaclass is used.

The most derived metaclass is selected from the explicitly specified metaclass (if any) and the metaclasses (i.e. type(cls) ) of all specified base classes. The most derived metaclass is one which is a subtype of all of these candidate metaclasses. If none of the candidate metaclasses meets that criterion, then the class definition will fail with TypeError .

3.3.3.4. Preparing the class namespace ¶

Once the appropriate metaclass has been identified, then the class namespace is prepared. If the metaclass has a __prepare__ attribute, it is called as namespace = metaclass.__prepare__(name, bases, **kwds) (where the additional keyword arguments, if any, come from the class definition). The __prepare__ method should be implemented as a classmethod . The namespace returned by __prepare__ is passed in to __new__ , but when the final class object is created the namespace is copied into a new dict .

If the metaclass has no __prepare__ attribute, then the class namespace is initialised as an empty ordered mapping.

Introduced the __prepare__ namespace hook

3.3.3.5. Executing the class body ¶

The class body is executed (approximately) as exec(body, globals(), namespace) . The key difference from a normal call to exec() is that lexical scoping allows the class body (including any methods) to reference names from the current and outer scopes when the class definition occurs inside a function.

However, even when the class definition occurs inside the function, methods defined inside the class still cannot see names defined at the class scope. Class variables must be accessed through the first parameter of instance or class methods, or through the implicit lexically scoped __class__ reference described in the next section.

3.3.3.6. Creating the class object ¶

Once the class namespace has been populated by executing the class body, the class object is created by calling metaclass(name, bases, namespace, **kwds) (the additional keywords passed here are the same as those passed to __prepare__ ).

This class object is the one that will be referenced by the zero-argument form of super() . __class__ is an implicit closure reference created by the compiler if any methods in a class body refer to either __class__ or super . This allows the zero argument form of super() to correctly identify the class being defined based on lexical scoping, while the class or instance that was used to make the current call is identified based on the first argument passed to the method.

CPython implementation detail: In CPython 3.6 and later, the __class__ cell is passed to the metaclass as a __classcell__ entry in the class namespace. If present, this must be propagated up to the type.__new__ call in order for the class to be initialised correctly. Failing to do so will result in a RuntimeError in Python 3.8.

When using the default metaclass type , or any metaclass that ultimately calls type.__new__ , the following additional customization steps are invoked after creating the class object:

The type.__new__ method collects all of the attributes in the class namespace that define a __set_name__() method;

Those __set_name__ methods are called with the class being defined and the assigned name of that particular attribute;

The __init_subclass__() hook is called on the immediate parent of the new class in its method resolution order.

After the class object is created, it is passed to the class decorators included in the class definition (if any) and the resulting object is bound in the local namespace as the defined class.

When a new class is created by type.__new__ , the object provided as the namespace parameter is copied to a new ordered mapping and the original object is discarded. The new copy is wrapped in a read-only proxy, which becomes the __dict__ attribute of the class object.

Describes the implicit __class__ closure reference

3.3.3.7. Uses for metaclasses ¶

The potential uses for metaclasses are boundless. Some ideas that have been explored include enum, logging, interface checking, automatic delegation, automatic property creation, proxies, frameworks, and automatic resource locking/synchronization.

3.3.4. Customizing instance and subclass checks ¶

The following methods are used to override the default behavior of the isinstance() and issubclass() built-in functions.

In particular, the metaclass abc.ABCMeta implements these methods in order to allow the addition of Abstract Base Classes (ABCs) as “virtual base classes” to any class or type (including built-in types), including other ABCs.

Return true if instance should be considered a (direct or indirect) instance of class . If defined, called to implement isinstance(instance, class) .

Return true if subclass should be considered a (direct or indirect) subclass of class . If defined, called to implement issubclass(subclass, class) .

Note that these methods are looked up on the type (metaclass) of a class. They cannot be defined as class methods in the actual class. This is consistent with the lookup of special methods that are called on instances, only in this case the instance is itself a class.

Includes the specification for customizing isinstance() and issubclass() behavior through __instancecheck__() and __subclasscheck__() , with motivation for this functionality in the context of adding Abstract Base Classes (see the abc module) to the language.

3.3.5. Emulating generic types ¶

When using type annotations , it is often useful to parameterize a generic type using Python’s square-brackets notation. For example, the annotation list[int] might be used to signify a list in which all the elements are of type int .

Introducing Python’s framework for type annotations

Documentation for objects representing parameterized generic classes

Documentation on how to implement generic classes that can be parameterized at runtime and understood by static type-checkers.

A class can generally only be parameterized if it defines the special class method __class_getitem__() .

Return an object representing the specialization of a generic class by type arguments found in key .

When defined on a class, __class_getitem__() is automatically a class method. As such, there is no need for it to be decorated with @classmethod when it is defined.

3.3.5.1. The purpose of __class_getitem__ ¶

The purpose of __class_getitem__() is to allow runtime parameterization of standard-library generic classes in order to more easily apply type hints to these classes.

To implement custom generic classes that can be parameterized at runtime and understood by static type-checkers, users should either inherit from a standard library class that already implements __class_getitem__() , or inherit from typing.Generic , which has its own implementation of __class_getitem__() .

Custom implementations of __class_getitem__() on classes defined outside of the standard library may not be understood by third-party type-checkers such as mypy. Using __class_getitem__() on any class for purposes other than type hinting is discouraged.

3.3.5.2. __class_getitem__ versus __getitem__ ¶

Usually, the subscription of an object using square brackets will call the __getitem__() instance method defined on the object’s class. However, if the object being subscribed is itself a class, the class method __class_getitem__() may be called instead. __class_getitem__() should return a GenericAlias object if it is properly defined.

Presented with the expression obj[x] , the Python interpreter follows something like the following process to decide whether __getitem__() or __class_getitem__() should be called:

In Python, all classes are themselves instances of other classes. The class of a class is known as that class’s metaclass , and most classes have the type class as their metaclass. type does not define __getitem__() , meaning that expressions such as list[int] , dict[str, float] and tuple[str, bytes] all result in __class_getitem__() being called:

However, if a class has a custom metaclass that defines __getitem__() , subscribing the class may result in different behaviour. An example of this can be found in the enum module:

Introducing __class_getitem__() , and outlining when a subscription results in __class_getitem__() being called instead of __getitem__()

3.3.6. Emulating callable objects ¶

Called when the instance is “called” as a function; if this method is defined, x(arg1, arg2, ...) roughly translates to type(x).__call__(x, arg1, ...) .

3.3.7. Emulating container types ¶

The following methods can be defined to implement container objects. Containers usually are sequences (such as lists or tuples ) or mappings (like dictionaries ), but can represent other containers as well. The first set of methods is used either to emulate a sequence or to emulate a mapping; the difference is that for a sequence, the allowable keys should be the integers k for which 0 <= k < N where N is the length of the sequence, or slice objects, which define a range of items. It is also recommended that mappings provide the methods keys() , values() , items() , get() , clear() , setdefault() , pop() , popitem() , copy() , and update() behaving similar to those for Python’s standard dictionary objects. The collections.abc module provides a MutableMapping abstract base class to help create those methods from a base set of __getitem__() , __setitem__() , __delitem__() , and keys() . Mutable sequences should provide methods append() , count() , index() , extend() , insert() , pop() , remove() , reverse() and sort() , like Python standard list objects. Finally, sequence types should implement addition (meaning concatenation) and multiplication (meaning repetition) by defining the methods __add__() , __radd__() , __iadd__() , __mul__() , __rmul__() and __imul__() described below; they should not define other numerical operators. It is recommended that both mappings and sequences implement the __contains__() method to allow efficient use of the in operator; for mappings, in should search the mapping’s keys; for sequences, it should search through the values. It is further recommended that both mappings and sequences implement the __iter__() method to allow efficient iteration through the container; for mappings, __iter__() should iterate through the object’s keys; for sequences, it should iterate through the values.

Called to implement the built-in function len() . Should return the length of the object, an integer >= 0. Also, an object that doesn’t define a __bool__() method and whose __len__() method returns zero is considered to be false in a Boolean context.

CPython implementation detail: In CPython, the length is required to be at most sys.maxsize . If the length is larger than sys.maxsize some features (such as len() ) may raise OverflowError . To prevent raising OverflowError by truth value testing, an object must define a __bool__() method.

Called to implement operator.length_hint() . Should return an estimated length for the object (which may be greater or less than the actual length). The length must be an integer >= 0. The return value may also be NotImplemented , which is treated the same as if the __length_hint__ method didn’t exist at all. This method is purely an optimization and is never required for correctness.

Slicing is done exclusively with the following three methods. A call like

is translated to

and so forth. Missing slice items are always filled in with None .

Called to implement evaluation of self[key] . For sequence types, the accepted keys should be integers. Optionally, they may support slice objects as well. Negative index support is also optional. If key is of an inappropriate type, TypeError may be raised; if key is a value outside the set of indexes for the sequence (after any special interpretation of negative values), IndexError should be raised. For mapping types, if key is missing (not in the container), KeyError should be raised.

for loops expect that an IndexError will be raised for illegal indexes to allow proper detection of the end of the sequence.

When subscripting a class , the special class method __class_getitem__() may be called instead of __getitem__() . See __class_getitem__ versus __getitem__ for more details.

Called to implement assignment to self[key] . Same note as for __getitem__() . This should only be implemented for mappings if the objects support changes to the values for keys, or if new keys can be added, or for sequences if elements can be replaced. The same exceptions should be raised for improper key values as for the __getitem__() method.

Called to implement deletion of self[key] . Same note as for __getitem__() . This should only be implemented for mappings if the objects support removal of keys, or for sequences if elements can be removed from the sequence. The same exceptions should be raised for improper key values as for the __getitem__() method.

Called by dict . __getitem__() to implement self[key] for dict subclasses when key is not in the dictionary.

This method is called when an iterator is required for a container. This method should return a new iterator object that can iterate over all the objects in the container. For mappings, it should iterate over the keys of the container.

Called (if present) by the reversed() built-in to implement reverse iteration. It should return a new iterator object that iterates over all the objects in the container in reverse order.

If the __reversed__() method is not provided, the reversed() built-in will fall back to using the sequence protocol ( __len__() and __getitem__() ). Objects that support the sequence protocol should only provide __reversed__() if they can provide an implementation that is more efficient than the one provided by reversed() .

The membership test operators ( in and not in ) are normally implemented as an iteration through a container. However, container objects can supply the following special method with a more efficient implementation, which also does not require the object be iterable.

Called to implement membership test operators. Should return true if item is in self , false otherwise. For mapping objects, this should consider the keys of the mapping rather than the values or the key-item pairs.

For objects that don’t define __contains__() , the membership test first tries iteration via __iter__() , then the old sequence iteration protocol via __getitem__() , see this section in the language reference .

3.3.8. Emulating numeric types ¶

The following methods can be defined to emulate numeric objects. Methods corresponding to operations that are not supported by the particular kind of number implemented (e.g., bitwise operations for non-integral numbers) should be left undefined.

These methods are called to implement the binary arithmetic operations ( + , - , * , @ , / , // , % , divmod() , pow() , ** , << , >> , & , ^ , | ). For instance, to evaluate the expression x + y , where x is an instance of a class that has an __add__() method, type(x).__add__(x, y) is called. The __divmod__() method should be the equivalent to using __floordiv__() and __mod__() ; it should not be related to __truediv__() . Note that __pow__() should be defined to accept an optional third argument if the ternary version of the built-in pow() function is to be supported.

If one of those methods does not support the operation with the supplied arguments, it should return NotImplemented .

These methods are called to implement the binary arithmetic operations ( + , - , * , @ , / , // , % , divmod() , pow() , ** , << , >> , & , ^ , | ) with reflected (swapped) operands. These functions are only called if the left operand does not support the corresponding operation [ 3 ] and the operands are of different types. [ 4 ] For instance, to evaluate the expression x - y , where y is an instance of a class that has an __rsub__() method, type(y).__rsub__(y, x) is called if type(x).__sub__(x, y) returns NotImplemented .

Note that ternary pow() will not try calling __rpow__() (the coercion rules would become too complicated).

If the right operand’s type is a subclass of the left operand’s type and that subclass provides a different implementation of the reflected method for the operation, this method will be called before the left operand’s non-reflected method. This behavior allows subclasses to override their ancestors’ operations.

These methods are called to implement the augmented arithmetic assignments ( += , -= , *= , @= , /= , //= , %= , **= , <<= , >>= , &= , ^= , |= ). These methods should attempt to do the operation in-place (modifying self ) and return the result (which could be, but does not have to be, self ). If a specific method is not defined, or if that method returns NotImplemented , the augmented assignment falls back to the normal methods. For instance, if x is an instance of a class with an __iadd__() method, x += y is equivalent to x = x.__iadd__(y) . If __iadd__() does not exist, or if x.__iadd__(y) returns NotImplemented , x.__add__(y) and y.__radd__(x) are considered, as with the evaluation of x + y . In certain situations, augmented assignment can result in unexpected errors (see Why does a_tuple[i] += [‘item’] raise an exception when the addition works? ), but this behavior is in fact part of the data model.

Called to implement the unary arithmetic operations ( - , + , abs() and ~ ).

Called to implement the built-in functions complex() , int() and float() . Should return a value of the appropriate type.

Called to implement operator.index() , and whenever Python needs to losslessly convert the numeric object to an integer object (such as in slicing, or in the built-in bin() , hex() and oct() functions). Presence of this method indicates that the numeric object is an integer type. Must return an integer.

If __int__() , __float__() and __complex__() are not defined then corresponding built-in functions int() , float() and complex() fall back to __index__() .

Called to implement the built-in function round() and math functions trunc() , floor() and ceil() . Unless ndigits is passed to __round__() all these methods should return the value of the object truncated to an Integral (typically an int ).

The built-in function int() falls back to __trunc__() if neither __int__() nor __index__() is defined.

Changed in version 3.11: The delegation of int() to __trunc__() is deprecated.

3.3.9. With Statement Context Managers ¶

A context manager is an object that defines the runtime context to be established when executing a with statement. The context manager handles the entry into, and the exit from, the desired runtime context for the execution of the block of code. Context managers are normally invoked using the with statement (described in section The with statement ), but can also be used by directly invoking their methods.

Typical uses of context managers include saving and restoring various kinds of global state, locking and unlocking resources, closing opened files, etc.

For more information on context managers, see Context Manager Types .

Enter the runtime context related to this object. The with statement will bind this method’s return value to the target(s) specified in the as clause of the statement, if any.

Exit the runtime context related to this object. The parameters describe the exception that caused the context to be exited. If the context was exited without an exception, all three arguments will be None .

If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method.

Note that __exit__() methods should not reraise the passed-in exception; this is the caller’s responsibility.

The specification, background, and examples for the Python with statement.

3.3.10. Customizing positional arguments in class pattern matching ¶

When using a class name in a pattern, positional arguments in the pattern are not allowed by default, i.e. case MyClass(x, y) is typically invalid without special support in MyClass . To be able to use that kind of pattern, the class needs to define a __match_args__ attribute.

This class variable can be assigned a tuple of strings. When this class is used in a class pattern with positional arguments, each positional argument will be converted into a keyword argument, using the corresponding value in __match_args__ as the keyword. The absence of this attribute is equivalent to setting it to () .

For example, if MyClass.__match_args__ is ("left", "center", "right") that means that case MyClass(x, y) is equivalent to case MyClass(left=x, center=y) . Note that the number of arguments in the pattern must be smaller than or equal to the number of elements in __match_args__ ; if it is larger, the pattern match attempt will raise a TypeError .

The specification for the Python match statement.

3.3.11. Emulating buffer types ¶

The buffer protocol provides a way for Python objects to expose efficient access to a low-level memory array. This protocol is implemented by builtin types such as bytes and memoryview , and third-party libraries may define additional buffer types.

While buffer types are usually implemented in C, it is also possible to implement the protocol in Python.

Called when a buffer is requested from self (for example, by the memoryview constructor). The flags argument is an integer representing the kind of buffer requested, affecting for example whether the returned buffer is read-only or writable. inspect.BufferFlags provides a convenient way to interpret the flags. The method must return a memoryview object.

Called when a buffer is no longer needed. The buffer argument is a memoryview object that was previously returned by __buffer__() . The method must release any resources associated with the buffer. This method should return None . Buffer objects that do not need to perform any cleanup are not required to implement this method.

Added in version 3.12.

Introduces the Python __buffer__ and __release_buffer__ methods.

ABC for buffer types.

3.3.12. Special method lookup ¶

For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary. That behaviour is the reason why the following code raises an exception:

The rationale behind this behaviour lies with a number of special methods such as __hash__() and __repr__() that are implemented by all objects, including type objects. If the implicit lookup of these methods used the conventional lookup process, they would fail when invoked on the type object itself:

Incorrectly attempting to invoke an unbound method of a class in this way is sometimes referred to as ‘metaclass confusion’, and is avoided by bypassing the instance when looking up special methods:

In addition to bypassing any instance attributes in the interest of correctness, implicit special method lookup generally also bypasses the __getattribute__() method even of the object’s metaclass:

Bypassing the __getattribute__() machinery in this fashion provides significant scope for speed optimisations within the interpreter, at the cost of some flexibility in the handling of special methods (the special method must be set on the class object itself in order to be consistently invoked by the interpreter).

3.4. Coroutines ¶

3.4.1. awaitable objects ¶.

An awaitable object generally implements an __await__() method. Coroutine objects returned from async def functions are awaitable.

The generator iterator objects returned from generators decorated with types.coroutine() are also awaitable, but they do not implement __await__() .

Must return an iterator . Should be used to implement awaitable objects. For instance, asyncio.Future implements this method to be compatible with the await expression.

The language doesn’t place any restriction on the type or value of the objects yielded by the iterator returned by __await__ , as this is specific to the implementation of the asynchronous execution framework (e.g. asyncio ) that will be managing the awaitable object.

Added in version 3.5.

PEP 492 for additional information about awaitable objects.

3.4.2. Coroutine Objects ¶

Coroutine objects are awaitable objects. A coroutine’s execution can be controlled by calling __await__() and iterating over the result. When the coroutine has finished executing and returns, the iterator raises StopIteration , and the exception’s value attribute holds the return value. If the coroutine raises an exception, it is propagated by the iterator. Coroutines should not directly raise unhandled StopIteration exceptions.

Coroutines also have the methods listed below, which are analogous to those of generators (see Generator-iterator methods ). However, unlike generators, coroutines do not directly support iteration.

Changed in version 3.5.2: It is a RuntimeError to await on a coroutine more than once.

Starts or resumes execution of the coroutine. If value is None , this is equivalent to advancing the iterator returned by __await__() . If value is not None , this method delegates to the send() method of the iterator that caused the coroutine to suspend. The result (return value, StopIteration , or other exception) is the same as when iterating over the __await__() return value, described above.

Raises the specified exception in the coroutine. This method delegates to the throw() method of the iterator that caused the coroutine to suspend, if it has such a method. Otherwise, the exception is raised at the suspension point. The result (return value, StopIteration , or other exception) is the same as when iterating over the __await__() return value, described above. If the exception is not caught in the coroutine, it propagates back to the caller.

Changed in version 3.12: The second signature (type[, value[, traceback]]) is deprecated and may be removed in a future version of Python.

Causes the coroutine to clean itself up and exit. If the coroutine is suspended, this method first delegates to the close() method of the iterator that caused the coroutine to suspend, if it has such a method. Then it raises GeneratorExit at the suspension point, causing the coroutine to immediately clean itself up. Finally, the coroutine is marked as having finished executing, even if it was never started.

Coroutine objects are automatically closed using the above process when they are about to be destroyed.

3.4.3. Asynchronous Iterators ¶

An asynchronous iterator can call asynchronous code in its __anext__ method.

Asynchronous iterators can be used in an async for statement.

Must return an asynchronous iterator object.

Must return an awaitable resulting in a next value of the iterator. Should raise a StopAsyncIteration error when the iteration is over.

An example of an asynchronous iterable object:

Changed in version 3.7: Prior to Python 3.7, __aiter__() could return an awaitable that would resolve to an asynchronous iterator .

Starting with Python 3.7, __aiter__() must return an asynchronous iterator object. Returning anything else will result in a TypeError error.

3.4.4. Asynchronous Context Managers ¶

An asynchronous context manager is a context manager that is able to suspend execution in its __aenter__ and __aexit__ methods.

Asynchronous context managers can be used in an async with statement.

Semantically similar to __enter__() , the only difference being that it must return an awaitable .

Semantically similar to __exit__() , the only difference being that it must return an awaitable .

An example of an asynchronous context manager class:

Table of Contents

  • 3.1. Objects, values and types
  • 3.2.1. None
  • 3.2.2. NotImplemented
  • 3.2.3. Ellipsis
  • 3.2.4.1. numbers.Integral
  • 3.2.4.2. numbers.Real ( float )
  • 3.2.4.3. numbers.Complex ( complex )
  • 3.2.5.1. Immutable sequences
  • 3.2.5.2. Mutable sequences
  • 3.2.6. Set types
  • 3.2.7.1. Dictionaries
  • 3.2.8.1.1. Special read-only attributes
  • 3.2.8.1.2. Special writable attributes
  • 3.2.8.2. Instance methods
  • 3.2.8.3. Generator functions
  • 3.2.8.4. Coroutine functions
  • 3.2.8.5. Asynchronous generator functions
  • 3.2.8.6. Built-in functions
  • 3.2.8.7. Built-in methods
  • 3.2.8.8. Classes
  • 3.2.8.9. Class Instances
  • 3.2.9. Modules
  • 3.2.10. Custom classes
  • 3.2.11. Class instances
  • 3.2.12. I/O objects (also known as file objects)
  • 3.2.13.1.1. Special read-only attributes
  • 3.2.13.1.2. Methods on code objects
  • 3.2.13.2.1. Special read-only attributes
  • 3.2.13.2.2. Special writable attributes
  • 3.2.13.2.3. Frame object methods
  • 3.2.13.3. Traceback objects
  • 3.2.13.4. Slice objects
  • 3.2.13.5. Static method objects
  • 3.2.13.6. Class method objects
  • 3.3.1. Basic customization
  • 3.3.2.1. Customizing module attribute access
  • 3.3.2.2. Implementing Descriptors
  • 3.3.2.3. Invoking Descriptors
  • 3.3.2.4. __slots__
  • 3.3.3.1. Metaclasses
  • 3.3.3.2. Resolving MRO entries
  • 3.3.3.3. Determining the appropriate metaclass
  • 3.3.3.4. Preparing the class namespace
  • 3.3.3.5. Executing the class body
  • 3.3.3.6. Creating the class object
  • 3.3.3.7. Uses for metaclasses
  • 3.3.4. Customizing instance and subclass checks
  • 3.3.5.1. The purpose of __class_getitem__
  • 3.3.5.2. __class_getitem__ versus __getitem__
  • 3.3.6. Emulating callable objects
  • 3.3.7. Emulating container types
  • 3.3.8. Emulating numeric types
  • 3.3.9. With Statement Context Managers
  • 3.3.10. Customizing positional arguments in class pattern matching
  • 3.3.11. Emulating buffer types
  • 3.3.12. Special method lookup
  • 3.4.1. Awaitable Objects
  • 3.4.2. Coroutine Objects
  • 3.4.3. Asynchronous Iterators
  • 3.4.4. Asynchronous Context Managers

Previous topic

2. Lexical analysis

4. Execution model

  • Report a Bug
  • Show Source

Python Operators: Arithmetic, Assignment, Comparison, Logical, Identity, Membership, Bitwise

Operators are special symbols that perform some operation on operands and returns the result. For example, 5 + 6 is an expression where + is an operator that performs arithmetic add operation on numeric left operand 5 and the right side operand 6 and returns a sum of two operands as a result.

Python includes the operator module that includes underlying methods for each operator. For example, the + operator calls the operator.add(a,b) method.

Above, expression 5 + 6 is equivalent to the expression operator.add(5, 6) and operator.__add__(5, 6) . Many function names are those used for special methods, without the double underscores (dunder methods). For backward compatibility, many of these have functions with the double underscores kept.

Python includes the following categories of operators:

Arithmetic Operators

Assignment operators, comparison operators, logical operators, identity operators, membership test operators, bitwise operators.

Arithmetic operators perform the common mathematical operation on the numeric operands.

The arithmetic operators return the type of result depends on the type of operands, as below.

  • If either operand is a complex number, the result is converted to complex;
  • If either operand is a floating point number, the result is converted to floating point;
  • If both operands are integers, then the result is an integer and no conversion is needed.

The following table lists all the arithmetic operators in Python:

Operation Operator Function Example in Python Shell
Sum of two operands + operator.add(a,b)
Left operand minus right operand - operator.sub(a,b)
* operator.mul(a,b)
Left operand raised to the power of right ** operator.pow(a,b)
/ operator.truediv(a,b)
equivilant to // operator.floordiv(a,b)
Reminder of % operator.mod(a, b)

The assignment operators are used to assign values to variables. The following table lists all the arithmetic operators in Python:

Operator Function Example in Python Shell
=
+= operator.iadd(a,b)
-= operator.isub(a,b)
*= operator.imul(a,b)
/= operator.itruediv(a,b)
//= operator.ifloordiv(a,b)
%= operator.imod(a, b)
&= operator.iand(a, b)
|= operator.ior(a, b)
^= operator.ixor(a, b)
>>= operator.irshift(a, b)
<<= operator.ilshift(a, b)

The comparison operators compare two operands and return a boolean either True or False. The following table lists comparison operators in Python.

Operator Function Description Example in Python Shell
> operator.gt(a,b) True if the left operand is higher than the right one
< operator.lt(a,b) True if the left operand is lower than right one
== operator.eq(a,b) True if the operands are equal
!= operator.ne(a,b) True if the operands are not equal
>= operator.ge(a,b) True if the left operand is higher than or equal to the right one
<= operator.le(a,b) True if the left operand is lower than or equal to the right one

The logical operators are used to combine two boolean expressions. The logical operations are generally applicable to all objects, and support truth tests, identity tests, and boolean operations.

Operator Description Example
and True if both are true
or True if at least one is true
not Returns True if an expression evalutes to false and vice-versa

The identity operators check whether the two objects have the same id value e.i. both the objects point to the same memory location.

Operator Function Description Example in Python Shell
is operator.is_(a,b) True if both are true
is not operator.is_not(a,b) True if at least one is true

The membership test operators in and not in test whether the sequence has a given item or not. For the string and bytes types, x in y is True if and only if x is a substring of y .

Operator Function Description Example in Python Shell
in operator.contains(a,b) Returns True if the sequence contains the specified item else returns False.
not in not operator.contains(a,b) Returns True if the sequence does not contains the specified item, else returns False.

Bitwise operators perform operations on binary operands.

Operator Function Description Example in Python Shell
& operator.and_(a,b) Sets each bit to 1 if both bits are 1.
| operator.or_(a,b) Sets each bit to 1 if one of two bits is 1.
^ operator.xor(a,b) Sets each bit to 1 if only one of two bits is 1.
~ operator.invert(a) Inverts all the bits.
<< operator.lshift(a,b) Shift left by pushing zeros in from the right and let the leftmost bits fall off.
>> operator.rshift(a,b) Shift right by pushing copies of the leftmost bit in from the left, and let the rightmost bits fall off.
  • Compare strings in Python
  • Convert file data to list
  • Convert User Input to a Number
  • Convert String to Datetime in Python
  • How to call external commands in Python?
  • How to count the occurrences of a list item?
  • How to flatten list in Python?
  • How to merge dictionaries in Python?
  • How to pass value by reference in Python?
  • Remove duplicate items from list in Python
  • More Python articles

python assignment dunder

We are a team of passionate developers, educators, and technology enthusiasts who, with their combined expertise and experience, create in -depth, comprehensive, and easy to understand tutorials.We focus on a blend of theoretical explanations and practical examples to encourages hands - on learning. Visit About Us page for more information.

  • Python Questions & Answers
  • Python Skill Test
  • Python Latest Articles

Python Enhancement Proposals

  • Python »
  • PEP Index »

PEP 8 – Style Guide for Python Code

Introduction, a foolish consistency is the hobgoblin of little minds, indentation, tabs or spaces, maximum line length, should a line break before or after a binary operator, blank lines, source file encoding, module level dunder names, string quotes, other recommendations, when to use trailing commas, block comments, inline comments, documentation strings, overriding principle, descriptive: naming styles, names to avoid, ascii compatibility, package and module names, class names, type variable names, exception names, global variable names, function and variable names, function and method arguments, method names and instance variables, designing for inheritance, public and internal interfaces, function annotations, variable annotations.

This document gives coding conventions for the Python code comprising the standard library in the main Python distribution. Please see the companion informational PEP describing style guidelines for the C code in the C implementation of Python .

This document and PEP 257 (Docstring Conventions) were adapted from Guido’s original Python Style Guide essay, with some additions from Barry’s style guide [2] .

This style guide evolves over time as additional conventions are identified and past conventions are rendered obsolete by changes in the language itself.

Many projects have their own coding style guidelines. In the event of any conflicts, such project-specific guides take precedence for that project.

One of Guido’s key insights is that code is read much more often than it is written. The guidelines provided here are intended to improve the readability of code and make it consistent across the wide spectrum of Python code. As PEP 20 says, “Readability counts”.

A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is the most important.

However, know when to be inconsistent – sometimes style guide recommendations just aren’t applicable. When in doubt, use your best judgment. Look at other examples and decide what looks best. And don’t hesitate to ask!

In particular: do not break backwards compatibility just to comply with this PEP!

Some other good reasons to ignore a particular guideline:

  • When applying the guideline would make the code less readable, even for someone who is used to reading code that follows this PEP.
  • To be consistent with surrounding code that also breaks it (maybe for historic reasons) – although this is also an opportunity to clean up someone else’s mess (in true XP style).
  • Because the code in question predates the introduction of the guideline and there is no other reason to be modifying that code.
  • When the code needs to remain compatible with older versions of Python that don’t support the feature recommended by the style guide.

Code Lay-out

Use 4 spaces per indentation level.

Continuation lines should align wrapped elements either vertically using Python’s implicit line joining inside parentheses, brackets and braces, or using a hanging indent [1] . When using a hanging indent the following should be considered; there should be no arguments on the first line and further indentation should be used to clearly distinguish itself as a continuation line:

The 4-space rule is optional for continuation lines.

When the conditional part of an if -statement is long enough to require that it be written across multiple lines, it’s worth noting that the combination of a two character keyword (i.e. if ), plus a single space, plus an opening parenthesis creates a natural 4-space indent for the subsequent lines of the multiline conditional. This can produce a visual conflict with the indented suite of code nested inside the if -statement, which would also naturally be indented to 4 spaces. This PEP takes no explicit position on how (or whether) to further visually distinguish such conditional lines from the nested suite inside the if -statement. Acceptable options in this situation include, but are not limited to:

(Also see the discussion of whether to break before or after binary operators below.)

The closing brace/bracket/parenthesis on multiline constructs may either line up under the first non-whitespace character of the last line of list, as in:

or it may be lined up under the first character of the line that starts the multiline construct, as in:

Spaces are the preferred indentation method.

Tabs should be used solely to remain consistent with code that is already indented with tabs.

Python disallows mixing tabs and spaces for indentation.

Limit all lines to a maximum of 79 characters.

For flowing long blocks of text with fewer structural restrictions (docstrings or comments), the line length should be limited to 72 characters.

Limiting the required editor window width makes it possible to have several files open side by side, and works well when using code review tools that present the two versions in adjacent columns.

The default wrapping in most tools disrupts the visual structure of the code, making it more difficult to understand. The limits are chosen to avoid wrapping in editors with the window width set to 80, even if the tool places a marker glyph in the final column when wrapping lines. Some web based tools may not offer dynamic line wrapping at all.

Some teams strongly prefer a longer line length. For code maintained exclusively or primarily by a team that can reach agreement on this issue, it is okay to increase the line length limit up to 99 characters, provided that comments and docstrings are still wrapped at 72 characters.

The Python standard library is conservative and requires limiting lines to 79 characters (and docstrings/comments to 72).

The preferred way of wrapping long lines is by using Python’s implied line continuation inside parentheses, brackets and braces. Long lines can be broken over multiple lines by wrapping expressions in parentheses. These should be used in preference to using a backslash for line continuation.

Backslashes may still be appropriate at times. For example, long, multiple with -statements could not use implicit continuation before Python 3.10, so backslashes were acceptable for that case:

(See the previous discussion on multiline if-statements for further thoughts on the indentation of such multiline with -statements.)

Another such case is with assert statements.

Make sure to indent the continued line appropriately.

For decades the recommended style was to break after binary operators. But this can hurt readability in two ways: the operators tend to get scattered across different columns on the screen, and each operator is moved away from its operand and onto the previous line. Here, the eye has to do extra work to tell which items are added and which are subtracted:

To solve this readability problem, mathematicians and their publishers follow the opposite convention. Donald Knuth explains the traditional rule in his Computers and Typesetting series: “Although formulas within a paragraph always break after binary operations and relations, displayed formulas always break before binary operations” [3] .

Following the tradition from mathematics usually results in more readable code:

In Python code, it is permissible to break before or after a binary operator, as long as the convention is consistent locally. For new code Knuth’s style is suggested.

Surround top-level function and class definitions with two blank lines.

Method definitions inside a class are surrounded by a single blank line.

Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations).

Use blank lines in functions, sparingly, to indicate logical sections.

Python accepts the control-L (i.e. ^L) form feed character as whitespace; many tools treat these characters as page separators, so you may use them to separate pages of related sections of your file. Note, some editors and web-based code viewers may not recognize control-L as a form feed and will show another glyph in its place.

Code in the core Python distribution should always use UTF-8, and should not have an encoding declaration.

In the standard library, non-UTF-8 encodings should be used only for test purposes. Use non-ASCII characters sparingly, preferably only to denote places and human names. If using non-ASCII characters as data, avoid noisy Unicode characters like z̯̯͡a̧͎̺l̡͓̫g̹̲o̡̼̘ and byte order marks.

All identifiers in the Python standard library MUST use ASCII-only identifiers, and SHOULD use English words wherever feasible (in many cases, abbreviations and technical terms are used which aren’t English).

Open source projects with a global audience are encouraged to adopt a similar policy.

It’s okay to say this though:

Imports should be grouped in the following order:

  • Standard library imports.
  • Related third party imports.
  • Local application/library specific imports.

You should put a blank line between each group of imports.

However, explicit relative imports are an acceptable alternative to absolute imports, especially when dealing with complex package layouts where using absolute imports would be unnecessarily verbose:

Standard library code should avoid complex package layouts and always use absolute imports.

If this spelling causes local name clashes, then spell them explicitly:

and use myclass.MyClass and foo.bar.yourclass.YourClass .

When republishing names this way, the guidelines below regarding public and internal interfaces still apply.

Module level “dunders” (i.e. names with two leading and two trailing underscores) such as __all__ , __author__ , __version__ , etc. should be placed after the module docstring but before any import statements except from __future__ imports. Python mandates that future-imports must appear in the module before any other code except docstrings:

In Python, single-quoted strings and double-quoted strings are the same. This PEP does not make a recommendation for this. Pick a rule and stick to it. When a string contains single or double quote characters, however, use the other one to avoid backslashes in the string. It improves readability.

For triple-quoted strings, always use double quote characters to be consistent with the docstring convention in PEP 257 .

Whitespace in Expressions and Statements

Avoid extraneous whitespace in the following situations:

  • Immediately inside parentheses, brackets or braces: # Correct: spam ( ham [ 1 ], { eggs : 2 }) # Wrong: spam ( ham [ 1 ], { eggs : 2 } )
  • Between a trailing comma and a following close parenthesis: # Correct: foo = ( 0 ,) # Wrong: bar = ( 0 , )
  • Immediately before a comma, semicolon, or colon: # Correct: if x == 4 : print ( x , y ); x , y = y , x # Wrong: if x == 4 : print ( x , y ) ; x , y = y , x
  • However, in a slice the colon acts like a binary operator, and should have equal amounts on either side (treating it as the operator with the lowest priority). In an extended slice, both colons must have the same amount of spacing applied. Exception: when a slice parameter is omitted, the space is omitted: # Correct: ham [ 1 : 9 ], ham [ 1 : 9 : 3 ], ham [: 9 : 3 ], ham [ 1 :: 3 ], ham [ 1 : 9 :] ham [ lower : upper ], ham [ lower : upper :], ham [ lower :: step ] ham [ lower + offset : upper + offset ] ham [: upper_fn ( x ) : step_fn ( x )], ham [:: step_fn ( x )] ham [ lower + offset : upper + offset ] # Wrong: ham [ lower + offset : upper + offset ] ham [ 1 : 9 ], ham [ 1 : 9 ], ham [ 1 : 9 : 3 ] ham [ lower : : step ] ham [ : upper ]
  • Immediately before the open parenthesis that starts the argument list of a function call: # Correct: spam ( 1 ) # Wrong: spam ( 1 )
  • Immediately before the open parenthesis that starts an indexing or slicing: # Correct: dct [ 'key' ] = lst [ index ] # Wrong: dct [ 'key' ] = lst [ index ]
  • More than one space around an assignment (or other) operator to align it with another: # Correct: x = 1 y = 2 long_variable = 3 # Wrong: x = 1 y = 2 long_variable = 3
  • Avoid trailing whitespace anywhere. Because it’s usually invisible, it can be confusing: e.g. a backslash followed by a space and a newline does not count as a line continuation marker. Some editors don’t preserve it and many projects (like CPython itself) have pre-commit hooks that reject it.
  • Always surround these binary operators with a single space on either side: assignment ( = ), augmented assignment ( += , -= etc.), comparisons ( == , < , > , != , <> , <= , >= , in , not in , is , is not ), Booleans ( and , or , not ).
  • If operators with different priorities are used, consider adding whitespace around the operators with the lowest priority(ies). Use your own judgment; however, never use more than one space, and always have the same amount of whitespace on both sides of a binary operator: # Correct: i = i + 1 submitted += 1 x = x * 2 - 1 hypot2 = x * x + y * y c = ( a + b ) * ( a - b ) # Wrong: i = i + 1 submitted += 1 x = x * 2 - 1 hypot2 = x * x + y * y c = ( a + b ) * ( a - b )
  • Function annotations should use the normal rules for colons and always have spaces around the -> arrow if present. (See Function Annotations below for more about function annotations.): # Correct: def munge ( input : AnyStr ): ... def munge () -> PosInt : ... # Wrong: def munge ( input : AnyStr ): ... def munge () -> PosInt : ...

When combining an argument annotation with a default value, however, do use spaces around the = sign:

Rather not:

Definitely not:

Trailing commas are usually optional, except they are mandatory when making a tuple of one element. For clarity, it is recommended to surround the latter in (technically redundant) parentheses:

When trailing commas are redundant, they are often helpful when a version control system is used, when a list of values, arguments or imported items is expected to be extended over time. The pattern is to put each value (etc.) on a line by itself, always adding a trailing comma, and add the close parenthesis/bracket/brace on the next line. However it does not make sense to have a trailing comma on the same line as the closing delimiter (except in the above case of singleton tuples):

Comments that contradict the code are worse than no comments. Always make a priority of keeping the comments up-to-date when the code changes!

Comments should be complete sentences. The first word should be capitalized, unless it is an identifier that begins with a lower case letter (never alter the case of identifiers!).

Block comments generally consist of one or more paragraphs built out of complete sentences, with each sentence ending in a period.

You should use one or two spaces after a sentence-ending period in multi-sentence comments, except after the final sentence.

Ensure that your comments are clear and easily understandable to other speakers of the language you are writing in.

Python coders from non-English speaking countries: please write your comments in English, unless you are 120% sure that the code will never be read by people who don’t speak your language.

Block comments generally apply to some (or all) code that follows them, and are indented to the same level as that code. Each line of a block comment starts with a # and a single space (unless it is indented text inside the comment).

Paragraphs inside a block comment are separated by a line containing a single # .

Use inline comments sparingly.

An inline comment is a comment on the same line as a statement. Inline comments should be separated by at least two spaces from the statement. They should start with a # and a single space.

Inline comments are unnecessary and in fact distracting if they state the obvious. Don’t do this:

But sometimes, this is useful:

Conventions for writing good documentation strings (a.k.a. “docstrings”) are immortalized in PEP 257 .

  • Write docstrings for all public modules, functions, classes, and methods. Docstrings are not necessary for non-public methods, but you should have a comment that describes what the method does. This comment should appear after the def line.
  • PEP 257 describes good docstring conventions. Note that most importantly, the """ that ends a multiline docstring should be on a line by itself: """Return a foobang Optional plotz says to frobnicate the bizbaz first. """
  • For one liner docstrings, please keep the closing """ on the same line: """Return an ex-parrot."""

Naming Conventions

The naming conventions of Python’s library are a bit of a mess, so we’ll never get this completely consistent – nevertheless, here are the currently recommended naming standards. New modules and packages (including third party frameworks) should be written to these standards, but where an existing library has a different style, internal consistency is preferred.

Names that are visible to the user as public parts of the API should follow conventions that reflect usage rather than implementation.

There are a lot of different naming styles. It helps to be able to recognize what naming style is being used, independently from what they are used for.

The following naming styles are commonly distinguished:

  • b (single lowercase letter)
  • B (single uppercase letter)
  • lower_case_with_underscores
  • UPPER_CASE_WITH_UNDERSCORES

Note: When using acronyms in CapWords, capitalize all the letters of the acronym. Thus HTTPServerError is better than HttpServerError.

  • mixedCase (differs from CapitalizedWords by initial lowercase character!)
  • Capitalized_Words_With_Underscores (ugly!)

There’s also the style of using a short unique prefix to group related names together. This is not used much in Python, but it is mentioned for completeness. For example, the os.stat() function returns a tuple whose items traditionally have names like st_mode , st_size , st_mtime and so on. (This is done to emphasize the correspondence with the fields of the POSIX system call struct, which helps programmers familiar with that.)

The X11 library uses a leading X for all its public functions. In Python, this style is generally deemed unnecessary because attribute and method names are prefixed with an object, and function names are prefixed with a module name.

In addition, the following special forms using leading or trailing underscores are recognized (these can generally be combined with any case convention):

  • _single_leading_underscore : weak “internal use” indicator. E.g. from M import * does not import objects whose names start with an underscore.
  • single_trailing_underscore_ : used by convention to avoid conflicts with Python keyword, e.g. : tkinter . Toplevel ( master , class_ = 'ClassName' )
  • __double_leading_underscore : when naming a class attribute, invokes name mangling (inside class FooBar, __boo becomes _FooBar__boo ; see below).
  • __double_leading_and_trailing_underscore__ : “magic” objects or attributes that live in user-controlled namespaces. E.g. __init__ , __import__ or __file__ . Never invent such names; only use them as documented.

Prescriptive: Naming Conventions

Never use the characters ‘l’ (lowercase letter el), ‘O’ (uppercase letter oh), or ‘I’ (uppercase letter eye) as single character variable names.

In some fonts, these characters are indistinguishable from the numerals one and zero. When tempted to use ‘l’, use ‘L’ instead.

Identifiers used in the standard library must be ASCII compatible as described in the policy section of PEP 3131 .

Modules should have short, all-lowercase names. Underscores can be used in the module name if it improves readability. Python packages should also have short, all-lowercase names, although the use of underscores is discouraged.

When an extension module written in C or C++ has an accompanying Python module that provides a higher level (e.g. more object oriented) interface, the C/C++ module has a leading underscore (e.g. _socket ).

Class names should normally use the CapWords convention.

The naming convention for functions may be used instead in cases where the interface is documented and used primarily as a callable.

Note that there is a separate convention for builtin names: most builtin names are single words (or two words run together), with the CapWords convention used only for exception names and builtin constants.

Names of type variables introduced in PEP 484 should normally use CapWords preferring short names: T , AnyStr , Num . It is recommended to add suffixes _co or _contra to the variables used to declare covariant or contravariant behavior correspondingly:

Because exceptions should be classes, the class naming convention applies here. However, you should use the suffix “Error” on your exception names (if the exception actually is an error).

(Let’s hope that these variables are meant for use inside one module only.) The conventions are about the same as those for functions.

Modules that are designed for use via from M import * should use the __all__ mechanism to prevent exporting globals, or use the older convention of prefixing such globals with an underscore (which you might want to do to indicate these globals are “module non-public”).

Function names should be lowercase, with words separated by underscores as necessary to improve readability.

Variable names follow the same convention as function names.

mixedCase is allowed only in contexts where that’s already the prevailing style (e.g. threading.py), to retain backwards compatibility.

Always use self for the first argument to instance methods.

Always use cls for the first argument to class methods.

If a function argument’s name clashes with a reserved keyword, it is generally better to append a single trailing underscore rather than use an abbreviation or spelling corruption. Thus class_ is better than clss . (Perhaps better is to avoid such clashes by using a synonym.)

Use the function naming rules: lowercase with words separated by underscores as necessary to improve readability.

Use one leading underscore only for non-public methods and instance variables.

To avoid name clashes with subclasses, use two leading underscores to invoke Python’s name mangling rules.

Python mangles these names with the class name: if class Foo has an attribute named __a , it cannot be accessed by Foo.__a . (An insistent user could still gain access by calling Foo._Foo__a .) Generally, double leading underscores should be used only to avoid name conflicts with attributes in classes designed to be subclassed.

Note: there is some controversy about the use of __names (see below).

Constants are usually defined on a module level and written in all capital letters with underscores separating words. Examples include MAX_OVERFLOW and TOTAL .

Always decide whether a class’s methods and instance variables (collectively: “attributes”) should be public or non-public. If in doubt, choose non-public; it’s easier to make it public later than to make a public attribute non-public.

Public attributes are those that you expect unrelated clients of your class to use, with your commitment to avoid backwards incompatible changes. Non-public attributes are those that are not intended to be used by third parties; you make no guarantees that non-public attributes won’t change or even be removed.

We don’t use the term “private” here, since no attribute is really private in Python (without a generally unnecessary amount of work).

Another category of attributes are those that are part of the “subclass API” (often called “protected” in other languages). Some classes are designed to be inherited from, either to extend or modify aspects of the class’s behavior. When designing such a class, take care to make explicit decisions about which attributes are public, which are part of the subclass API, and which are truly only to be used by your base class.

With this in mind, here are the Pythonic guidelines:

  • Public attributes should have no leading underscores.

Note 1: See the argument name recommendation above for class methods.

Note 1: Try to keep the functional behavior side-effect free, although side-effects such as caching are generally fine.

Note 2: Avoid using properties for computationally expensive operations; the attribute notation makes the caller believe that access is (relatively) cheap.

Note 1: Note that only the simple class name is used in the mangled name, so if a subclass chooses both the same class name and attribute name, you can still get name collisions.

Note 2: Name mangling can make certain uses, such as debugging and __getattr__() , less convenient. However the name mangling algorithm is well documented and easy to perform manually.

Note 3: Not everyone likes name mangling. Try to balance the need to avoid accidental name clashes with potential use by advanced callers.

Any backwards compatibility guarantees apply only to public interfaces. Accordingly, it is important that users be able to clearly distinguish between public and internal interfaces.

Documented interfaces are considered public, unless the documentation explicitly declares them to be provisional or internal interfaces exempt from the usual backwards compatibility guarantees. All undocumented interfaces should be assumed to be internal.

To better support introspection, modules should explicitly declare the names in their public API using the __all__ attribute. Setting __all__ to an empty list indicates that the module has no public API.

Even with __all__ set appropriately, internal interfaces (packages, modules, classes, functions, attributes or other names) should still be prefixed with a single leading underscore.

An interface is also considered internal if any containing namespace (package, module or class) is considered internal.

Imported names should always be considered an implementation detail. Other modules must not rely on indirect access to such imported names unless they are an explicitly documented part of the containing module’s API, such as os.path or a package’s __init__ module that exposes functionality from submodules.

Programming Recommendations

For example, do not rely on CPython’s efficient implementation of in-place string concatenation for statements in the form a += b or a = a + b . This optimization is fragile even in CPython (it only works for some types) and isn’t present at all in implementations that don’t use refcounting. In performance sensitive parts of the library, the ''.join() form should be used instead. This will ensure that concatenation occurs in linear time across various implementations.

Also, beware of writing if x when you really mean if x is not None – e.g. when testing whether a variable or argument that defaults to None was set to some other value. The other value might have a type (such as a container) that could be false in a boolean context!

  • Use is not operator rather than not ... is . While both expressions are functionally identical, the former is more readable and preferred: # Correct: if foo is not None : # Wrong: if not foo is None :

To minimize the effort involved, the functools.total_ordering() decorator provides a tool to generate missing comparison methods.

PEP 207 indicates that reflexivity rules are assumed by Python. Thus, the interpreter may swap y > x with x < y , y >= x with x <= y , and may swap the arguments of x == y and x != y . The sort() and min() operations are guaranteed to use the < operator and the max() function uses the > operator. However, it is best to implement all six operations so that confusion doesn’t arise in other contexts.

The first form means that the name of the resulting function object is specifically ‘f’ instead of the generic ‘<lambda>’. This is more useful for tracebacks and string representations in general. The use of the assignment statement eliminates the sole benefit a lambda expression can offer over an explicit def statement (i.e. that it can be embedded inside a larger expression)

Design exception hierarchies based on the distinctions that code catching the exceptions is likely to need, rather than the locations where the exceptions are raised. Aim to answer the question “What went wrong?” programmatically, rather than only stating that “A problem occurred” (see PEP 3151 for an example of this lesson being learned for the builtin exception hierarchy)

Class naming conventions apply here, although you should add the suffix “Error” to your exception classes if the exception is an error. Non-error exceptions that are used for non-local flow control or other forms of signaling need no special suffix.

When deliberately replacing an inner exception (using raise X from None ), ensure that relevant details are transferred to the new exception (such as preserving the attribute name when converting KeyError to AttributeError, or embedding the text of the original exception in the new exception message).

A bare except: clause will catch SystemExit and KeyboardInterrupt exceptions, making it harder to interrupt a program with Control-C, and can disguise other problems. If you want to catch all exceptions that signal program errors, use except Exception: (bare except is equivalent to except BaseException: ).

A good rule of thumb is to limit use of bare ‘except’ clauses to two cases:

  • If the exception handler will be printing out or logging the traceback; at least the user will be aware that an error has occurred.
  • If the code needs to do some cleanup work, but then lets the exception propagate upwards with raise . try...finally can be a better way to handle this case.
  • When catching operating system errors, prefer the explicit exception hierarchy introduced in Python 3.3 over introspection of errno values.
  • Additionally, for all try/except clauses, limit the try clause to the absolute minimum amount of code necessary. Again, this avoids masking bugs: # Correct: try : value = collection [ key ] except KeyError : return key_not_found ( key ) else : return handle_value ( value ) # Wrong: try : # Too broad! return handle_value ( collection [ key ]) except KeyError : # Will also catch KeyError raised by handle_value() return key_not_found ( key )
  • When a resource is local to a particular section of code, use a with statement to ensure it is cleaned up promptly and reliably after use. A try/finally statement is also acceptable.

The latter example doesn’t provide any information to indicate that the __enter__ and __exit__ methods are doing something other than closing the connection after a transaction. Being explicit is important in this case.

  • Be consistent in return statements. Either all return statements in a function should return an expression, or none of them should. If any return statement returns an expression, any return statements where no value is returned should explicitly state this as return None , and an explicit return statement should be present at the end of the function (if reachable): # Correct: def foo ( x ): if x >= 0 : return math . sqrt ( x ) else : return None def bar ( x ): if x < 0 : return None return math . sqrt ( x ) # Wrong: def foo ( x ): if x >= 0 : return math . sqrt ( x ) def bar ( x ): if x < 0 : return return math . sqrt ( x )

startswith() and endswith() are cleaner and less error prone:

  • Object type comparisons should always use isinstance() instead of comparing types directly: # Correct: if isinstance ( obj , int ): # Wrong: if type ( obj ) is type ( 1 ):
  • For sequences, (strings, lists, tuples), use the fact that empty sequences are false: # Correct: if not seq : if seq : # Wrong: if len ( seq ): if not len ( seq ):
  • Don’t write string literals that rely on significant trailing whitespace. Such trailing whitespace is visually indistinguishable and some editors (or more recently, reindent.py) will trim them.
  • Use of the flow control statements return / break / continue within the finally suite of a try...finally , where the flow control statement would jump outside the finally suite, is discouraged. This is because such statements will implicitly cancel any active exception that is propagating through the finally suite: # Wrong: def foo (): try : 1 / 0 finally : return 42

With the acceptance of PEP 484 , the style rules for function annotations have changed.

  • Function annotations should use PEP 484 syntax (there are some formatting recommendations for annotations in the previous section).
  • The experimentation with annotation styles that was recommended previously in this PEP is no longer encouraged.
  • However, outside the stdlib, experiments within the rules of PEP 484 are now encouraged. For example, marking up a large third party library or application with PEP 484 style type annotations, reviewing how easy it was to add those annotations, and observing whether their presence increases code understandability.
  • The Python standard library should be conservative in adopting such annotations, but their use is allowed for new code and for big refactorings.

near the top of the file; this tells type checkers to ignore all annotations. (More fine-grained ways of disabling complaints from type checkers can be found in PEP 484 .)

  • Like linters, type checkers are optional, separate tools. Python interpreters by default should not issue any messages due to type checking and should not alter their behavior based on annotations.
  • Users who don’t want to use type checkers are free to ignore them. However, it is expected that users of third party library packages may want to run type checkers over those packages. For this purpose PEP 484 recommends the use of stub files: .pyi files that are read by the type checker in preference of the corresponding .py files. Stub files can be distributed with a library, or separately (with the library author’s permission) through the typeshed repo [5] .

PEP 526 introduced variable annotations. The style recommendations for them are similar to those on function annotations described above:

  • Annotations for module level variables, class and instance variables, and local variables should have a single space after the colon.
  • There should be no space before the colon.
  • If an assignment has a right hand side, then the equality sign should have exactly one space on both sides: # Correct: code : int class Point : coords : Tuple [ int , int ] label : str = '<unknown>' # Wrong: code : int # No space after colon code : int # Space before colon class Test : result : int = 0 # No spaces around equality sign
  • Although the PEP 526 is accepted for Python 3.6, the variable annotation syntax is the preferred syntax for stub files on all versions of Python (see PEP 484 for details).

This document has been placed in the public domain.

Source: https://github.com/python/peps/blob/main/peps/pep-0008.rst

Last modified: 2024-07-24 19:02:17 GMT

  • Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers
  • Advertising & Talent Reach devs & technologists worldwide about your product, service or employer brand
  • OverflowAI GenAI features for Teams
  • OverflowAPI Train & fine-tune LLMs
  • Labs The future of collective knowledge sharing
  • About the company Visit the blog

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.

Is there assign a dunder method a custom operator?

So I was wondering is it possible to make a new dunder method that has its own operator? Something like this:

Thanks in Advance!

**EDIT: ** Thank you @user2357112-supports-Monica and @Mad-Physicist for explaining to me what I wanted to do is impossible (without recompiling Python that is...) and how to do my original example.

  • customization
  • user-defined

RhinoCodes's user avatar

  • 2 There's already a method for @ . It's __matmul__ , because @ is intended for matrix multiplication. –  user2357112 Commented Sep 25, 2019 at 0:27
  • You can't add your own operator, but you can implement the defined ones as you choose. What's your specific question? –  Mad Physicist Commented Sep 25, 2019 at 0:29
  • what are you trying to accomplish with a custom dunder method? –  Mason Caiby Commented Sep 25, 2019 at 0:36
  • Well it was to make a custom operator but now I know I cant do that –  RhinoCodes Commented Sep 25, 2019 at 0:36

Know someone who can answer? Share a link to this question via email , Twitter , or Facebook .

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 .

Browse other questions tagged python operators customization user-defined or ask your own question .

  • The Overflow Blog
  • Mobile Observability: monitoring performance through cracked screens, old...
  • Featured on Meta
  • Announcing a change to the data-dump process
  • Bringing clarity to status tag usage on meta sites
  • What does a new user need in a homepage experience on Stack Overflow?
  • Feedback requested: How do you use tag hover descriptions for curating and do...
  • Staging Ground Reviewer Motivation

Hot Network Questions

  • Is it illegal to use a fake state ID to enter a private establishment even when a legitimate ID would've been fine?
  • What does this translated phrase convey "The heart refuses to obey."?
  • What story starts off with the character waking up in a battlefield with wolves and vultures and snow?
  • Why does Jenny hug Cindy at the end of "Commando" (1985)?
  • When a star becomes a black hole do the neutrons that are squeezed together release binding energy and if so does this energy escape from the hole?
  • How do I get GUI wokring for Ubuntu linux, How do I go about " searching your distribtion's package database for the file missing"?
  • Expensive constructors. Should they exist? Should they be replaced?
  • How make this table? And which package more helpful for these tables
  • How many ways can you make change?
  • Can an international student email a professor at a foreign university for an internship opportunity?
  • How to disavow backlinks from the Bing group of websites?
  • Strange current shape in joule thief
  • Coding exercise to represent an integer as words using python
  • Journal keeps messing with my proof
  • Is it valid to replace the limit of a function inside the indifnite integration when computing limits?
  • Sum of a Series whose difference is an Arithmetic Progression
  • What is the relation between rope length, object weight and force needed?
  • Doesn't counting hole and electron current lead to double-counting of actual current?
  • Is a company liable for "potential" harms?
  • Whats the safest way to store a password in database?
  • Functor composition rule necessary?
  • How specific does the GDPR require you to be when providing personal information to the police?
  • If you switch to a non-orthogonal basis, are vectors that were previously orthogonal still orthogonal?
  • How do I backup only changed files on an external hard drive?

python assignment dunder

IMAGES

  1. Dunder Data Python Challenge #1

    python assignment dunder

  2. PYTHON TUTORIAL: DUNDER METHOD|MAGIC METHOD|USE OF DUNDER METHOD WITH

    python assignment dunder

  3. Python Dunder Methods

    python assignment dunder

  4. 5 Python Magic Methods or Dunder Methods to Know

    python assignment dunder

  5. 5 Python Magic Methods or Dunder Methods to Know

    python assignment dunder

  6. What are dunder methods in Python?

    python assignment dunder

VIDEO

  1. Assignment

  2. dunders

  3. Python

  4. Python

  5. 23. Python Object-Oriented Programming, Dunder Method: rich comparison methods

  6. Object Orientated Programming in Python

COMMENTS

  1. Is it possible to override the assignment ('=') operator in Python?

    I think this would violate Python's object model or variable/naming scheme. A name does not represent one object only, but just points to an object under the hood. Using the assignment operator just changes the object a name points to. - SethMMorton Aug 22, 2014 at 22:32

  2. Every dunder method in Python

    Note that the Python documentation refers to these as special methods and notes the synonym "magic method" but very rarely uses the term "dunder method". However, "dunder method" is a fairly common Python colloquialism, as noted in my unofficial Python glossary.

  3. Python's Assignment Operator: Write Robust Assignments

    In this tutorial, you'll learn how to use Python's assignment operators to write assignment statements that allow you to create, initialize, and update variables in your code.

  4. Python's Magic Methods: Leverage Their Power in Your Classes

    As a Python developer who wants to harness the power of object-oriented programming, you'll love to learn how to customize your classes using special methods, also known as magic methods or dunder methods. A special method is a method whose name starts and ends with a double underscore. These methods have special meanings for Python.

  5. Assignment Operators in Python

    The Python Operators are used to perform operations on values and variables. These are the special symbols that carry out arithmetic, logical, and bitwise computations. The value the operator operates on is known as the Operand. Here, we will cover Different Assignment operators in Python.

  6. Overloading arithmetic operators with dunder methods

    Introduction Python lets you override the arithmetic operators like + for addition or * for multiplication through dunder methods . Dunder methods are special methods whose name starts and ends with a double underscore (hence, "dunder"), and some dunder methods are specific to arithmetic operations.

  7. Python In-Place Assignment Operators

    Python's in-place division operator x /= y divides two objects in-place by calculating x / y and assigning the result to the first operands variable name x. Set up in-place division for your own class by overriding the magic "dunder" method __truediv__(self, other) in your class definition.

  8. Pythonic OOP: Properties and Dunder Methods

    Many languages have OOP features, but Python has some unique OOP features, including properties and dunder methods. Learning how to use these Pythonic techniques can help you write concise and readable code.

  9. 5 Python Magic Methods or Dunder Methods to Know

    Python magic methods, also called dunder methods, are necessary to understand the programming language. Here's a guide to getting started with them.

  10. PEP 572

    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.

  11. Python Assignment Operator: Overload, List, Precedence

    In Python, we can overload assignment operators using special methods called 'magic methods' or 'dunder methods' (double underscore methods). These methods have specific names and functionalities that make operator overloading possible.

  12. operator

    Mapping Operators to Functions ¶ This table shows how abstract operations correspond to operator symbols in the Python syntax and the functions in the operator module.

  13. Operator and Function Overloading in Custom Python Classes

    How to overload built-in functions and operators in your custom Python classes in order to make your code more Pythonic.

  14. 3. Data model

    3.1. Objects, values and types ¶ Objects are Python's abstraction for data. All data in a Python program is represented by objects or by relations between objects. (In a sense, and in conformance to Von Neumann's model of a "stored program computer", code is also represented by objects.)

  15. Python Operators: Arithmetic, Assignment, Comparison, Logical, Identity

    Python Operators: Arithmetic, Assignment, Comparison, Logical, Identity, Membership, Bitwise Operators are special symbols that perform some operation on operands and returns the result. For example, 5 + 6 is an expression where + is an operator that performs arithmetic add operation on numeric left operand 5 and the right side operand 6 and returns a sum of two operands as a result.

  16. A guide to python's dunder methods : r/learnpython

    What are dunder methods Dunder (or "Magic") methods are special class, or instance methods that python looks for to implement core operations of the language, or results of built-in functions, operations, and even some modules of the standard library's functions.

  17. PEP 8

    This document gives coding conventions for the Python code comprising the standard library in the main Python distribution. Please see the companion informational PEP describing style guidelines for the C code in the C implementation of Python.

  18. EECS 201

    layout: true <div class=bot-bar> Python </div> --- class: center, middle # Python #### `import tensorflow as tf` --- # Overview * High level scripting * What is ...

  19. How to make python class support item assignment?

    3 I realize this is an old post, but I was looking for some details around item assignment and stumbled upon the answers here. Ted's post wasn't completely wrong. To avoid inheritance from dict, you can make a class inherit from MutableMapping, and then provide methods for __setitem__ and __getitem__.

  20. Data Science Analyst- On-Site at Mayo Clinic

    Knowledge and experience in multiple programming and domain-specific languages (we primarily use Python, R, SQL, and Linux/Unix scripting) ... Weekend Schedule As needed International Assignment No Site Description. Just as our reputation has spread beyond our Minnesota roots, so have our locations. Today, our employees are located at our three ...

  21. python

    I know what Python dunder variables are, and I'm aware of name mangling. But for some reason I can't access the dunder variable in the following code snippet: for node in ast.find_all((Call,...

  22. python

    Is there assign a dunder method a custom operator? Asked 4 years, 11 months ago Modified 4 years, 4 months ago Viewed 394 times