Python Variables and Data Types — The Complete Guide
Learning Objectives
By the end of this tutorial, you will be able to:
- Understand how Python variables differ from variables in statically-typed languages like C and Java
- Identify and use all major Python data types including numeric, text, boolean, sequence, mapping, set, and binary types
- Perform type checking with
type()andisinstance(), and convert between types safely - Use all relevant built-in functions for type inspection, conversion, and manipulation
- Apply dynamic typing principles to write flexible, idiomatic Python code
- Recognize and avoid common mistakes related to variable assignment and type behavior
- Understand memory management with
id(),hash(), and reference counting
What Are Variables?
In Python, a variable is a name that refers to a value stored in memory. Unlike C or Java where variables have fixed types declared at compile time, Python variables are labels (or references) bound to objects. When you write x = 42, Python creates an integer object with the value 42 and binds the name x to it.
Python uses reference counting for memory management. Every object tracks how many names refer to it. When the reference count drops to zero, the memory is freed automatically.
| Feature | C / Java | Python |
|---|---|---|
| Type declaration | Required (int x = 42;) | Not required (x = 42) |
| Type binding | Compile-time (static) | Run-time (dynamic) |
| Memory management | Manual or GC | Reference counting + garbage collector |
| Variable meaning | Memory location | Reference to an object |
| Mutability of variables | Variables can be const | Variables are always rebindable |
Creating Variables
Creating a variable in Python is as simple as assigning a value. No type declaration is required.
age = 25 # int
temperature = 98.6 # float
name = "Alice" # str
is_active = True # bool
x, y, z = 10, 20, 30 # Multiple assignment
a = b = c = 0 # Same value to all
m, n = 5, 10
m, n = n, m # Swap — Pythonic way
print(m, n) # 10 5
Assignment Types
# Simple assignment
x = 10
# Multiple assignment
x, y, z = 1, 2, 3
# Unpacking an iterable
a, b, c = [10, 20, 30]
# Star unpacking
first, *rest = [1, 2, 3, 4, 5]
print(first) # 1
print(rest) # [2, 3, 4, 5]
# Ignoring values
_, important, _ = (1, 42, 3)
# Augmented assignment
x = 10
x += 5 # x = x + 5 -> 15
x -= 3 # x = x - 3 -> 12
x *= 2 # x = x * 2 -> 24
x /= 4 # x = x / 4 -> 6.0
x //= 2 # x = x // 2 -> 3.0
x **= 3 # x = x ** 3 -> 27.0
x %= 5 # x = x % 5 -> 2.0
Python supports optional type annotations (Python 3.6+) for clarity and static analysis, but they do not enforce types at runtime:
count: int = 42
name: str = "Bob"
count = "now I'm a string" # Valid — type hints are not enforced
Python Data Types Deep Dive
Numeric Types
int — Integers
Python integers have unlimited precision. Unlike C's int, they can be arbitrarily large.
big_number = 10 ** 100 # No overflow
binary_num = 0b1010 # Binary: 10
octal_num = 0o17 # Octal: 15
hex_num = 0xFF # Hex: 255
population = 7_900_000_000 # Underscores for readability (3.6+)
# Integer from different bases
print(int("42")) # 42
print(int("1010", 2)) # 10 (binary to int)
print(int("FF", 16)) # 255 (hex to int)
print(int("17", 8)) # 15 (octal to int)
float — Floating-Point Numbers
Python floats are IEEE 754 double-precision (64-bit).
speed_of_light = 3e8 # Scientific notation: 300000000.0
# ⚠️ Precision gotcha
print(0.1 + 0.2) # 0.30000000000000004
print(0.1 + 0.2 == 0.3) # False
import math
print(math.isclose(0.1 + 0.2, 0.3)) # True — use this instead
print(math.inf) # inf (positive infinity)
print(float('nan') == float('nan')) # False — NaN != NaN
# Float from different formats
print(float("3.14")) # 3.14
print(float("inf")) # inf
print(float("-inf")) # -inf
print(float("nan")) # nan
complex — Complex Numbers
z = 3 + 4j
print(z.real) # 3.0
print(z.imag) # 4.0
print(abs(z)) # 5.0 (magnitude: sqrt(3² + 4²))
print(z.conjugate()) # (3-4j)
# Create complex from values
z2 = complex(3, 4)
print(z2) # (3+4j)
# Complex from string
z3 = complex("1+2j")
print(z3) # (1+2j)
decimal — Precise Decimal Arithmetic
For financial calculations where IEEE 754 floating-point errors are unacceptable:
from decimal import Decimal, getcontext, ROUND_HALF_UP
getcontext().prec = 28
price = Decimal('19.99')
tax = Decimal('0.08')
total = (price * tax).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
print(total) # 1.60
# Compare with float
print(Decimal('0.1') + Decimal('0.2')) # 0.3 (exact)
print(0.1 + 0.2) # 0.30000000000000004 (inexact)
Text Type
str — Strings
Strings in Python are immutable sequences of Unicode characters.
s = "Hello, World!"
raw = r"C:\new\folder\test" # Raw string — no escape processing
multi = """Triple quotes
span multiple lines"""
# f-strings (3.6+)
name = "Alice"
age = 30
print(f"My name is {name} and I'll be {age + 1} next year.")
# Strings are immutable
# s[0] = "h" # TypeError
s_new = "h" + s[1:] # Create a new string
# Common methods
text = " Hello, Python World! "
print(text.strip()) # "Hello, Python World!"
print(text.lower()) # " hello, python world! "
print(text.replace("World", "Universe")) # " Hello, Python Universe! "
print(text.split(",")) # [" Hello", " Python World! "]
print("-".join(["a", "b", "c"])) # a-b-c
Boolean Type
bool — Booleans
Booleans are a subclass of integers. True equals 1, False equals 0.
print(True + True) # 2
print(isinstance(True, int)) # True
# Truthy and Falsy values
# Everything evaluates to False except:
# False, None, 0, 0.0, 0j, "", [], (), {}, set(), frozenset(), range(0)
print(bool("")) # False
print(bool("False")) # True — non-empty string!
print(bool(0)) # False
print(bool(-1)) # True — any non-zero number
print(bool([])) # False
print(bool([0])) # True — non-empty list
Sequence Types
list — Lists (Mutable, Ordered)
fruits = ["apple", "banana", "cherry"]
mixed = [1, "hello", 3.14, True, None] # Mixed types allowed
fruits.append("date")
fruits.insert(1, "blueberry")
fruits.remove("banana")
popped = fruits.pop()
# Slicing
print(fruits[0:2]) # ['apple', 'blueberry']
print(fruits[::-1]) # Reversed copy
# List comprehension
squares = [x**2 for x in range(10)]
evens = [x for x in range(20) if x % 2 == 0]
# Sorting
numbers = [3, 1, 4, 1, 5]
numbers.sort() # In-place
sorted_list = sorted(numbers, reverse=True) # New list
tuple — Tuples (Immutable, Ordered)
coordinates = (10, 20)
single = (42,) # Trailing comma required for single-item tuple
# Tuple unpacking
x, y = coordinates
print(x, y) # 10 20
# When to use tuples: fixed collections, dict keys, return values
# Named tuples for readability
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)
print(p.x, p.y) # 3 4
range — Range Objects
print(list(range(5))) # [0, 1, 2, 3, 4]
print(list(range(2, 8))) # [2, 3, 4, 5, 6, 7]
print(list(range(0, 10, 2))) # [0, 2, 4, 6, 8]
r = range(1_000_000)
print(999_999 in r) # True — O(1) membership test, no list in memory
Mapping Type
dict — Dictionaries (Mutable, Key-Value Pairs)
person = {"name": "Alice", "age": 30}
person["email"] = "a@b.com" # Add
person["age"] = 31 # Update
del person["email"] # Delete
print(person.get("phone", "N/A")) # Default if key missing
# Dict comprehension
squares = {x: x**2 for x in range(6)}
# Merging (3.9+)
defaults = {"color": "blue", "size": "medium"}
custom = {"color": "red"}
merged = defaults | custom # {'color': 'red', 'size': 'medium'}
# Iteration
for key, value in person.items():
print(f"{key}: {value}")
Set Types
set and frozenset
colors = {"red", "green", "blue"} # Duplicates removed automatically
empty_set = set() # Not {} — that creates an empty dict!
colors.add("yellow")
colors.discard("red") # No error if missing
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b) # Union: {1, 2, 3, 4, 5, 6}
print(a & b) # Intersection: {3, 4}
print(a - b) # Difference: {1, 2}
print(a ^ b) # Sym diff: {1, 2, 5, 6}
fs = frozenset([1, 2, 3]) # Immutable set — can be dict key or set element
Binary Types
bytes, bytearray, memoryview
# bytes — immutable
b = b"Hello"
print(b[0]) # 72 (ASCII for 'H')
# bytearray — mutable
ba = bytearray(b"Hello")
ba[0] = 74 # Change to 'J'
print(ba) # b'Jello'
# Encoding/decoding
text = "Hello, 世界"
encoded = text.encode("utf-8")
decoded = encoded.decode("utf-8")
print(decoded) # Hello, 世界
# memoryview — zero-copy access to binary data
data = bytearray(b"Hello, World!")
mv = memoryview(data)
mv[0] = 74
print(data) # bytearray(b'Jello, World!')
Built-In Functions for Types
type() — Get the Type of an Object
print(type(42)) # <class 'int'>
print(type(3.14)) # <class 'float'>
print(type("hello")) # <class 'str'>
print(type(True)) # <class 'bool'>
print(type(None)) # <class 'NoneType'>
print(type([1, 2])) # <class 'list'>
print(type((1, 2))) # <class 'tuple'>
print(type({1: 2})) # <class 'dict'>
print(type({1, 2})) # <class 'set'>
print(type(b"hello")) # <class 'bytes'>
# type() with three arguments creates a new type dynamically
MyType = type('MyType', (object,), {'value': 42})
obj = MyType()
print(obj.value) # 42
print(type(obj)) # <class '__main__.MyType'>
id() — Get Unique Identity of an Object
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(id(a)) # e.g., 140234866534400
print(id(b)) # e.g., 140234866534720 (different!)
print(id(c)) # e.g., 140234866534400 (same as a!)
print(a is c) # True — same object
print(a is b) # False — different objects with same value
# Small integer caching: Python caches -5 to 256
x = 256; y = 256
print(x is y) # True — same cached object
x = 257; y = 257
print(x is y) # False (usually) — different objects
isinstance() — Check Type (Including Inheritance)
print(isinstance(42, int)) # True
print(isinstance(3.14, float)) # True
print(isinstance("hello", str)) # True
print(isinstance(True, int)) # True — bool is subclass of int!
print(isinstance(True, bool)) # True
# Check against multiple types
print(isinstance(42, (int, float))) # True
print(isinstance("hi", (int, float))) # False
# Works with inheritance
class MyInt(int):
pass
num = MyInt(42)
print(isinstance(num, int)) # True — MyInt inherits from int
print(type(num) == int) # False — type() doesn't recognize subclasses
hasattr() — Check if Object Has an Attribute
class Person:
def __init__(self, name):
self.name = name
p = Person("Alice")
print(hasattr(p, "name")) # True
print(hasattr(p, "age")) # False
# Works with any object
print(hasattr([1, 2], "append")) # True — lists have append
print(hasattr(42, "append")) # False — ints don't have append
getattr() — Get an Attribute Value
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Alice", 30)
print(getattr(p, "name")) # Alice
print(getattr(p, "age")) # 30
print(getattr(p, "email", "N/A")) # N/A — default if not found
# getattr with strings (dynamic attribute access)
attr_name = "name"
print(getattr(p, attr_name)) # Alice
setattr() — Set an Attribute Value
class Person:
def __init__(self, name):
self.name = name
p = Person("Alice")
setattr(p, "age", 30)
print(p.age) # 30
# Dynamic attribute setting
for key, value in [("city", "NYC"), ("job", "Engineer")]:
setattr(p, key, value)
print(p.city) # NYC
print(p.job) # Engineer
delattr() — Delete an Attribute
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Alice", 30)
print(hasattr(p, "age")) # True
delattr(p, "age")
print(hasattr(p, "age")) # False
# Also works with del keyword
p.name = "Bob"
del p.name
print(hasattr(p, "name")) # False
hash() — Get Hash Value of an Object
print(hash(42)) # 42
print(hash("hello")) # some integer
print(hash((1, 2, 3))) # some integer
# Mutable objects are not hashable
# hash([1, 2]) # TypeError: unhashable type: 'list'
# hash({1: "a"}) # TypeError: unhashable type: 'dict'
# Custom hash
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __hash__(self):
return hash((self.x, self.y))
p1 = Point(1, 2)
p2 = Point(1, 2)
print(hash(p1) == hash(p2)) # True
repr() and str() — String Representations
# str() — readable string (for users)
print(str(42)) # "42"
print(str(3.14)) # "3.14"
print(str("hello")) # "hello"
print(str([1, 2, 3])) # "[1, 2, 3]"
# repr() — unambiguous string (for developers/debugging)
print(repr(42)) # "42"
print(repr(3.14)) # "3.14"
print(repr("hello")) # "'hello'" — includes quotes!
print(repr([1, 2])) # "[1, 2]"
# The difference matters most with strings
s = "Hello\nWorld"
print(str(s)) # Hello
# World
print(repr(s)) # 'Hello\nWorld'
# In f-strings
print(f"{s!r}") # 'Hello\nWorld'
print(f"{s!s}") # Hello
int(), float(), complex() — Numeric Constructors
# int() — convert to integer
print(int("42")) # 42
print(int(3.9)) # 3 (truncates, does NOT round!)
print(int("1010", 2)) # 10 (binary string)
print(int("FF", 16)) # 255 (hex string)
print(int(True)) # 1
print(int(False)) # 0
# float() — convert to float
print(float("3.14")) # 3.14
print(float(42)) # 42.0
print(float("inf")) # inf
print(float("nan")) # nan
# complex() — create complex numbers
print(complex(3, 4)) # (3+4j)
print(complex("1+2j")) # (1+2j)
print(complex(5)) # (5+0j)
bool() — Convert to Boolean
# Falsy values
print(bool(0)) # False
print(bool(0.0)) # False
print(bool(0j)) # False
print(bool("")) # False
print(bool([])) # False
print(bool(())) # False
print(bool({})) # False
print(bool(set())) # False
print(bool(None)) # False
print(bool(range(0))) # False
# Truthy values
print(bool(1)) # True
print(bool(-1)) # True
print(bool("0")) # True — non-empty string!
print(bool([0])) # True — non-empty list!
print(bool(" ")) # True — whitespace is a character!
bin(), oct(), hex() — Base Conversions
# bin() — to binary string
print(bin(10)) # "0b1010"
print(bin(255)) # "0b11111111"
# oct() — to octal string
print(oct(10)) # "0o12"
print(oct(255)) # "0o377"
# hex() — to hexadecimal string
print(hex(10)) # "0xa"
print(hex(255)) # "0xff"
chr() and ord() — Character/Code Conversion
# chr() — integer to character
print(chr(65)) # "A"
print(chr(97)) # "a"
print(chr(8364)) # "€" (Euro sign)
print(chr(0x1F600)) # "😀" (emoji)
# ord() — character to integer
print(ord("A")) # 65
print(ord("a")) # 97
print(ord("€")) # 8364
print(ord("😀")) # 128512
abs(), round(), pow(), divmod() — Math Operations
# abs() — absolute value
print(abs(-5)) # 5
print(abs(3.14)) # 3.14
print(abs(-3+4j)) # 5.0 (magnitude of complex)
# round() — round to N decimal places
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
# 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
# divmod() — returns (quotient, remainder)
print(divmod(17, 5)) # (3, 2) because 17 = 3*5 + 2
print(divmod(10.5, 3)) # (3.0, 1.5)
bytes(), bytearray(), memoryview() — Binary Constructors
# bytes() — create immutable bytes
print(bytes(5)) # b'\x00\x00\x00\x00\x00' (5 zero bytes)
print(bytes([65, 66, 67])) # b'ABC'
print(bytes("hello", "utf-8")) # b'hello'
# bytearray() — create mutable bytearray
ba = bytearray(5)
print(ba) # bytearray(b'\x00\x00\x00\x00\x00')
ba[0] = 72
print(ba) # bytearray(b'H\x00\x00\x00\x00')
# memoryview() — zero-copy view
data = b"Hello, World!"
mv = memoryview(data)
print(mv[0]) # 72 (H)
print(mv[0:5].tobytes()) # b'Hello'
format() — Format Values
# Positional
print(format(42, "d")) # "42"
print(format(3.14159, ".2f")) # "3.14"
print(format(255, "x")) # "ff"
print(format(255, "08b")) # "11111111"
# Using the built-in format() function
print(format(1234567, ",")) # "1,234,567"
print(format(0.856, "%")) # "85.600000%"
print(format(0.856, ".1%")) # "85.6%"
Type Checking and Conversion
Checking Types
x = 42
print(type(x)) # <class 'int'>
print(isinstance(x, int)) # True — preferred for most cases
# isinstance() recognizes subclasses; type() does not
class MyInt(int): pass
num = MyInt(42)
print(type(num) == int) # False
print(isinstance(num, int)) # True
Type Conversion Rules
| Function | Example | Result | Notes |
|---|---|---|---|
int("42") | "42" -> 42 | int | int(3.9) -> 3 (truncates, not rounds) |
float("3.14") | "3.14" -> 3.14 | float | float(5) -> 5.0 |
str(42) | 42 -> "42" | str | str(True) -> "True" |
bool("") | "" -> False | bool | See truthy/falsy section |
list("abc") | "abc" -> ["a","b","c"] | list | |
tuple([1,2]) | [1,2] -> (1,2) | tuple | |
set([1,1,2]) | [1,1,2] -> {1, 2} | set | Duplicates removed |
dict([("a",1)]) | [("a",1)] -> {"a":1} | dict | List of pairs |
Common Gotchas
print(bool("False")) # True — non-empty string!
print(bool("0")) # True — non-empty string!
print(int("3.14")) # ValueError — use int(float("3.14")) -> 3
# Safe conversion pattern
def safe_int(value, default=0):
try:
return int(value)
except (ValueError, TypeError):
return default
print(safe_int("hello")) # 0
print(safe_int("42")) # 42
Constants in Python
Python has several built-in constants:
| Constant | Value | Description |
|---|---|---|
True | bool | Boolean true |
False | bool | Boolean false |
None | NoneType | Absence of value |
__debug__ | bool | True if not running with -O flag |
print(True) # True
print(False) # False
print(None) # None
# __debug__ — True in normal mode, False with python -O
print(__debug__) # True
# None is the canonical "null" value
x = None
print(x is None) # True
print(x == None) # True, but use `is` instead!
Dynamic Typing Explained
Variables don't have fixed types — the objects they reference do. Reassignment changes which object the name refers to.
x = 42
x = "Hello" # Now refers to a string
x = [1, 2, 3] # Now refers to a list
# is vs ==
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(a == b) # True — same value
print(a is b) # False — different objects
print(a is c) # True — same object
# Small integer caching: Python caches -5 to 256
x = 256; y = 256
print(x is y) # True — same cached object
x = 257; y = 257
print(x is y) # False (usually) — different objects
Variable Naming Rules and Best Practices
| Element | Convention | Example |
|---|---|---|
| Variables | snake_case | user_name, total_count |
| Functions | snake_case | get_user(), calculate_total() |
| Classes | PascalCase | UserProfile, DataProcessor |
| Constants | UPPER_SNAKE_CASE | MAX_RETRIES, PI |
| Private | Leading underscore | _internal_var |
| Module | snake_case | my_module.py |
# Valid names
_name = "valid"
user_name = "valid"
_count2 = "valid"
__double_underscore = "valid"
# Invalid names
# 2name = "invalid" # Starts with number
# my-var = "invalid" # Hyphen
# class = "invalid" # Reserved keyword
import keyword
print(keyword.kwlist) # All reserved keywords
Memory and Variables
Reference Counting
import sys
a = [1, 2, 3]
print(sys.getrefcount(a)) # 2 (variable + function call argument)
b = a
print(sys.getrefcount(a)) # 3
del b
print(sys.getrefcount(a)) # 2
Shallow vs Deep Copy
import copy
# Shallow copy — copies references, not objects
original = [[1, 2], [3, 4]]
shallow = original.copy()
shallow[0][0] = 99
print(original[0][0]) # 99 — nested list is shared!
# Deep copy — recursively copies everything
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)
deep[0][0] = 99
print(original[0][0]) # 1 — original is safe
Common Mistakes
1. Mutable Default Arguments
# ❌ WRONG — default list shared across calls
def append_to(item, target=[]):
target.append(item)
return target
print(append_to(1)) # [1]
print(append_to(2)) # [1, 2] — unexpected!
# ✅ CORRECT
def append_to_fixed(item, target=None):
if target is None:
target = []
target.append(item)
return target
2. Floating-Point Comparison
# ❌ WRONG
if 0.1 + 0.2 == 0.3: print("Equal")
# ✅ CORRECT
import math
if math.isclose(0.1 + 0.2, 0.3): print("Equal")
3. Modifying a List While Iterating
# ❌ WRONG — skips elements
numbers = [1, 2, 3, 4, 5]
for n in numbers:
if n % 2 == 0:
numbers.remove(n)
# ✅ CORRECT — list comprehension
numbers = [n for n in numbers if n % 2 != 0]
4. Confusing = with ==
x = 5 # Assignment
if x == 5: # Comparison — two equals signs
print("x is 5")
5. Using is for Value Comparison
# ❌ WRONG — may fail due to interning behavior
x = "hello"
if x is "hello":
print("match")
# ✅ CORRECT — use == for value comparison
if x == "hello":
print("match")
# ✅ CORRECT use of `is` — checking for None
if x is None:
print("no value")
Practice Exercises
Exercise 1: Type Explorer
Write a function that accepts a string and converts it to the most appropriate type.
Exercise: Create identify_and_convert(value) that returns True/False for "True"/"False", an int for integer strings, a float for float strings, and the original string otherwise.
See Solution
def identify_and_convert(value):
if value.lower() == "true":
return True
if value.lower() == "false":
return False
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
return value
print(identify_and_convert("True")) # True
print(identify_and_convert("42")) # 42
print(identify_and_convert("3.14")) # 3.14
print(identify_and_convert("hello")) # "hello"
Exercise 2: Variable Swap and Conversion
Exercise: Given a = 10, b = "20", c = 30.0: swap a and c, convert b to int, build a list of all three, and print each element's type.
See Solution
a = 10
b = "20"
c = 30.0
a, c = c, a # Swap
b = int(b) # Convert
my_list = [a, b, c]
for i, item in enumerate(my_list):
print(f"Element {i}: value={item}, type={type(item).__name__}")
# Element 0: value=30.0, type=float
# Element 1: value=20, type=int
# Element 2: value=10, type=int
Exercise 3: Conversion Chain
Exercise: Write conversion_chain(value) that converts any value to string, list, bool, and numeric (if possible), printing each step.
See Solution
def conversion_chain(value):
print(f"Original: {value!r} ({type(value).__name__})")
print(f" String: {str(value)!r} (len {len(str(value))})")
print(f" List: {list(str(value))}")
print(f" Bool: {bool(value)}")
try:
print(f" Int: {int(value)}")
except (ValueError, TypeError):
try:
print(f" Float: {float(value)}")
except (ValueError, TypeError):
print(" Numeric: not possible")
print()
conversion_chain(42)
conversion_chain("Hello")
conversion_chain(None)
Exercise 4: Attribute Manipulation
Write a function that creates an object, sets attributes dynamically, and retrieves them.
def attribute_demo():
class Dynamic:
pass
obj = Dynamic()
# Set attributes dynamically
attrs = {"name": "Alice", "age": 30, "city": "NYC"}
for key, value in attrs.items():
setattr(obj, key, value)
# Retrieve and display
for key in attrs:
print(f"{key} = {getattr(obj, key)}")
# Check existence
print(f"Has 'name': {hasattr(obj, 'name')}")
print(f"Has 'email': {hasattr(obj, 'email')}")
# Delete an attribute
delattr(obj, "city")
print(f"Has 'city' after delete: {hasattr(obj, 'city')}")
attribute_demo()
Key Takeaways
- Python variables are names, not containers. They reference objects in memory. The object has a type; the variable does not.
- Dynamic typing makes Python flexible but requires discipline. Always ensure variables hold the expected type in larger programs.
- Use
isinstance()overtype()for type checking when you want to support inheritance hierarchies. - Be mindful of mutability. Lists, dicts, and sets are shared by reference. Use
.copy()or slicing for independent copies. - Prefer
math.isclose()for floating-point comparison instead of==. IEEE 754 arithmetic introduces precision errors. - Follow PEP 8 naming conventions.
snake_casefor variables/functions,PascalCasefor classes,UPPER_SNAKE_CASEfor constants. hash()is essential for dict keys and sets. Only immutable (hashable) objects can be dictionary keys or set elements.repr()gives unambiguous output;str()gives human-readable output. Userepr()in debugging.hasattr(),getattr(),setattr(),delattr()enable dynamic attribute manipulation without knowing the class structure at write time.- Booleans are subclasses of integers.
True == 1andFalse == 0, so boolean arithmetic works:True + True == 2.