Variables and Data Types
Variables in Python
Section titled “Variables in Python”A variable in Python is a symbolic name that acts as a reference to an object stored in memory. Unlike some other programming languages, Python does not treat variables as containers that directly hold values; instead, variables point to objects.
Python follows a dynamic typing model, which means that the type of a variable is determined automatically at runtime based on the value assigned to it. There is no need to explicitly declare the data type of a variable.
Key Distinction: Python variables are references to objects, not containers. Understanding this difference is crucial for mastering Python’s memory model.
In Python everything is an object, and objects have three key attributes:
- Identity → a unique identifier for the object (memory address)
- Type → the type of the object (e.g., int, str, list)
- Value → the data stored in the object
x = 10print(id(x)) # Identity (memory address)print(type(x)) # Type (int)print(x) # Value (10)Variable Creation
Section titled “Variable Creation”A variable is created when a value is assigned to it using the assignment operator =.
x = 10name = "Sahil"is_active = TrueIn the above examples:
xrefers to an integer objectnamerefers to a string objectis_activerefers to a boolean object
Key Characteristics of Variables
Section titled “Key Characteristics of Variables”- A variable is a reference to an object, not the object itself
- Variables do not store actual data, but rather point to data stored in memory
- Multiple variables can refer to the same object
- Variables can be reassigned to objects of different types at runtime
How Variables Work Internally
Section titled “How Variables Work Internally”Python uses a reference-based memory model where variables point to objects stored in memory.
Memory Model
Section titled “Memory Model”x = 10y = xExecution flow:
- An integer object
10is created in memory xis assigned a reference to that objectyis assigned the same reference asx
This means both x and y refer to the same object.
Reassignment
Section titled “Reassignment”x = 20- A new integer object
20is created xnow points to the new objectystill points to the old object (10)
Diagram
Section titled “Diagram”Identity vs Equality
Section titled “Identity vs Equality”Python provides two different ways to compare objects:
a = [1, 2]b = [1, 2]
a == b # True → values are equala is b # False → different objects in memory==checks value equalityischecks object identity (memory reference)
Data Types in Python
Section titled “Data Types in Python”A data type defines the type of value that an object can hold and determines the operations that can be performed on that value.
Built-in Primitive Types
Section titled “Built-in Primitive Types”int # Integer numbers (e.g., 10)float # Floating-point numbers (e.g., 3.14)bool # Boolean values (True or False)str # Strings (text data)complex # Complex numbers (e.g., 1 + 2j)NoneType # Represents the absence of a valueBuilt-in Collection Types
Section titled “Built-in Collection Types”list # Ordered, mutable collectiontuple # Ordered, immutable collectionset # Unordered collection of unique elementsfrozenset # Unordered, immutable collection of unique elementsdict # Key-value mappingData Types inbuilt methods
Section titled “Data Types inbuilt methods”String Methods
Section titled “String Methods”String is a sequence of characters and has various built-in methods for manipulation:
name = "Sahil"print(name.upper()) # SAHILprint(name.lower()) # sahilprint(name.capitalize()) # Sahilprint(name.replace("a", "o")) # Sohilprint(name.split("a")) # ['S', 'hil']print(name.strip()) # Removes leading and trailing whitespaceprint(name.startswith("S")) # Trueprint(name.endswith("l")) # Trueprint(name.find("h")) # 2 (index of first occurrence)String Slicing:
[start:stop:step] → returns a substring based on the specified indices and step.
name = "Sahil"print(name[0]) # S (first character)print(name[1:4]) # hil (substring from index 1 to 3)print(name[:3]) # Sah (first three characters)print(name[3:]) # il (substring from index 3 to end)print(name[-1]) # l (last character)print(name[-3:]) # hil (last three characters)print(name[::2]) # S a (every second character)List Methods
Section titled “List Methods”Lists are ordered, mutable collections that have various built-in methods for manipulation:
my_list = [1, 2, 3]my_list.append(4) # [1, 2, 3, 4]my_list.insert(1, 10) # [1, 10, 2, 3, 4]my_list.remove(2) # [1, 10, 3, 4my_list.pop() # [1, 10, 3] (removes last element)my_list.pop(1) # [1, 3] (removes element at index 1)my_list.sort() # Sorts the list in placemy_list.reverse() # Reverses the list in placemy_list.clear() # Empties the listmy_list.extend([5, 6]) # [5, 6] (adds elements from another iterable)my_list.count(3) # 1 (counts occurrences of 3)my_list.index(5) # 0 (returns index of first occurrence of 5)my_list.join(", ") # "5, 6" (joins list elements into a string)List Slicing:
[start:stop:step] → returns a sublist based on the specified indices and step.
my_list = [1, 2, 3, 4, 5]print(my_list[0]) # 1 (first element)print(my_list[1:4]) # [2, 3, 4] (sublist from index 1 to 3)print(my_list[:3]) # [1, 2, 3] (first three elements)print(my_list[3:]) # [4, 5] (sublist from index 3 to end)print(my_list[-1]) # 5 (last element)print(my_list[-3:]) # [3, 4, 5] (last three elements)print(my_list[::2]) # [1, 3, 5] (every second element)Dictionary Methods
Section titled “Dictionary Methods”Dictionaries are unordered collections of key-value pairs that have various built-in methods for manipulation:
my_dict = {"name": "Sahil", "age": 20}my_dict["name"] # "Sahil" (access value by key)my_dict["age"] = 21 # Update value for key "age"my_dict["city"] = "New York" # Add new key-value pairdel my_dict["age"] # Remove key-value pair with key "age"my_dict.get("name") # "Sahil" (returns value for key, or None if key not found)my_dict.keys() # dict_keys(['name', 'city']) (returns a view of keys)my_dict.values() # dict_values(['Sahil', 'New York']) (returns a view of values)my_dict.items() # dict_items([('name', 'Sahil'), ('city', 'New York')]) (returns a view of key-value pairs)my_dict.clear() # Empties the dictionarySet Methods
Section titled “Set Methods”Sets are unordered collections of unique elements that have various built-in methods for manipulation:
my_set = {1, 2, 3}my_set.add(4) # {1, 2, 3, 4} (adds an element to the set)my_set.remove(2) # {1, 3, 4} (removes an element from the set)my_set.discard(5) # {1, 3, 4} (does not raise an error if element not found)my_set.pop() # Removes and returns an arbitrary element from the setmy_set.clear() # Empties the setmy_set.union({5, 6}) # {1, 3, 4, 5, 6} (returns a new set with elements from both sets)my_set.intersection({3, 4, 5}) # {3, 4} (returns a new set with elements common to both sets)my_set.difference({3, 4, 5}) # {1} (returns a new set with elements in my_set but not in the other set)my_set.symmetric_difference({3, 4, 5}) # {1, 5} (returns a new set with elements in either set but not in both)Frozenset Methods
Section titled “Frozenset Methods”Frozensets are immutable sets that have various built-in methods for manipulation:
my_frozenset = frozenset([1, 2, 3])my_frozenset.union(frozenset([4, 5])) # frozenset({1, 2, 3, 4, 5}) (returns a new frozenset with elements from both sets)my_frozenset.intersection(frozenset([2, 3, 4])) # frozenset({2, 3}) (returns a new frozenset with elements common to both sets)my_frozenset.difference(frozenset([2, 3, 4])) # frozenset({1}) (returns a new frozenset with elements in my_frozenset but not in the other set)my_frozenset.symmetric_difference(frozenset([2, 3, 4])) # frozenset({1, 4}) (returns a new frozenset with elements in either set but not in both)Tuple Methods
Section titled “Tuple Methods”Tuples are ordered, immutable collections that have various built-in methods for manipulation:
my_tuple = (1, 2, 3)my_tuple[0] # 1 (access element by index)my_tuple.count(2) # 1 (counts occurrences of 2)my_tuple.index(3) # 2 (returns index of first occurrence of 3)Type Checking
Section titled “Type Checking”Before performing type conversion, it’s important to check the type of the value to ensure that the conversion is valid.
x = "10"if isinstance(x, str): print("x is a string")There are two main way to check types in Python:
type()→ returns the exact type of the objectisinstance()→ checks if an object is an instance of a class or a subclass
x = 10print(type(x)) # <class 'int'>print(isinstance(x, int)) # TrueType Casting
Section titled “Type Casting”Type casting refers to the process of converting a value from one data type to another.
Implicit Type Conversion
Section titled “Implicit Type Conversion”In some cases, Python automatically converts one data type to another to prevent data loss.
x = 10 # inty = 3.5 # float
z = x + y # result is float (10.0 + 3.5 → 13.5)Explicit Type Conversion
Section titled “Explicit Type Conversion”Explicit conversion is performed using built-in functions.
int("10") # Converts string to integerfloat("3.14") # Converts string to floatstr(100) # Converts integer to stringbool(1) # Converts integer to booleanint("abc") # Raises ValueErrorOperations and Type Compatibility
Section titled “Operations and Type Compatibility”Python supports various operations that may involve type compatibility and coercion. For example, when performing arithmetic operations between different types, Python will attempt to convert them to a common type.
x = 10 # inty = 3.5 # floatresult = x + y # result is float (10.0 + 3.5 → 13.5)Operations between incompatible types will raise a TypeError.
x = "10"y = 5result = x + y # Raises TypeError (cannot add str and int)Types of operations:
Section titled “Types of operations:”- Arithmetic operations (e.g.,
+,-,*,/) - Comparison operations (e.g.,
==,!=,<,>) - Logical operations (e.g.,
and,or,not) - Membership operations (e.g.,
in,not in) - Identity operations (e.g.,
is,is not) - Bitwise operations (e.g.,
&,|,^,~) - Assignment operations (e.g.,
=,+=,-=,*=,/=)
Type Compatibility Rules
Section titled “Type Compatibility Rules”- Operations between compatible types are allowed (e.g., int and float)
- Operations between incompatible types raise a
TypeError - Python performs implicit type conversion when necessary to prevent data loss
Truthy and Falsy Values
Section titled “Truthy and Falsy Values”In Python, certain values are considered “truthy” or “falsy” when evaluated in a boolean context (e.g., in an if statement).
Falsy Values
Section titled “Falsy Values”NoneFalse0(zero of any numeric type)0.0(zero float)0j(zero complex)''(empty string)[](empty list)()(empty tuple){}(empty dictionary)set()(empty set)
Truthy Values
Section titled “Truthy Values”- Any value that is not falsy is considered truthy, including:
- Non-empty strings
- Non-zero numbers
- Non-empty collections (lists, tuples, sets, dictionaries)
- Custom objects (instances of user-defined classes)
Mutable vs Immutable Objects
Section titled “Mutable vs Immutable Objects”Objects in Python are categorized based on whether their state can be changed after creation.
Immutable Objects
Section titled “Immutable Objects”An immutable object is an object whose value cannot be modified after it is created. Any operation that appears to modify it actually creates a new object.
x = 10x = x + 1- A new integer object is created
- The original object remains unchanged
Examples:
- int
- float
- str
- tuple
Mutable Objects
Section titled “Mutable Objects”lst = [1, 2]lst.append(3)- The same list object is modified
- Memory reference remains unchanged
Examples:
- list
- dict
- set
Comparison
Section titled “Comparison”| Feature | Mutable Objects | Immutable Objects |
|---|---|---|
| Modifiable | Yes | No |
| Memory usage | Same object modified | New object created |
| Examples | list, dict, set | int, str, tuple |
Object Storage and Memory Behavior
Section titled “Object Storage and Memory Behavior”Python manages memory using a combination of:
- Heap memory → stores all objects
- References (names) → point to objects
Variables are simply names bound to objects.
Internal Flow
Section titled “Internal Flow”Copying in Python
Section titled “Copying in Python”Copying refers to creating a new object based on an existing object. The behavior of copying depends on whether the object is mutable and whether the copy is shallow or deep.
Shallow Copy vs Deep Copy
Section titled “Shallow Copy vs Deep Copy”Shallow Copy
Section titled “Shallow Copy”A shallow copy creates a new outer object, but the inner objects are still shared between the original and the copy.
import copy
a = [[1, 2], [3, 4]]b = copy.copy(a)
b[0][0] = 100print(a) # Original is affectedThis Diagram illustrates the concept of shallow copy:
- List
bis a new list object that references the same inner lists asa - Modifying the inner list through
balso modifies the inner list referenced bya
Deep Copy
Section titled “Deep Copy”A deep copy creates a completely independent copy of the object, including all nested objects.
import copy
a = [[1, 2], [3, 4]]b = copy.deepcopy(a)
b[0][0] = 100print(a) # Original is NOT affectedThis Diagram illustrates the concept of deep copy:
- List
bis a new list object that references new inner lists, so modifications tobdo not affecta - Deep copying is more memory-intensive than shallow copying, but it ensures complete independence between the original and the copy.
Assignment vs Copy
Section titled “Assignment vs Copy”Assignment
Section titled “Assignment”a = [1, 2]b = a- No new object is created
- Both variables refer to the same object
- Changes in one affect the other
b = a.copy()- A new object is created
- Only the outer structure is copied (shallow copy)
- Nested objects may still be shared