Python Operators — Complete Reference
Operators are special symbols that perform operations on variables and values. Python provides a rich set of operators for different purposes. This guide covers every operator type with practical examples.
Learning Objectives
- Understand and use all seven categories of Python operators
- Know the difference between
==andis - Master short-circuit evaluation with logical operators
- Use bitwise operators for real-world tasks like permissions and flags
- Recognize common operator pitfalls and how to avoid them
- Apply correct operator precedence in complex expressions
- Use all built-in math functions that complement operators
Arithmetic Operators
Arithmetic operators perform mathematical calculations.
| Operator | Name | Example | Result |
|---|---|---|---|
+ | Addition | 5 + 3 | 8 |
- | Subtraction | 5 - 3 | 2 |
* | Multiplication | 5 * 3 | 15 |
/ | Division | 5 / 3 | 1.6667 |
// | Floor Division | 5 // 3 | 1 |
% | Modulo | 5 % 3 | 2 |
** | Power | 5 ** 3 | 125 |
Addition (+)
# Integers
print(5 + 3) # 8
# Floats
print(5.5 + 3.2) # 8.7
# Strings (concatenation)
print("Hello" + " " + "World") # Hello World
# Lists (concatenation)
print([1, 2] + [3, 4]) # [1, 2, 3, 4]
# Tuples (concatenation)
print((1, 2) + (3, 4)) # (1, 2, 3, 4)
# Mixed types (with implicit conversion)
print(5 + 3.0) # 8.0 (int + float -> float)
Subtraction (-)
# Integers
print(10 - 4) # 6
# Floats
print(10.5 - 3.2) # 7.3
# Unary minus (negation)
x = 5
print(-x) # -5
# Sets (difference)
print({1, 2, 3} - {2, 3, 4}) # {1}
Multiplication (*)
# Integers
print(6 * 7) # 42
# Floats
print(3.14 * 2) # 6.28
# Strings (repetition)
print("ha" * 3) # hahaha
print("-" * 20) # --------------------
# Lists (repetition)
print([0] * 5) # [0, 0, 0, 0, 0]
print([1, 2] * 3) # [1, 2, 1, 2, 1, 2]
# Tuples (repetition)
print((0,) * 4) # (0, 0, 0, 0)
Division (/)
In Python 3, / always returns a float:
# Python 3
print(10 / 4) # 2.5
print(10 / 2) # 5.0 — still a float!
print(7 / 3) # 2.3333...
# Floor division (//) for integer result
print(10 // 4) # 2
print(10 // 2) # 5
print(-10 // 4) # -3 — floors toward negative infinity
Floor Division (//)
# Positive numbers
print(17 // 5) # 3
# Negative numbers (floors toward negative infinity)
print(-17 // 5) # -4 (not -3!)
print(17 // -5) # -4
# Float floor division
print(7.0 // 2) # 3.0
print(7.5 // 2) # 3.0
Modulo (%)
The modulo operator in Python always returns a result with the same sign as the divisor:
# Basic modulo
print(7 % 3) # 1
# Negative numbers
print(-7 % 3) # 2 (not -1!)
print(7 % -3) # -2
print(-7 % -3) # -1
# Float modulo
print(10.5 % 3) # 1.5
# Practical uses
print(42 % 2) # 0 (even number)
print(43 % 2) # 1 (odd number)
print(99 % 10) # 9 (last digit)
Power (**)
# Basic power
print(2 ** 10) # 1024
print(5 ** 2) # 25
# Negative power
print(2 ** -1) # 0.5
print(4 ** -0.5) # 0.5
# Float power
print(9 ** 0.5) # 3.0 (square root)
print(8 ** (1/3)) # 2.0 (cube root)
# Large numbers (no overflow)
print(10 ** 100) # 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Division in Python 3 vs Python 2
In Python 3, / always returns a float:
# Python 3
>>> 10 / 4
2.5
>>> 10 / 2
5.0 # Still a float!
# Python 2 (legacy)
>>> 10 / 4
2 # Integer division (truncated)
Use // when you want integer (floor) division in Python 3:
>>> 10 // 4
2
>>> -10 // 4
-3 # Floors toward negative infinity
Comparison (Relational) Operators
Comparison operators return True or False based on the comparison.
| Operator | Meaning | Example | Result |
|---|---|---|---|
== | Equal to | 5 == 5 | True |
!= | Not equal to | 5 != 3 | True |
> | Greater than | 5 > 3 | True |
< | Less than | 5 < 3 | False |
>= | Greater or equal | 5 >= 5 | True |
<= | Less or equal | 5 <= 3 | False |
Chained Comparisons
Python supports chaining multiple comparisons in a single expression:
>>> x = 5
>>> 1 < x < 10
True
>>> 1 < x < 3
False
# Equivalent to:
>>> 1 < x and x < 10
True
# You can chain even more:
>>> 1 < x < 100 > 50
True
# All comparisons are evaluated
>>> 1 < 2 < 3 < 4 < 5
True
== vs is for None
This is a critical distinction:
>>> x = None
>>> x == None
True
>>> x is None
True
# But be careful:
>>> a = float('nan')
>>> a == a
False # NaN is never equal to itself
>>> a is a
True # Identity check still works
Always use is to compare with None, True, or False:
# Correct
if x is None:
pass
# Incorrect (works but not Pythonic)
if x == None:
pass
Floating Point Comparison Issues
Floating point arithmetic can produce unexpected results:
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False
Use math.isclose() for float comparisons:
>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True
# With custom tolerance
>>> math.isclose(0.1 + 0.2, 0.3, rel_tol=1e-9)
True
Rich Comparisons
Python objects can define rich comparison methods that are called by comparison operators:
class Money:
def __init__(self, amount):
self.amount = amount
def __eq__(self, other):
return self.amount == other.amount
def __lt__(self, other):
return self.amount < other.amount
def __le__(self, other):
return self.amount <= other.amount
def __gt__(self, other):
return self.amount > other.amount
def __ge__(self, other):
return self.amount >= other.amount
def __ne__(self, other):
return self.amount != other.amount
m1 = Money(100)
m2 = Money(200)
print(m1 < m2) # True
print(m1 >= m2) # False
Logical Operators
Logical operators combine conditional statements.
| Operator | Description | Example |
|---|---|---|
and | Returns True if both are True | True and False -> False |
or | Returns True if at least one is True | True or False -> True |
not | Reverses the boolean value | not True -> False |
Truth Tables
# and truth table
True and True # True
True and False # False
False and True # False
False and False # False
# or truth table
True or True # True
True or False # True
False or True # True
False or False # False
# not truth table
not True # False
not False # True
Short-Circuit Evaluation
Python evaluates logical operators lazily — it stops as soon as the result is determined:
# 'and' stops at first False
>>> False and print("Never executes")
False
# 'or' stops at first True
>>> True or print("Never executes")
True
Return Values (Not What You Expect)
Logical operators return operands, not necessarily True or False:
>>> 3 and 5
5 # Returns last truthy value
>>> 0 and 5
0 # Returns first falsy value
>>> 3 or 5
3 # Returns first truthy value
>>> 0 or 5
5 # Returns last value if all falsy before
>>> "" or "hello"
'hello'
>>> "hello" or ""
'hello'
The rule:
a and b: Ifais falsy, returna; otherwise returnba or b: Ifais truthy, returna; otherwise returnb
Common Patterns
Default values:
>>> name = "" or "Anonymous"
'Anonymous'
>>> name = "Alice" or "Anonymous"
'Alice'
Guard clauses:
# Safe attribute access
>>> users = {"admin": {"role": "super"}}
>>> role = users.get("admin") and users["admin"].get("role")
'super'
>>> role = users.get("missing") and users["missing"].get("role")
None
Conditional assignment:
>>> x = 5
>>> result = x > 10 and "big" or "small"
>>> result
'small'
Bitwise Operators
Bitwise operators work on integers at the binary level.
| Operator | Name | Example | Result (Binary) |
|---|---|---|---|
& | AND | 5 & 3 | 1 (001) |
| | OR | 5 | 3 | 7 (111) |
^ | XOR | 5 ^ 3 | 6 (110) |
~ | NOT | ~5 | -6 |
<< | Left Shift | 5 << 2 | 20 (10100) |
>> | Right Shift | 5 >> 2 | 1 (1) |
Truth Tables
# AND (&): Both bits must be 1
>>> 0b1010 & 0b1100 # 10 & 12
0b1000 # 8
# OR (|): At least one bit must be 1
>>> 0b1010 | 0b1100 # 10 | 12
0b1110 # 14
# XOR (^): Bits must be different
>>> 0b1010 ^ 0b1100 # 10 ^ 12
0b0110 # 6
# NOT (~): Flips all bits
>>> ~5
-6 # Equivalent to -(n+1)
# Left Shift: Multiply by 2^n
>>> 5 << 2 # 5 * 2^2
20
# Right Shift: Integer divide by 2^n
>>> 20 >> 2 # 20 // 2^2
5
Real-World Uses: Flags and Permissions
Bitwise operators are commonly used for feature flags and permissions:
# Define permission flags
READ = 0b001 # 1
WRITE = 0b010 # 2
EXECUTE = 0b100 # 4
# Combine permissions
user_permissions = READ | WRITE # 0b011 = 3
# Check permissions
has_read = bool(user_permissions & READ) # True
has_execute = bool(user_permissions & EXECUTE) # False
# Add permission
user_permissions |= EXECUTE # 0b111 = 7
# Remove permission
user_permissions &= ~WRITE # 0b101 = 5
# Toggle permission
user_permissions ^= READ # 0b100 = 4
# Check if multiple permissions set
needs = READ | WRITE
has_all = (user_permissions & needs) == needs # False
More Bitwise Use Cases
# Check if a number is a power of 2
def is_power_of_two(n):
return n > 0 and (n & (n - 1)) == 0
print(is_power_of_two(16)) # True
print(is_power_of_two(18)) # False
# Swap two numbers without a temp variable
a, b = 5, 10
a ^= b
b ^= a
a ^= b
print(a, b) # 10, 5
# Get unique element (all others appear twice)
def find_unique(lst):
result = 0
for num in lst:
result ^= num
return result
print(find_unique([2, 3, 2, 4, 3])) # 4
# Extract individual bits
n = 42
print(f"Binary: {n:b}") # 101010
print(f"Bit 0: {n & 1}") # 0
print(f"Bit 1: {(n >> 1) & 1}") # 1
print(f"Bit 2: {(n >> 2) & 1}") # 0
print(f"Bit 3: {(n >> 3) & 1}") # 1
print(f"Bit 4: {(n >> 4) & 1}") # 0
print(f"Bit 5: {(n >> 5) & 1}") # 1
Membership Operators
Membership operators test if a value is found in a sequence.
| Operator | Description | Example |
|---|---|---|
in | Returns True if value is in sequence | 3 in [1, 2, 3] -> True |
not in | Returns True if value is not in sequence | 3 not in [1, 2, 3] -> False |
With Different Types
# Lists
>>> 3 in [1, 2, 3, 4, 5]
True
>>> "x" in ["a", "b", "c"]
False
# Strings (substring check)
>>> "py" in "python"
True
>>> "Python" in "python"
False # Case-sensitive!
# Tuples
>>> 10 in (10, 20, 30)
True
# Dictionaries (checks keys, not values!)
>>> "name" in {"name": "Alice", "age": 30}
True
>>> "Alice" in {"name": "Alice", "age": 30}
False # Check values with .values()
# Sets (most efficient membership testing)
>>> 3 in {1, 2, 3, 4, 5}
True
# Range
>>> 5 in range(10)
True
>>> 10 in range(10)
False # range(10) is 0-9
__contains__ Protocol
When you use in, Python calls the __contains__ method:
class Fibonacci:
def __init__(self, limit):
self.limit = limit
self.fibs = [0, 1]
while self.fibs[-1] < limit:
self.fibs.append(self.fibs[-1] + self.fibs[-2])
def __contains__(self, item):
return item in self.fibs
>>> fib = Fibonacci(100)
>>> 21 in fib
True
>>> 22 in fib
False
Performance Comparison
import time
data_list = list(range(10000))
data_set = set(range(10000))
# List membership: O(n)
start = time.time()
for _ in range(1000):
9999 in data_list
print(f"List: {time.time() - start:.4f}s")
# Set membership: O(1)
start = time.time()
for _ in range(1000):
9999 in data_set
print(f"Set: {time.time() - start:.4f}s")
Identity Operators
Identity operators check if two variables point to the same object in memory.
| Operator | Description | Example |
|---|---|---|
is | True if both reference same object | a is b |
is not | True if they reference different objects | a is not b |
Integer CPython Caching
CPython caches integers from -5 to 256 for performance:
>>> a = 256
>>> b = 256
>>> a is b
True # Same cached object
>>> a = 257
>>> b = 257
>>> a is b
False # Different objects (usually)
# This behavior is implementation-specific and shouldn't be relied upon
String Interning
Python interns (reuses) strings that look like identifiers:
>>> a = "hello"
>>> b = "hello"
>>> a is b
True # Interned
>>> a = "hello world"
>>> b = "hello world"
>>> a is b
True # Also interned (contains only identifier chars)
>>> a = "hello!"
>>> b = "hello!"
>>> a is b
False # Contains non-identifier char, not interned
When to Use is vs ==
Use is for:
- Checking
None:if x is None: - Checking
True/False:if flag is True: - Checking sentinels:
if result is MISSING:
Use is not for:
- Checking
None:if x is not None:
Use == for:
- Everything else (value comparison)
# Correct
if x is None:
pass
if result is not True:
pass
# Incorrect (may fail with custom __eq__)
if x == None: # Works but not Pythonic
pass
Assignment Operators
Assignment operators assign values to variables.
| Operator | Equivalent | Example |
|---|---|---|
= | x = 5 | x = 5 |
+= | x = x + 5 | x += 5 |
-= | x = x - 5 | x -= 5 |
*= | x = x * 5 | x *= 5 |
/= | x = x / 5 | x /= 5 |
//= | x = x // 5 | x //= 5 |
%= | x = x % 5 | x %= 5 |
**= | x = x ** 5 | x **= 5 |
&= | x = x & 5 | x &= 5 |
|= | x = x | 5 | x |= 5 |
^= | x = x ^ 5 | x ^= 5 |
<<= | x = x << 5 | x <<= 5 |
>>= | x = x >> 5 | x >>= 5 |
Augmented Assignment with Mutable Types
Be careful with mutable objects:
>>> a = [1, 2, 3]
>>> b = a
>>> a += [4, 5] # This calls __iadd__ (in-place add)
>>> a
[1, 2, 3, 4, 5]
>>> b
[1, 2, 3, 4, 5] # b is affected! Same object
>>> a = [1, 2, 3]
>>> b = a
>>> a = a + [4, 5] # This creates a NEW list
>>> a
[1, 2, 3, 4, 5]
>>> b
[1, 2, 3] # b is NOT affected
This happens because += calls __iadd__ which modifies in place for lists, while + creates a new object.
Operator Precedence
From highest to lowest:
| Precedence | Operators | Description |
|---|---|---|
| 1 | () | Parentheses (grouping) |
| 2 | ** | Exponentiation |
| 3 | ~, +x, -x | Bitwise NOT, unary plus/minus |
| 4 | *, /, //, % | Multiplication, division, floor div, modulo |
| 5 | +, - | Addition, subtraction |
| 6 | <<, >> | Bitwise shifts |
| 7 | & | Bitwise AND |
| 8 | ^ | Bitwise XOR |
| 9 | | | Bitwise OR |
| 10 | ==, !=, >, <, >=, <=, is, is not, in, not in | Comparisons |
| 11 | not | Logical NOT |
| 12 | and | Logical AND |
| 13 | or | Logical OR |
Parentheses for Clarity
Always use parentheses when precedence is unclear:
# Avoid this
>>> 2 + 3 * 4
14
# Prefer this
>>> 2 + (3 * 4)
14
# Essential with mixed operators
>>> (x > 5) and (y < 10)
# Without parentheses - harder to read
>>> x > 5 and y < 10
Precedence Gotchas
# Be careful with not vs comparison
print(not 1 == 1) # False (not (1 == 1))
print(not 1 == 2) # True (not (1 == 2))
# Bitwise vs comparison precedence
print(1 & 1 == 1) # 1 & (1 == 1) -> 1 & True -> 1
print((1 & 1) == 1) # True
# ** is right-associative
print(2 ** 3 ** 2) # 2 ** (3 ** 2) = 2 ** 9 = 512
print((2 ** 3) ** 2) # 8 ** 2 = 64
Special Operator Behaviors
String Repetition with *
The * operator repeats strings:
>>> "ha" * 3
'hahaha'
>>> "-" * 20
'--------------------'
>>> 3 * "py"
'pypypy'
List Repetition with *
Lists can also be repeated:
>>> [0] * 5
[0, 0, 0, 0, 0]
>>> [1, 2] * 3
[1, 2, 1, 2, 1, 2]
# Be careful with nested lists!
>>> a = [[0]] * 3
>>> a
[[0], [0], [0]]
>>> a[0].append(1)
>>> a
[[0, 1], [0, 1], [0, 1]] # All sublists are the same object!
# Correct way
>>> b = [[0] for _ in range(3)]
>>> b[0].append(1)
>>> b
[[0, 1], [0], [0]] # Independent sublists
Operator Overloading Preview
Python allows custom classes to define operator behavior:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __repr__(self):
return f"Vector({self.x}, {self.y})"
>>> v1 = Vector(1, 2)
>>> v2 = Vector(3, 4)
>>> v1 + v2
Vector(4, 6)
>>> v1 * 3
Vector(3, 6)
Built-In Functions for Operators
abs() — Absolute Value
print(abs(-5)) # 5
print(abs(5)) # 5
print(abs(-3.14)) # 3.14
print(abs(3+4j)) # 5.0 (magnitude)
print(abs(True)) # 1
print(abs(False)) # 0
round() — Round to N Decimal Places
print(round(3.14159)) # 3
print(round(3.14159, 2)) # 3.14
print(round(3.145, 2)) # 3.14 or 3.15 (banker's rounding)
print(round(2.5)) # 2 (banker's rounding: rounds to even!)
print(round(3.5)) # 4
print(round(-2.5)) # -2
pow() — Power with Optional Modulo
print(pow(2, 10)) # 1024
print(pow(2, 10, 1000)) # 24 (2^10 mod 1000)
print(pow(2, -1)) # 0.5
print(pow(9, 0.5)) # 3.0 (square root)
print(pow(25, 0.5)) # 5.0
divmod() — Quotient and Remainder
print(divmod(17, 5)) # (3, 2) because 17 = 3*5 + 2
print(divmod(10.5, 3)) # (3.0, 1.5)
print(divmod(100, 7)) # (14, 2)
# Practical use: converting seconds to hours:minutes:seconds
total_seconds = 3665
hours, remainder = divmod(total_seconds, 3600)
minutes, seconds = divmod(remainder, 60)
print(f"{hours}h {minutes}m {seconds}s") # 1h 1m 5s
Common Mistakes
Mistake 1: Using = Instead of ==
x = 5
# Wrong: assignment in condition
if x = 5: # SyntaxError
print("yes")
# Correct
if x == 5:
print("yes")
Mistake 2: Chained Comparison with and
# Wrong (looks right but behaves differently)
>>> x = 5
>>> 1 < x and x < 10
True
# This looks similar but isn't:
>>> 1 < x < 10 # Actually equivalent to above (good)
True
# The gotcha: logical operators return operands
>>> 1 and 2 and 3
3
>>> 0 and 2 and 3
0
Mistake 3: Mutable Default with *
# Wrong: shared mutable default
>>> def append_to(item, lst=[]):
... lst.append(item)
... return lst
>>> append_to(1)
[1]
>>> append_to(2)
[1, 2] # Not [2]! List persists between calls
# Correct
>>> def append_to(item, lst=None):
... if lst is None:
... lst = []
... lst.append(item)
... return lst
Mistake 4: Float Comparison with ==
# Wrong
>>> 0.1 + 0.2 == 0.3
False
# Correct
>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True
Mistake 5: Confusing in with Dictionaries
>>> d = {"name": "Alice", "age": 30}
# Wrong: checking values instead of keys
>>> "Alice" in d
False
# Correct: 'in' checks keys
>>> "name" in d
True
# To check values
>>> "Alice" in d.values()
True
Mistake 6: Using is for Large Numbers
# WRONG — unreliable for large integers
x = 1000
y = 1000
print(x is y) # May be True or False depending on implementation
# CORRECT — use == for value comparison
print(x == y) # Always True
Mistake 7: Operator Precedence with not
# NOT is a function call, not a comparison operator
print(not 1 in [1, 2, 3]) # False (not (1 in [1, 2, 3]))
print(1 not in [1, 2, 3]) # False (clearer intent)
Practice Exercises
Exercise 1: Temperature Converter
Write a function that converts Celsius to Fahrenheit and determines if the water is boiling (>= 100°C / 212°F).
def check_boiling(celsius):
fahrenheit = celsius * 9/5 + 32
is_boiling = celsius >= 100
return fahrenheit, is_boiling
# Test
temp_f, boiling = check_boiling(100)
print(f"{temp_f}°F, Boiling: {boiling}")
# Output: 212.0°F, Boiling: True
temp_f, boiling = check_boiling(25)
print(f"{temp_f}°F, Boiling: {boiling}")
# Output: 77.0°F, Boiling: False
Exercise 2: Permission Checker
Create a function that checks what operations a user can perform based on permission flags.
READ = 1
WRITE = 2
EXECUTE = 4
def check_permissions(user_flags):
can_read = bool(user_flags & READ)
can_write = bool(user_flags & WRITE)
can_execute = bool(user_flags & EXECUTE)
permissions = []
if can_read:
permissions.append("read")
if can_write:
permissions.append("write")
if can_execute:
permissions.append("execute")
return permissions
# Test
print(check_permissions(READ | WRITE)) # ['read', 'write']
print(check_permissions(READ | EXECUTE)) # ['read', 'execute']
print(check_permissions(READ | WRITE | EXECUTE)) # ['read', 'write', 'execute']
print(check_permissions(0)) # []
Exercise 3: Data Validator
Write a validator that checks if data meets multiple conditions using logical operators.
def validate_user(data):
has_name = bool(data.get("name"))
has_email = "@" in data.get("email", "")
age = data.get("age", 0)
valid_age = 18 <= age <= 120
is_valid = has_name and has_email and valid_age
errors = []
if not has_name:
errors.append("Name required")
if not has_email:
errors.append("Valid email required")
if not valid_age:
errors.append("Age must be 18-120")
return is_valid, errors
# Test
valid, errors = validate_user({
"name": "Alice",
"email": "alice@example.com",
"age": 25
})
print(f"Valid: {valid}, Errors: {errors}")
# Output: Valid: True, Errors: []
valid, errors = validate_user({
"name": "",
"email": "invalid",
"age": 15
})
print(f"Valid: {valid}, Errors: {errors}")
# Output: Valid: False, Errors: ['Name required', 'Valid email required', 'Age must be 18-120']
Exercise 4: Bit Manipulation
Write a function that extracts and displays individual bits of a number, then tests if it's a power of two.
def analyze_bits(n):
print(f"\nNumber: {n} (binary: {n:b})")
# Extract each bit
bits = []
for i in range(max(n.bit_length(), 1)):
bits.append((n >> i) & 1)
print(f"Bits (LSB first): {bits}")
# Check if power of 2
is_power_of_2 = n > 0 and (n & (n - 1)) == 0
print(f"Is power of 2: {is_power_of_2}")
return bits
analyze_bits(42)
analyze_bits(16)
analyze_bits(7)
Key Takeaways
- Division:
/always returns a float in Python 3; use//for floor division - Modulo with negatives: Returns result with the same sign as the divisor
- Chained comparisons:
1 < x < 10is cleaner than1 < x and x < 10 ==vsis: UseisforNone/True/Falsechecks;==for value comparison- Logical operators return operands:
0 or "hello"returns"hello", notTrue - Short-circuit evaluation:
andstops at first falsy;orstops at first truthy - Membership testing:
inon dictionaries checks keys, not values - Integer caching:
ismay work for small integers but shouldn't be relied upon - Bitwise operators: Essential for flags, permissions, and low-level operations
- Use parentheses: When operator precedence is unclear, always use parentheses for clarity
Next: Python Strings — Learn about string manipulation, formatting, and methods.