2 Objects and Variables
Python programs operate on different kinds of objects. An object is a fundamental concept in Python - it’s a self-contained unit that combines both data (values) and the operations that can be performed on that data. Every value in Python is an object, whether it’s a simple number, a piece of text, or something more complex.
We can think of an object as a region of memory whose contents represent a value of a certain kind. For example, an integer object representing the value 232
is stored in memory as a pattern of binary digits (zeros and ones). A string object would have a more complex memory representation. However, as Python programmers, we don’t need to worry about these internal representations. Instead, we focus on:
- What kind of data the object holds (its type)
- What operations we can perform on it (its methods and attributes)
- How we can use it in our programs
The language provides us with certain basic kinds of objects (objects of primitive types) that serve as building blocks. We can then make more complicated objects by combining these primitives in various ways. This object-based approach gives Python programs their flexibility and power.
But before we start looking at the different kind of objects that Python deal with, lets look at the way Python gives names to objects: variables.
2.1 Variables and Object Binding
In Python, variables are names that bind to objects. When you create a variable, you’re creating a name that refers to an object in memory. This binding is established using the assignment operator =
. For example:
= 42 # Creates a binding between name 'x' and integer object 42
x = "Hello" # Binds 'message' to string object "Hello" message
Variables can be used anywhere their bound objects could be used. For instance:
= 5
number = abs(-number) # Using variable 'number' with built-in abs() function
result print(result) # Outputs: 5
= "hello"
text = text.upper() # Using variable with string method
upper_text print(upper_text) # Outputs: HELLO
A key feature of Python variables is that they can be rebound to different objects at any time:
= 42 # x binds to integer 42
x = "Hello" # x now binds to string "Hello"
x = 3.14 # x now binds to float object x
Multiple variables can bind to the same object:
= "Hello" # Creates a string object and binds 'a' to it
a = a # Binds 'b' to the same string object
b print(len(a)) # Outputs: 5
print(len(b)) # Outputs: 5 (both reference the same string)
Assignment creates new bindings but doesn’t copy objects. When working with primitive types like numbers and strings, this distinction is less important since they are immutable - their values cannot be changed after creation.
2.1.1 Variable Names and Naming Conventions
Variable names in Python must follow certain rules:
- Names must start with a letter (a-z, A-Z) or underscore (_)
- After the first character, names can contain letters, numbers, and underscores
- Names are case-sensitive (
temperature
andTemperature
are different variables) - Names cannot be Python keywords like
if
,for
,while
,True
, etc.
# Valid variable names
= 98.6
temperature = "secret"
_hidden = 42
counter2 = "Alice"
first_name
# Invalid variable names
2counter = 42 # Cannot start with number
-value = 10 # Cannot use hyphen
myclass = "Math" # Cannot use Python keyword
Python follows these naming conventions (PEP 8):
- Use lowercase letters for most variable names:
temperature
,count
,name
- Use underscores to separate words in longer names:
first_name
,room_temperature
- Start with underscore for non-public variables:
_internal_value
- Use meaningful names that describe the data:
temperature
rather thant
Good variable naming practices:
- Choose descriptive names that indicate the variable’s purpose
- Use full words rather than abbreviations (except for common ones)
- Make names as long as needed to be clear, but not longer
# Good variable names
= 98.6
temperature = "Alice"
first_name = 5
room_count
# Poor variable names (avoid these)
= 98.6 # Too short, unclear meaning
t = "Alice" # Unclear abbreviation
fn = 5 # Uninformative name x
2.2 Primitive object types and operations on them
Python is a richly-typed language, meaning it comes with many different kinds of objects to represent different types of data and operations. While programmers can create their own custom object types (which we’ll explore later), Python provides a set of fundamental building blocks called primitive types. These are the basic data types built into the language itself, like numbers and strings.
Think of primitive types as the atoms from which we build more complex molecules. Just as all matter is made of fundamental particles, all Python programs are built using these primitive types, either directly or by combining them into more complex structures. Understanding these primitive types is essential because:
- They represent the basic kinds of data most programs need to work with
- They provide the foundation for building more complex data structures
- They implement the fundamental operations that more complex objects build upon
2.2.1 Number types
Python supports integers, which are whole numbers like 3
, -7
, or 2024
. Operations on integers include addition (3 + 2
), subtraction (10 - 4
), multiplication (6 * 7
), and division (8 / 2
).
The exponentiation operator (**
) allows you to raise a number to a power. For example, 2 ** 3
evaluates to 8
(2 raised to the power of 3). The modulo operator (%
) computes the remainder of division. For instance, 10 % 3
results in 1
, as 10 divided by 3 leaves a remainder of 1.
Floating point numbers represent real numbers and can include decimals, such as 3.14
, -0.001
, or 2.0
. They support similar operations to integers but are subject to precision limitations. For example, 0.1 + 0.2
may not precisely equal 0.3
due to floating-point representation. Special values like Inf
(infinity) and NaN
(not a number) exist to handle certain mathematical scenarios. Here are some examples of operations that produce these values:
# Division by zero resulting in infinity
1.0 / 0.0) # Results in inf
(
# Negative division by zero resulting in negative infinity
-1.0 / 0.0) # Results in -inf
(
# Exponentiation producing NaN
0.0 ** -1) # Results in nan
(
# Invalid operations also producing NaN
float('nan') # Results in nan
# Negative division by zero resulting in negative infinity
# Outputs: -inf
For advanced mathematical operations, Python provides the math
library, which you can import using import math
.
2.2.2 Importing Libraries and the Math Library
Python includes a vast collection of additional functionality bundled into packages, which are collections of related modules. A module is a single Python file containing code (such as functions and classes) that provides specific functionality. A package is a higher-level structure—a collection of related modules grouped together to organize code. When working with Python’s standard library or third-party libraries, you will often encounter both terms.
When you import a module, Python creates a namespace for that module. A namespace is a container that holds the module’s functions, variables, and classes, ensuring they do not conflict with names in other parts of your program. Modules provide namespaces, and packages allow for hierarchical organization of these modules into logical groupings.
For example, the math
module provides mathematical functions and constants, accessible through its namespace, using the .
syntax.
Here is how it works:
import math
# Using functions from the math library
print(math.sqrt(16)) # Outputs: 4.0
print(math.pi) # Outputs: 3.141592653589793
print(math.sin(math.pi / 2)) # Outputs: 1.0
The above example demonstrates key aspects of working with the math
module:
- The
.
syntax is used to access attributes or functions within a module. For example,math.sqrt
refers to thesqrt
function (square root) in themath
namespace. math.pi
is a constant provided by the module, representing the mathematical constant π (pi).math.sqrt
and other functions in the module are invoked using parentheses, following the same logic as mathematical functions, e.g.,sqrt(x)
.- Comments in Python, indicated by
#
, are used to annotate the code and explain its purpose, helping improve readability and maintainability. The rest of the line after the#
is ignored by the interpreter.
The import
statement can also be tailored. You can import specific functions or variables, like this:
from math import sqrt, pi
print(sqrt(25)) # Outputs: 5.0
print(pi) # Outputs: 3.141592653589793
Alternatively, you can alias imports for brevity:
import math as m
print(m.cos(0)) # Outputs: 1.0
print(m.e) # Outputs: 2.718281828459045
The math
library provides a variety of functions beyond basic arithmetic, such as trigonometric functions (sin
, cos
), logarithms (log
), and constants like e
and pi
. By leveraging the standard library, you can perform complex calculations efficiently and effectively.
2.2.3 Unicode strings
Unicode is a standard for encoding text in various writing systems, covering diverse writing systems including most human scripts, mathematical symbols, and even emojis. In Python, strings are Unicode by default. You can define strings using single quotes ('example'
), double quotes ("example"
), or triple quotes ('''example'''
or """example"""
). Triple quotes allow strings to span multiple lines, making them useful for multi-line text or documentation within the code. Here is an example of a multi-line string:
"""This is an example of a
multi-line string. It spans
several lines."""
"""This is an example of a
multi-line string. It spans
several lines."""
You can mix quote types to include one kind of quote inside another, like 'She said, "Hello!"'
.
For example, Unicode allows for strings such as "你好"
(Chinese), "مرحبا"
(Arabic), "🙂"
(emoji), and "αβγ"
(Greek).
Concatenation refers to the operation of joining two or more strings together. In Python, this is achieved using the +
operator. For example, 'Hello' + ' World'
results in 'Hello World'
. This operation creates a new string by appending the contents of the second string to the first.
2.2.3.1 String Methods
Methods are functions that belong to and operate on specific types of objects. They are a key part of object-oriented programming, where data (the object) and the operations that can be performed on that data (the methods) are bundled together. Methods are accessed using the .
syntax and are applied directly to the object. When you call a method on an object, that object is automatically passed to the method as its first argument (often called self
in Python).
Here are examples of commonly used string methods:
- Changing Case: The
.upper()
and.lower()
methods convert strings to uppercase and lowercase, respectively.
"hello".upper() # Outputs: HELLO
"WORLD".lower() # Outputs: world
- Trimming Whitespace: The
.strip()
method removes whitespace from the beginning and end of a string.
" text ".strip() # Outputs: text
- Replacing Substrings: The
.replace()
method replaces occurrences of a substring with another.
"apples and bananas".replace("apples", "oranges") # Outputs: oranges and bananas
These methods allow for powerful string manipulations, making them essential tools in Python programming.
2.2.4 Booleans
Boolean values, named after mathematician George Boole, represent logical truth values and are either True
or False
. These values form the foundation of logical decision-making in programming. Just as we use logic in everyday reasoning (“if it’s raining AND I’m going outside, THEN I need an umbrella”), programs use boolean logic to make decisions and control their flow.
The comparison operator ==
checks for equality, while the single equals sign =
is reserved for assignment, which will be covered later. You can obtain boolean values through comparison operators, such as 5 > 3
(True), 4 == 4
(True), and 7 < 2
(False). These comparisons work with numbers, strings, and other types of objects, though the meaning of comparison may vary depending on the type.
Python provides three fundamental boolean operators: - and
: Returns True only if both operands are True - or
: Returns True if at least one operand is True - not
: Reverses the logical value of its operand
These operators follow the classical rules of Boolean logic. When evaluating boolean expressions, Python uses “short-circuit evaluation” - it stops evaluating as soon as the final result is determined. For example, in an and
operation, if the first operand is False, Python knows the result must be False regardless of the second operand.
# Short-circuit evaluation example
= 0
x = x != 0 and (10/x > 1) # Safely evaluates to False, avoiding division by zero result
Python provides a complete set of comparison operators that produce boolean results: - <
less than - >
greater than - <=
less than or equal to - >=
greater than or equal to - ==
equal to - !=
not equal to
When combining boolean operations, Python follows standard operator precedence: not
has the highest precedence, followed by and
, then or
. Using parentheses can make complex expressions clearer and ensure intended evaluation order.
Complex boolean expressions can be constructed by nesting these operators:
# A nested boolean expression
5 > 3) and (2 < 4) or not (7 == 8) # Outputs: True
(
# Another complex boolean expression
10 > 5) or ((3 == 2) and not (4 < 1)) # Outputs: True (
These concepts are foundational for decision-making in Python programs.
2.3 Working with Dates and Time
Working with dates and times in programming requires specialized tools because these concepts are more complex than they might first appear. Consider these challenges:
- Dates and times follow irregular patterns (months have different lengths, leap years occur, daylight saving time shifts)
- Time zones and international date lines make global time coordination complex
- Many different date formats exist across cultures and regions
- Date arithmetic isn’t as simple as regular number arithmetic (what’s “one month” after January 31st?)
Python’s datetime
module provides comprehensive tools for handling these complexities. The module contains several key classes:
datetime
: Represents a specific point in time, including date and timedate
: Represents just a date (year, month, day)time
: Represents just a time (hour, minute, second, microsecond)timedelta
: Represents a duration or time difference
To work with dates, you first need to import the required classes:
from datetime import datetime, date, timedelta
The datetime
class provides several ways to create date objects: - datetime.now()
: Creates an object representing the current date and time - datetime(year, month, day, hour, minute)
: Creates an object for a specific point in time - datetime.strptime()
: Creates a datetime object by parsing a string according to a specified format
Time intervals are represented using timedelta
objects, which allow you to: - Add or subtract periods of time from dates - Calculate differences between dates - Express durations in various units (days, seconds, microseconds)
Date formatting uses special codes called format directives. Common directives include: - %Y
: Four-digit year - %m
: Month as a zero-padded number - %d
: Day of the month as a zero-padded number - %H
: Hour (24-hour clock) - %M
: Minute - %S
: Second
Here’s a practical example combining these concepts:
from datetime import datetime
= datetime.now()
current_time = current_time.strftime('%Y-%m-%d') # Creates date string like "2024-01-15" formatted_date
When working with dates, be mindful of: - Time zones (the datetime module includes timezone support) - Date arithmetic across month/year boundaries - Leap years and other calendar complexities
2.4 Interpolating variables in strings
When writing programs, we often need to combine text with the values of our variables to create meaningful messages. Python provides a special kind of string called an f-string (short for “formatted string”) that makes this easy to do.
To create an f-string, you put the letter ‘f’ right before the opening quote of your string. Then, inside the string, you can put variable names or expressions inside curly braces {}. Python will replace these with their actual values. Here’s a simple example:
= "Alice"
name = 25
age # The 'f' before the quotes makes this an f-string
print(f"My name is {name} and I am {age} years old")
When you run this code, Python will: 1. See the ‘f’ before the string and know this is an f-string 2. Look for any curly braces {} in the string 3. Take what’s inside the braces and replace it with its value 4. So {name} becomes “Alice” and {age} becomes “25” The output will be: My name is Alice and I am 25 years old
You can do calculations inside the curly braces too. This is helpful when you need to show results:
= 49.99
price = 3
quantity # The calculation price * quantity happens inside the braces
print(f"Total cost: ${price * quantity}") # Shows: Total cost: $149.97
You can even use methods (special functions that belong to objects) inside the braces:
= "hello"
greeting # The upper() method converts text to uppercase
print(f"Uppercase greeting: {greeting.upper()}") # Shows: Uppercase greeting: HELLO
f-strings are the modern and recommended way to put variables into strings in Python. They make your code easier to read because you can see exactly where the variables will go, and they’re less prone to errors than older methods of string formatting.
For numbers, you can control how they’re displayed using a format specifier after a colon inside the braces:
= 3.14159
pi # Show only 2 decimal places using :.2f
print(f"Pi rounded to 2 decimal places: {pi:.2f}") # Shows: Pi rounded to 2 decimal places: 3.14
Let’s break down the format specifier .2f
: - The colon :
tells Python that what follows is a format specification - The .2
means “show 2 digits after the decimal point” - The f
indicates we want to format this as a floating-point number
This is particularly useful when dealing with money, measurements, or any situation where you want to control how many decimal places to display. For example:
= 19.99999
price print(f"The item costs ${price:.2f}") # Shows: The item costs $20.00
Here are more examples of f-strings with different format specifications:
# Padding numbers with zeros
= 12345
account print(f"Account #: {account:08d}") # Shows: Account #: 00012345
# Percentage formatting
= 0.8523
ratio print(f"Progress: {ratio:.1%}") # Shows: Progress: 85.2%
# Scientific notation
= 1234567.89
large_num print(f"Scientific: {large_num:e}") # Shows: Scientific: 1.234568e+06
# Aligning text
= "Bob"
name = 95
score print(f"|{name:<10}|{score:>5}|") # Shows: |Bob | 95|
# Date formatting
from datetime import datetime
= datetime.now()
now print(f"Date: {now:%Y-%m-%d}") # Shows current date like: Date: 2025-01-13
2.5 Type and Value Errors
Python will raise errors when operations or functions are used incorrectly. Two common types of errors are TypeError
and ValueError
.
- Type Errors: These occur when an operation is applied to an object of an inappropriate type. For example:
# Trying to add a string and a number
"Hello" + 5 # Raises TypeError: can only concatenate str (not "int") to str
# Using a string with a numeric operation
len(10) # Raises TypeError: object of type 'int' has no len()
- Value Errors: These occur when a function receives an argument of the correct type but an invalid value. For example:
# Invalid conversion from string to integer
int("abc") # Raises ValueError: invalid literal for int() with base 10: 'abc'
# Square root of a negative number
import math
-1) # Raises ValueError: math domain error math.sqrt(
Understanding these errors helps in debugging and writing robust Python programs.
2.6 Exercises
Variable Binding
What will be printed? Explain why:
= 42 x = x y = "Hello" x print(y) # What value is y bound to?
String Operations
Given these strings:
= "HELLO" text1 = "world" text2 = " Python " text3
What will each line print?
print(text1.lower()) print(text2.upper()) print(text3.strip())
Numbers and Math
What is the result of each calculation?
= 10 x = 3 y print(x + y) # Addition print(x - y) # Subtraction print(x * y) # Multiplication print(x / y) # Division print(x ** y) # Exponentiation
Math Module
Import math and predict these results:
import math print(math.pi) # What is π? print(math.sqrt(25)) # Square root of 25 print(math.ceil(3.1)) # Ceiling of 3.1
Boolean Values
What will these print? Why?
print(5 > 3) # Greater than print(10 == 10) # Equality print(True and False) # AND operation print(True or False) # OR operation print(not True) # NOT operation
String Formatting
What will these f-strings display?
= "Alice" name = 25 age = 3.14159 pi print(f"Name: {name}") print(f"Age next year: {age + 1}") print(f"Pi to 2 decimals: {pi:.2f}")
Type Checking
What type will each variable have?
= 42 a = "42" b = 3.14 c = True d print(type(a)) print(type(b)) print(type(c)) print(type(d))
Current Date
Using datetime, what information will this show?
from datetime import datetime = datetime.now() now print(f"Year: {now:%Y}") print(f"Month: {now:%m}") print(f"Day: {now:%d}")