In this tutorial, you’ll learn about all the **operators in Python**. Instead of just learning their basic usage, what you might already know, you’ll achieve a **broader understanding **of how they behave behind the scene and what’s possible to do with them.

As of Python 3.9, operators in Python can be divided into the following categories:

- Power Operator:
****** - Unary Arithmetic:
**–**,**+**,**~** - Binary Arithmetic:
*****,**@**,**/**,**//**,**%**,**+**,**–** - Shifting Operators:
**<<**,**>>** - Binary Bitwise Operators:
**&**,**^**,**|** - Comparisions:
- Value Comparisons:
**<**,**>**,**==**,**>=**,**<=**,**!=** - Membership Test:
**in**,**not in** - Identity Comparisons:
**is**,**is not**

- Value Comparisons:
- Boolean Operators:
**or**,**and**,**not** - Walrus Operator:
**:=** - Augmented Assignment Operators

Notice that the categories have been arranged according to their precedence. The precedence of the operators

Operators Precedence Documentationdecreases from top to bottombutincreases from left to rightin the same category. Except for theBinary Arithmeticwhere the+, –operators have less priority than the rest of the same category.

Before we get started, let’s see some useful definitions.

### What are Operators?

An **Operator** is a special symbol that indicates to a specific operation or computation. For example, the plus symbol (**+**) indicates to the addition operation.

### What is an Operand?

An **Operand** is a data on which an operator operates. Some operator require one operand whereas some require two. For example, **x + y**. Here **x** and **y** are two **operands**.

### What is an Expression?

An **Expression** is a combination of some Operators and Operands that evaluates to a single value. For example, **5 + 3** is an expression, and evaluates to **8**.

Now let’s dive into each of the operators. If you’ve already read the previous tutorial on Python Numbers, Formatting Number, and math Module: A Complete Guide, where I’ve already covered two types of operators, don’t worry! This tutorial will add some extra spice on top of that. So let’s get started!

## Power Operator: **

The syntax of the power operator is **a**b**, which returns **a ^{b}**. The power operator is more powerful than the unary operators (known as the sign of a number) on the left. That means, -1**2 evaluates to -(1

^{2}), and not (-1)

^{2}as you may expect.

Operators in Python generally evaluate from **left to right**. But the power operator evaluates from **right to left**. For example, 2**3**4 equals to **2**(3**4)**, what we actually do in math.

Here are some examples:

```
>>> 3**2
9
>>> -1**2
-1
>>> -(1**2)
-1
>>> 2**3**4
2417851639229258349412352
>>> (2**3)**4
4096
>>> 2**(3**4)
2417851639229258349412352
>>> 2**-2
0.25
```

The power operator works the same as the built-in **pow( )** function, when called with 2 arguments. That means, **pow(2,3) == 2**3 == 2 ^{3}**

The numeric arguments of the power operator are **first converted into a common type**, and the result is returned in the same type. It turns out that if at least one argument is a **float**, then all are converted into floats. And if at least one argument is a **complex**, then all are converted into complexes.

An alternate happens when both arguments are **ints**, but the second one is a negative number. In that case, all are converted into **floats** and the returned value is also in **float**.

Raising 0.0 to a negative number results in a **ZeroDivisionError**. Raising a negative number to a fractional number results in a complex number. Both of these are common in mathematics.

```
>>> -10**0.5 # The power operator has higher precedence than unary sign
-3.1622776601683795
>>> (-10)**0.5
(1.9363366072701937e-16+3.1622776601683795j)
```

## Unary Arithmetic and Bitwise Operation: -, +, ~

- The
**unary minus**(**–**) operator represents the**negation**of a number. For example, -5, -10 - The
**unary plus**(**+**) operator yields its numeric argument unchanged. It’s generally not used. Example: +5, +10. Generally, you’ll always use 5, 10 instead. - The
**unary invert**(**~**) operator calculates the bitwise inversion of its integer argument. The bitwise inversion of a number**X**is defined as**-(X+1)**. Note that the argument**X**must be an integer number.

```
>>> ~0 # -(0+1)
-1
>>> ~1 # -(1+1)
-2
>>> ~2 # -(2+1)
-3
>>> ~-1 # -(-1+1)
0
>>> ~-2 # -(-2+1)
1
```

All unary arithmetics have the same priority. But when represented together, the priority **increases from left to right**. That means, **“-“** < **“+”** < **“~”**

```
>>> 2+-2 # (2)+(-2)
0
>>> -2+-2 # (-2)+(-2)
-4
```

The plus sign between two arguments is not a unary plus operator. That’s a binary plus operator, which adds two numbers.

## Binary Arithmetic Operations: *, @, /, //, %, +, –

The operators in Python that perform arithmetic operations on two arguments are called **Binary Arithmetic Operators**. They have the conventional priority level. When used together, priority **increases from left to right**.

The multiplication (

*) and the addition (+) operators work not only with numbers, but also with sequence types.

### Multiplication Operator: “*”

The multiplication ( ***** ) operator returns the product of its arguments. Depending on the type of the arguments, the operations can differ.

- When both the arguments are
**numbers**: Both arguments are first converted into a common type, and then multiplied together. - When one argument is an
**integer**, the other one is a**sequence**: The sequence repetition happens. If the integer is a negative number, an**empty sequence**is returned.

The conversion of common types of numbers is always the same. If at least one is a complex, then all are converted into complex numbers. If at least one is a float, then all are converted into floats. The priority being **complex** **> float > int**

Example 1:

```
>>> 123 * 321
39483
>>> 123 * 32.19
3959.37
>>> 123 * (3+2j)
(369+246j)
>>> 12.3 * 3 * 2j
73.80000000000001j
```

Example 2:

```
>>> 'abc' * 2
'abcabc'
>>> 2 * 'abc'
'abcabc'
>>> 2 * 'abc' * 2
'abcabcabcabc'
>>> [1,2,3] * 2
[1, 2, 3, 1, 2, 3]
>>> b'abc' * 2 # byte string
b'abcabc'
```

### Matrix Multiplication Operator: “@”

The **at** (**@**) operator is used to perform matrix multiplication. But no built-in type has support for this operator, as no built-in type works with matrix multiplication. The third-party or external numeric libraries can have support for this. For example, the **NumPy** module deals with matrix multiplication. Hence, the **NumPy** module supports the **at** operator.

The **numpy** module comes by default with the **anaconda** distribution.

### Division Operator: “/”

The division operator returns the **quotient** of their arguments. In the syntax **a/b**, **a** is divided by **b**. The arguments are first converted into a common type and then the operator is applied. But the **division of two integers is always a float**, even there is no decimal point. Note that if **b==0**, then a **ZeroDivisionError** is raised. You’ll see people calling the division operator as **float division operator**, but it can also return the result in a **complex** number!

```
>>> 10/2
5.0
>>> 10/2.5
4.0
>>> 10.6/2.3
4.608695652173913
>>> (2+3j)/(4+5j)
(0.5609756097560976+0.0487804878048781j)
```

### Floor Division Operator: “//”

The floor division operator (//) is kind of a combination of the division operator and the floor function. It first divides the number, and then rounds down the result. As always, the arguments are converted to a common type, and then the operator is applied. Note that, **Python floor division operator does not support complex numbers**. Remember, it’s **floor division** and not **integer division** like many websites name it!

```
>>> 10//2
5
>>> 10//2.0
5.0
>>> 10//2.5
4.0
>>> 10.6//2.3
4.0
>>> (2+3j)//(4+5j)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't take floor of complex number.
>>> 10//(2+3j)
```

**Floor division of negative number**s

The floor division can be confusing if applied on negative numbers. For example:

```
>>> 9//2
4
>>> -9//2
-5
```

Notice that the behavior is as expected. Because, -9/2 = -4.5. Now rounding it to -4 would be rounding it up, and not down. Because -4 > -4.5.

### Modulo Operator: “%”

The modulo operator returns the remainder of a division operation. The syntax is **a%b**. If **b==0**, a **ZeroDivisionError** is raised. Both of the numeric arguments are first converted into a common type and then the operator is applied.

```
>>> 10%3
1
>>> 3.14%0.7
0.3400000000000003
```

It’s not exactly 0.34 because of Floating Point Arithmetic: Issues and Limitations.

**The modulo operator always returns the result with the same sign as the second argument**. That means, if the second argument is a negative number, the modulo operator must return a negative remainder.

```
>>> -10%3
2
>>> 10%-3
-2
>>> -10%-3
-1
```

Let me explain the second one about how it works.

```
>>> 10%-3
-2
>>> 10//-3
-4
>>> (-3*-4)+(-2)
10
```

If you’re working with the

For more: Modulo operation on a python negative decimal.Decimal and a positive intdecimalmodule, be conscious about the fact that it takes theceilin the floor division (//) (instead offloor) and yields a result with the same sign as itsfirstoperand (instead of thesecond) in modulo operation.

Both the floor division and the modulo operation are not applicable to complex numbers. But if you need, first convert the complex number into its absolute value using the

abs( )function.

### Binary Addition Operator: “+”

When a plus sign (+) is used between two operands, it performs Binary Addition. It yields the summation of the arguments. **The binary addition power has less priority than the unary plus and minus operators**.

- Both arguments can be numbers. In this case, both the arguments are first converted into a common type and then added together.

```
>>> 2+2
4
>>> 2+-2 # (+2)+(-2) as unary operator has higher priority
0
>>> -2+-2
-4
```

- Both the arguments can be any sequence type. In this case, the sequences are added together.

```
>>> 'Hello ' + 'World!'
'Hello World!'
>>> [1,2,3] + [4,5,6]
[1, 2, 3, 4, 5, 6]
>>> b'abc' + b'def'
b'abcdef'
```

- A combination of number and sequence type, or different types is not allowed.

```
>>> 'MLwiki' + 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str
>>> [2,3,4] + 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "int") to list
>>> 'MLwiki' + [2,3,4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "list") to str
```

### Binary Subtraction Operator: “-“

When a minus sign (+) is used between two operands, it performs Binary subtraction. It yields the difference between the arguments. **The binary subtraction power has less priority than the unary plus and minus operators**.

The arguments of the binary subtraction operator ( **–** ) must always be numbers. The arguments are first converted into a common type.

```
>>> 2-2
0
>>> 2--2
4
>>> -2+-2 # No binary subtraction operator here
-4
```

###### Join Facebook Group

**Public Group**

A group optimized for learning purposes. Track your progress by joining a unit

###### Follow on Quora

**Public Space**

Best place to ask questions! Join the built-in community to get help from others

## Shifting Operations: <<, >>

Shifting operations are applicable to **binary** numbers. As numbers are stored in binary format, you can apply shifting operations on any number base. But only **integers (Binary, Octal, Decimal, and Hexadecimal)** are supported.

**Shifting are of 2 types: right shift and left shift**. Binary numbers are stored in memory as bytes. **1 byte equals 8 bits**. One bit contains either 0 or 1. Say, the number 42 is stored in a 1-byte memory. It would look like this:

Right shifting it by 2 bits means shifting the entire number to the right by 2 bits. In this case, the rightmost 2 bits will get dropped as you can’t add new bits to the right. Because that will change the base values for the entire representation.

Left shifting by 2 bits means shifting the entire number to the left by 2 bits. Note that in both cases, **empty bits are filled with zeros**.

Operators in Python includes right shift and left shift operations. The syntax is as follows:

```
Right Shift: number >> bits
Left Shift: number << bits
```

Shifting can be defined as a power of 2. So right shifting **x** by **n** bits means **x // 2 ^{n}** or

**x // pow(2,n)**, whereas left shifting means

**x * 2**or

^{n}**x * pow(2,n)**.

```
>>> 42>>2
10
>>> 42//pow(2,2)
10
>>>
>>> 42<<2
168
>>> 42*pow(2,2)
168
>>>
>>> 0b101100101 >> 3
44
>>> 0b101100101 << 10
365568
```

## Binary Bitwise Operations: &, ^, |

Binary bitwise operations include **bitwise AND (&)**, **bitwise X-OR (^)**, and **bitwise OR (|)**. Their priority **increases** **from left to right**. All three of the operations **only support integer numbers**. Bitwise operations are generally used to create complex logic gates in circuits. The numbers are first converted into binary format and then the operators are applied. Bitwise operations are applied to two identical bits at once.

### Bitwise AND: “&”

**The bitwise AND operation returns 1 if both the arguments are 1, otherwise returns 0. **

Here is the truth table for binary bitwise AND operations:

Argument 1 | Argument 2 | Result |
---|---|---|

0 | 0 | 0 |

0 | 1 | 0 |

1 | 0 | 0 |

1 | 1 | 1 |

Here are some examples:

```
>>> 0&0
0
>>> 0&1
0
>>> 1&0
0
>>> 1&1
1
```

When the binary bitwise operation is applied on two integer numbers, they are first converted into binary numbers of 8 bits or 16 bits or anything else depending on the program. Then the bitwise operation is applied on each pair of identical bits. Finally, the resultant is converted back into a decimal by default. Here’s how it works. Let’s say the numbers are converted into 8 bits of binary.

In Python:

```
>>> bin(13)
'0b1101'
>>> bin(31)
'0b11111'
>>> 13&31
13
>>> format(13&31, 'b')
'1101'
>>> int('1101', base=2)
13
```

You can compute multiple bitwise **AND** operations at once. Just keep in mind, they are computed **from left to right**. For example, **13&31&131** equals **((13&31) & 131)**.

```
>>> 13&31&131
1
>>> 13&31
13
>>> 13&131 # This 13 is the previous result
1
>>> ((13&31)&131)
1
```

### Bitwise X-OR (Exclusive OR): “^”

**Bitwise X-OR operation returns 0 if both the arguments are identical (either both 1 or both 0), otherwise 1. **

Here is the truth table for binary bitwise **X-OR** operations:

Argument 1 | Argument 2 | Result |
---|---|---|

0 | 0 | 0 |

0 | 1 | 1 |

1 | 0 | 1 |

1 | 1 | 0 |

Here are the examples:

```
>>> 0^0
0
>>> 0^1
1
>>> 1^0
1
>>> 1^1
0
```

The binary bitwise **X-OR** operations are computed the same way as before. So let’s jump to the examples.

In Python:

```
>>> bin(13)
'0b1101'
>>> bin(31)
'0b11111'
>>> 13^31
18
>>> format(13^31, 'b')
'10010'
>>> int('10010', base=2)
18
```

Computing multiple **X-OR **operations at once is the same as before. For example, **13^31^131** equals **((13^31) ^ 131)**.

```
>>> 13^31^131
145
>>> 13^31
18
>>> 18^131
145
>>> ((13^31)^(131))
145
```

### Bitwise OR: “|”

**The Bitwise OR operation returns 1 if any of the arguments is 1, otherwise returns 0.**

Here is the truth table for binary bitwise **OR** operations:

Argument 1 | Argument 2 | Result |
---|---|---|

0 | 0 | 0 |

0 | 1 | 1 |

1 | 0 | 1 |

1 | 1 | 1 |

Here are the examples:

```
>>> 0|0
0
>>> 0|1
1
>>> 1|0
1
>>> 1|1
1
```

The binary bitwise **OR** operations are computed the same way as before. So let’s jump to the examples.

In Python:

```
>>> bin(13)
'0b1101'
>>> bin(31)
'0b11111'
>>> 13|31
31
>>> format(13|31, 'b')
'11111'
>>> int('11111', base=2)
31
```

Computing multiple **OR** operations at once is the same as before. For example, **13|31|131** equals **((13|31) | 131)**.

```
>>> 13|31|131
159
>>> 13|31
31
>>> 31|131
159
>>> ((13|31)|131)
159
```

### All Bitwise Operators at Once

When all of the three bitwise operators are used at once, the operations follow the priority: **& > ^ > |**

Notice the examples carefully:

```
>>> 13&31^131|313 # Equals ((13&31)^131)|313
447
>>> 13&31
13
>>> 13^131
142
>>> 142|313
447
>>>
>>> 13^31|131&313|113 # Equals (13^31)|(131&313)|113
115
>>> (131&313)
1
>>> 13^31
18
>>> 18|1|113
115
```

## Comparison Operators

The **comparison operators** in Python compares the first argument with respect to the second one and returns either **True** or **False**. Expressions like **a < b < c** have the same interpretation that is conventional in Mathematics. That means, **comparisons can be chained arbitrarily**. For example, the expression **x < y <= z** is equivalent to the expression **x < y and y <= z**, except that the former expression evaluates **y** only once while the latter evaluates twice. But in both cases, the latter part (**y<=z**) won’t be evaluated if **x < y** returns **False**. Because that becomes unnecessary. This behavior is known as the **short circuit evaluation**.

Comparison operators in Python can be categorized in 3 different sections.

### Value Comparisons: <, >, ==, >=, <=, !=

The value comparison operators (**<**, **>**, **==**, **>=**, **<=**, **!=**) compare the values of two objects. The objects do not need to have the same type.

Operator | Name | Example |
---|---|---|

< | Less than | x<y |

> | Greater than | x>y |

== | Equal | x==y |

>= | Greater than or equal | x>=y |

<= | Less than or equal | x<=y |

!= | not equal | x!=y |

```
>>> 123<321
True
>>> 123>321
False
>>> 0==0.0
True
>>> 123>=123
True
>>> 321<=123
False
>>> 123!=321
True
```

To get a deeper understanding, **value comparison** operators in Python can be further categorized:

**Order Comparison**:**<**,**>**,**>=**,**<=****Equality Comparison**:**==**,**!=**

Let’s describe the comparison behavior of the most important built-in types.

- Numbers of built-in types (Numeric Types: int, float, complex) and of standard library types
**fractions.Fraction**and**decimal.Decimal**can be compared within and across their types. One exception is that the**complex**numbers**do not**support Order Comparison.

```
>>> 12.34>=12
True
>>> 123!=0.34
True
>>> 2+3j < 3+4j
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'complex' and 'complex'
```

- The not-a-number values e.g.
**float(‘NaN’)**, or**decimal.Decimal(‘NaN’)**are special.**Any ordered comparison**of a number to a**NaN**value returns**False**. Even**NaN**values are not equal to themselves!

```
>>> import decimal
>>> a = float("NaN")
>>> b = decimal.Decimal('NaN')
>>> a;b
nan
Decimal('NaN')
>>> a>0
False
>>> a<=0
False
>>> a==b # Not equal to themselves
False
>>> a!=1 # True because they are not equal to themselves
True
>>> a!=b
True
```

- None and NotImplemented are singletons. Comparisons for singletons should always be done with
**is**or**is not**, and never the equality comparisons.

```
>>> a = None
>>> a
>>> a is None
True
```

- Binary sequences (
**bytes**and**bytearray**) can be compared within and across their types. They compare lexicographically (character by character) using the numeric values of their elements. That means the first element of each argument are compared. If they equal, then the corresponding second elements are compared.

```
>>> x = b'abc'
>>> y = b'bcd'
>>> x<y # a<b is True
True
>>> z = b'ab'
>>> x<z # a==a, b==b, but c>empty.
False
>>> a=b'abc'
>>> b=b'b'
>>> a<b
True
```

- Strings (
**str**) compare lexicographically (character by character) using the numerical Unicode of the characters. You can find the Unicode of a character using the built-in**ord( )**function.

```
>>> var1 = 'abcd'
>>> var2 = 'bbcd'
>>> ord('a')
97
>>> ord('b')
98
>>> var2>var1
True
>>> var3 = 'bba'
>>> var3>var1
True
>>> var3<var2
True
```

- Strings and binary sequences cannot be directly compared.

```
>>> b'abc' == 'abc'
False
>>> b'abc' <= 'abc'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<=' not supported between instances of 'bytes' and 'str'
>>>
>>> a = b'abc'
>>> b = bytes('abc', 'utf-8')
>>> id(a); id(b) # Checking the a and b are two different objects
139701914886672
139701919208720
>>> a==b
True
>>> a<=b
True
```

- Sequences (instances of
**list**,**tuple**, or**range**) can be compared ONLY within each of their own types. But**range**objects cannot be compared even within their own type.**Equality Comparison**across them results in inequality and**Order Comparison**across them raises**TypeError**.

```
>>> [1,2,3,4]>[1,2,3]
True
>>> (1,2,3,4)>(1,2,3)
True
>>> range(5)<range(10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'range' and 'range'
>>> (1,2,3) == (1,2,3) # equality comparison within type
True
>>> [1,2,3] == (1,2,3) # equality comparison across types
False
>>> [1,2,3,4]>[1,2,3] # Order comparison across types
True
>>> [1,2,3,4]>(1,2,3) # Order comparison within type
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'list' and 'tuple'
```

Sequences compare lexicographically using comparison of corresponding elements. Lexical comparison between built-in types works as follows:

- For two collections to compare equal (
**Equality Comparison**), they must be of the**same type**, have the**same length**, and each pair of the corresponding elements must compare equal.

```
>>> [1,2,3,4]==[1,2,3,4] # same type, length, each corresponding pair is equal
True
>>> [1,2,3,4]!=(1,2,3,4) # different types
True
>>> [1,2,3,4]!=[1,2,3] # different length
True
>>> [1,2,3,4]!=[4,3,2,1] # each corresponding pair is not equal
True
```

- Collections that support
**Order Comparison**are ordered the same as their first unequal elements. For example,**[1,2,x,a]<=[1,2,y,b]**has the same value as**x<=y**. A comparison between**a**and**b**is completely ignored. If a corresponding element does not exist, then the shorter collection is ordered first. For example,**[1,2,3]<[1,2]**is true.

```
>>> [1,2,3,100]<[1,2,4,5]
True
>>> [1,2,3,4] > [1,2,3]
True
>>> [1,2,3,4] < [2] # The first unequal
True
```

- Dictionaries or Mappings (instances of
**dict**) only supports**Equality Comparison**. They compare equal if and only if they have equal**(key, value)**pairs.**Order Comparison**of them raises**TypeError**.

```
>>> {'a':1, 'b':2, 'c':3} == {'a':1.0, 'b':10/5, 'c':2+1}
True
>>> {'a':1, 'b':2, 'c':3} <= {'a':1.0, 'b':10/5, 'c':2+1}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<=' not supported between instances of 'dict' and 'dict'
```

- Sets (instances of
**set**and**frozenset**) can be compared within and across their types. They work the same as sequences. But they define**Order Comparison**operators in python to mean**subset**and**superset**tests.

```
>>> {1,2}<={1,2}
True
>>> {1,2}<{1,2}
False
>>> {1,2,3}<{1,2}
False
>>> {1,2,3}>{1,2}
True
>>> {1,2,3}!={1,2}
True
>>> [1,2,3]=={1,2,3}
False
```

**Equality comparison on floating point numbers may show you unexpected results**. Because most of the floating point numbers cannot be represented precisely in the binary number system. That’s why it’s a better idea to use a threshold value when comparing two floating point numbers for equality. For more about this limitation, recall Floating Point Arithmetic: Issues and Limitations from the previous tutorial.

```
>>> x = 1.1 + 2.2
>>> x
3.3000000000000003
>>> x == 3.3
False
>>>
>>> # With a threshold or tolerance:
>>> tolerance = 0.00001 # Choice is yours
>>> x = 1.1 + 2.2
>>> abs(x-3.3) < tolerance
True
```

The built-in **abs( )** function returns the absolute value, which is the difference between the actual number and the expected number.

### Membership Test Operations: “in”, “not in”

The operators **in** and **not in** test for membership, i.e. whether an element is present within an object. **x in s** returns **True** if **x** is a member of **s**, **False** otherwise. The operator **not in** does the opposite.

All built-in sequences, set types, and dictionary support membership test operations.

- For sequences (
**list**,**tuple**), set types (**set**,**frozenset**), and other container types (such as**dict**,**collections.deque**), the**in**operator tests whether a value exists or not, whereas**not in**does the opposite.

```
>>> var1 = [1,2,3,4,5]
>>> 5 in var1
True
>>> 6 not in var1
True
>>>
>>> popular = {'Python', 'Java', 'C++'}
>>> 'Python' in popular
True
>>> 'Go' not in popular
True
```

- For dictionary,
**in**tests whether the dictionary has the given**key**.

```
>>> mlwiki = {'name':'MLwiki', 'admin':'Fahim', 'language':'Python'}
>>> 'admin' in mlwiki
True
>>> 'Python' in mlwiki # Python is not a key
False
>>> 'language' in mlwiki
True
```

- For
**string**and**byte**types,**x in s**returns**True**if and only if**x**is a substring of**s**. Note that**empty strings are always considered to be a substring of any other string.**So**” ” in “xyz”**will always result in**True**.

```
>>> 'w' in 'MLwiki'
True
>>> 'wiki' in 'MLwiki'
True
>>> 'mlwiki' in 'MLwiki'
False
>>> '' in 'MLwiki'
True
>>> ' ' in 'MLwiki' # ' ' is not empty, it has a whitespace
False
>>> 'wiki' in b'MLwiki' # 'wiki' is a string but b'MLwiki' is a byte string.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: a bytes-like object is required, not 'str'
>>> b'wiki' in b'MLwiki'
True
```

### Identity Comparisons: “is”, “is not”

The operators **is** and **is not** test for an object’s identity. The expression **x is y** returns **True** if and only if **x** and **y** have the **same identity**, i.e. they are the **same object**. Use the **id( )** function to check the identity of an object. **x is not y** returns **True** if they are different objects.

Be careful when using the identity comparison operators. People tend to make mistakes as they puzzle between **is** and **==** operators. **is** checks whether two objects are actually the same object, where **==** checks whether the values of two objects are equal. Notice the examples carefully.

```
>>> num1 = 1001
>>> num2 = 2000-999
>>> id(num1);id(num2)
139877275281936
139877254031280
>>> num1 == num2
True
>>> num1 is num2
False
```

When practicing this example, try to use a larger number. Because Python caches small numbers (-5 to 256) to increase the performance. Your terminal may cache a larger range of numbers.

## Boolean Operators: “or”, “and”, “not”

**Boolean operators** in Python work almost the same as the **binary bitwise operators**. Binary bitwise operators work for integers, whereas the Boolean operators work for Boolean values, **True** and **False**. Consider 1 as **True** and 0 as **False**, then the truth table will be the same. People tend to puzzle between these two types of operators, be careful.

Remember that the following values are interpreted as **False**. Everything else is **True**.

**False****None**- Numeric Zero of all types:
**0**,**0.0**,**0j**,**Decimal(0)**,**Fraction(0, 1)** - Empty strings and containers: strings, tuples, lists, dictionaries, sets and frozensets

### Boolean “or” Operator

The boolean **or** operator returns **True** if at least one of the arguments is **True**. It works from left to right and stops checking the arguments whenever a **True** value is found. That indicates to the **short-circuit evaluation** discussed earlier.

```
>>> True or False
True
>>> True or True
True
>>> False or False or False or True or False
True
>>> 1 or 0 # Non zero value = True
1
>>> 2 or 1 # Both are non-zero, so both are True
2
>>> 3 or 4
3
```

In practical, you’ll use the Boolean **or** operator as shown below:

```
>>> 10>3 or 3>10 # True or False = True
True
>>> 3>10 or 10>3 # False or True = True
True
>>> '' or 'MLwiki' # False or True = True
'MLwiki'
>>> 'ML' or 'Wiki' # True or True = True
'ML'
```

### Boolean “and” Operator

The boolean **and** operator returns **True** if all of the arguments are **True**. It works from left to right and stops checking the arguments whenever a **False** value is found, i.e. the short-circuit behavior discussed earlier.

```
>>> True and False
False
>>> True and True
True
>>> True or True or False or True or True
False
>>> 1 and 0 # True and False = False. Hence 0
0
>>> 2 and 1
1
>>> 3 and 4
4
>>> 4 and 3
3
```

If a **False** value is present, then **False** is returned. Otherwise, every **True** value is checked and the last **True** value is returned. For example, **2 and 1** returned **1**

In practical, you’ll use the Boolean **and** operator as shown below:

```
>>> 10>3 and 3>10 # True and False = False
False
>>> 3>10 and 10>3 # False and True = False
False
>>> '' and 'MLwiki' # False and True = False
''
>>> 'ML' and 'Wiki' # True and True = True
'Wiki'
```

### Boolean “not” operator

The boolean **not** operator changes **True** to **False** and vise versa.

```
>>> not True
False
>>> not False
True
>>> val = True
>>> val is True
True
>>> val is not False
True
>>> not 1
False
>>> not 0
True
>>> not 2
False
>>> not None
True
>>> not 10>3 # Not True
False
>>> not '' # Not False
True
```

Note that neither

andnororchanges the type and value of its argument toTrueorFalse. Rather they return the last evaluated value. That’s whyFalse or 3 or 4returns3. On the other hand,True and 3 and 4returns4. This characteristic is very useful in Python to set a default value.As the

notoperator needs to generate a new value, it always returns a Boolean value regardless of its argument.

Let’s see an example where the Boolean operators can help to get a default value. Notice that for **y=0**, **x/y** will raise a **ZeroDivisionError** and terminate the code execution.

```
# Bad approach
result = x/y
# Better approach
if y!= 0:
result = x/y
# Or even better
result = y!=0 and x/y
```

Another example with the **or** operator:

```
>>> Notification = 'Hello!'
>>> message = Notification or 'No Notification'
>>> message
'Hello!'
>>>
>>> Notification = ''
>>> message = Notification or 'No Notification'
>>> message
'No Notification'
```

## Walrus Operator/ Assignment Expression: “:=”

Walrus Operator, the most **controversial** Python feature! This feature has been added to Python 3.8 after a whole lot of drama! The majority of the Python core developers were against it, and the drama drove **Guido Van Rossum**, creator of Python, to step down from his leadership role of Benevolent Dictator for Life (**BDFL**) after he accepted the walrus operator as part of the PEP 572 proposal.

Although this feature is common in other programming languages, a lot of Python core developers said that it surely reduces the number of lines of code, but it **increases the complexity** and **decreases the readability**. Which is against PEP 20, The Zen of Python. Which says:

Simple is better than complex

PEP 20 — The Zen of Python

In my opinion, although this can be very complex depending on how you use it, the walrus operator can be very helpful in certain applications.

**So what is this walrus operator or the assignment expression? **

The walrus operator does 2 things at once:

- Assigns an expression to an identifier (e.g. variable)
- Returns the value of the expression

Notice the very basic difference between the assignment statement and the assignment expression:

```
>>> # Assignment statement
>>> # 1. assign 42 to x
>>> x = 42
>>> x
42
>>>
>>> # Assignment expression:
>>> # 1. Assigns 42 to x
>>> # 2. returns 42
>>> (x := 42)
42
>>> x
42
>>>
>>> # Another example
>>> (y := 2**3-4/6)
7.333333333333333
>>> y
7.333333333333333
```

You must enclose the assignment expression inside parentheses, otherwise it’ll raise an error,

SyntaxError: invalid syntax. But if you use the walrus operator inside another expression, such asifstatement,whileloop,forloop, then you can ignore the parenthesis. Notice the next two examples carefully.

So what are the **applications of the walrus operator** or the assignment expression?

- In data science, you need to use regular expressions to extract data matching a certain structure. Here’s how you can do it with and without the assignment expression.

- While reading a file, the assignment expression might come handy. In the example, the code keeps reading a chunk size data from a file and processing it, until the end of the file is reached.

## Augmented Assignment Operators in Python

An augmented assignment operator is a combination of the assignment operator (=) and any other operator discussed above that returns a numerical value. This is used to shorten an expression and increase code readability. The concept has been discussed with examples in the previous tutorial, Augmented Assignment Operators in Python. Here, I’ll just list out all the possible augmented assignment operators in Python.

Note that the augmented assignment operators have the least precedence. Check out the proof very carefully.

```
>>> x = 10
>>> x *= 2+3 # stands for x = x*2+3
>>> x
50
>>> 10*2+3 # Initially x was 10
23
>>> In practical, Python treats augmented assignment like this:
>>> var <operator>= expression # var = var<operator>(expression)
>>> y = 10
>>> y *= 2+3 # y = y* (2+3)
>>> 10* (2+3)
50
```

Now check out the table. In each case, consider **x=10**:

Operator | Example | Equivalent | Result |
---|---|---|---|

+= | x += 3 | x = x+3 | 13 |

-= | x += 3 | x = x-3 | 7 |

*= | x *= 3 | x = x*3 | 30 |

/= | x /= 3 | x = x/3 | 3.33 |

%= | x %= 3 | x = x%3 | 1 |

//= | x //= 3 | x = x//3 | 3 |

**= | x **= 3 | x = x**3 | 1000 |

&= | x &= 3 | x = x&3 | 2 |

^= | x ^= 3 | x = x^3 | 9 |

|= | x |=3 | x = x|3 | 11 |

<<= | x <<= 3 | x = x<<3 | 80 |

>>= | x >>= 3 | x = x>>3 | 1 |

## Last Words

In this tutorial, you learned about every use case of all the operators in Python. Now you not only can use them properly but also aware of the common mistakes people make. For example, the difference between Binary Bitwise Operators (**&, ^, |**) and Boolean Operators (**or, and, not**). You also know when to use the **is** operator over the **==** operator.

If you didn’t read all the earlier tutorials, here’s a list of recommended ones.