Scope of the Documentation
This document is going to cover all of the topics that were covered during the presentation and/or are mentioned in the Powerpoint Presentation in the TPL group.
Topics¶
- History of Python: Understanding the origins and evolution of Python.
- Major Computational Models: Python uses an insane amount of models but our main focus will remain limited to the models covered during the course, for the most part. The models covered are listed below:
- Imperative Computational Model
- Logical Computational Model
- Functional Computational Model
- Object-Oriented Computational Model
- Syntax and Semantics: We will cover everything discussed about syntax and semantics in the course along with several points that are either worth discussing or we found interesting. Here is an outline for this section:
- Case Sensitivity
- Statement Endings
- Comments
- Indentation
- Dynamic Typing
- Multiple Assignment Statement
- Conditional Statements
- Loops
- Functions
- Importing Libraries
- String Manipulation
- Classes and Objects
- Operators
- Precedence
- Associativity
- Implicit and Explicit Type Casting
- Algebraic Semantics
- Axiomatic Semantics
- Denotational Semantics
- Operational Semantics
- Python Script Compilation Process: We will be discussing the how the Python Source Code is converted into object code and ultimately executed.
- Pragmatics: We will be discussing the real-world applicability of Python and the potential constraints that come with it. The points we will cover about Python's Pragmaticism include the following:
- The Zen of Python
- Readability
- Dynamic typing
- Interoperability
- Modularity
- Dedicated Interactive Environment
- Real-World Applications:
- Data Science
- Web Development
- Game Programming
- Automation and Scripting
- Comparison and Potential Improvements: We will be comparing Python with C++ and Java primarily since these languages share the same dominant computational model. We will compare several aspects of each language such as:
- Syntax (Readability): How easy it is to read/write the code.
- Performance (Speed): How fast the language executes.
- Memory Consumption: How efficiently memory is managed.
- Type System: Static vs. Dynamic typing.
- Compilation/Execution: How the code is processed.
- Libraries & Ecosystem: Availability and variety of libraries/frameworks.
- Concurrency/Parallelism: Multi-threading and handling parallel tasks.
- Platform Independence: How easily the code runs across platforms.
- Error Handling: Mechanisms for handling exceptions.
- Use Cases: Popular applications of each language.
Readers will have gained a holistic understanding of Python and its versatile capabilities by the end of this document (if you get to the end that is :). If you already know a bit of Python or wanna start learning than useful links will also be provided from where you can start your learning journey.
# Just some code to show the version of Python that I'm ganna be using for the code examples etc.
import sys
print(f"All the code executed in this file is running on Python: \033[36m{sys.version}\033[0m unless explicitly specified.")
All the code executed in this file is running on Python: 3.13.1 (tags/v3.13.1:0671451, Dec 3 2024, 19:06:28) [MSC v.1942 64 bit (AMD64)] unless explicitly specified.
History of Python
Origins and Motivations¶
Python was developed by Guido van Rossum (Contents Articles, n.d.) in 1989 at Centrum Wiskunde & Informatica (CWI) in the Netherlands. The language emerged from Van Rossum’s desire to create a programming language that was both simple and highly readable. Inspired by the ABC programming language(PL), Python aimed to address issues of complexity while maintaining ease of use. ABC programming language was a language tha evolved from BASIC (which is another PL) primarily used to teach programming fundamentals btw.
Van Rossum’s motivation stemmed from a need for a scripting language capable of handling system administration tasks while offering a clean, structured, and engaging coding experience (Tulchak & Маrchuk, n.d.). Interestingly, the name "Python" was not derived from the snake but rather from Monty Python’s Flying Circus, a British comedy series, emphasizing Van Rossum’s intention to make programming enjoyable (this was interesting according to GPT, not us).
Major Milestones in Python's Evolution¶
- 1989: Python was first conceptualized as a holiday project by Van Rossum.
- 1991: The first public release of Python 1.0 happened which included features such as exception handling, core data types like lists and strings, and module support. It prioritized simplicity and gave a solid foundation for future growth.
- 1994: Establishment of the Python Software Foundation (PSF) which was tasked with managing Python’s open-source development and encouraging community contributions.
- 2000: Launch of Python 2.0 (alalqab.com, 2024) which introduced features such as list comprehensions, garbage collection, and improved Unicode support but it faced challenges due to backward compatibility issues, leading to divided adoption.
- 2008: Release of Python 3.0 (alalqab.com, 2024) which was a major overhaul aimed at addressing inconsistencies from the 2.x series (the previous versions). It introduced significant improvements such as enhanced integer division, a unified string type, and modernized input/output operations.
- 2010s: Python solidified its role in fields like data science and artificial intelligence. Libraries such as NumPy,pandas, and TensorFlow made it a lot more popular.
- 2020s: Continuous updates with performance optimizations. Latest stable release (as of December 23, 2024): Python 3.11, with Python 3.13.1 (alalqab.com, 2024) under active development, promising advancements in speed and functionality.
Current Day Status of Python¶
Python is consistently ranked as the number 1 language by popularity across the globe with perhaps the most use cases out of all the PLs (Srinath, n.d.).

Source: TIOBE Index
Python also has the largest market share out of all of the PLs. Although, it’s worth highlighting that JS is the most popular language among Professional Developers.

Source: Orient Software
Computational Models of Python
If your basics are a bit rough then just hit this link to revise what are in Programming Languages Computational Models. Python implements several programming paradigms (GeeksforGeeks, 2020), we are going to explore just a few below:
Imperative Computational Model¶
Imperative Programming Model is implemented into Python just like any other language (Denys Volokh, 2024). it has the same traits as usual:
- Sequential Execution.
- Variables can be reassigned throughout execution.
You can think of the imperative Model in Python as the sum of these 4 things:
- Sequence of statements
- Use of variables to store state
- Loops and conditional statements
- In-place modification of data
# here is a basic example of the imperative model in play, its just basic code for factorials.
def factorial(n):
result = 1
for i in range(1, n + 1): # Sequential loop, as in sequential execution
result *= i # Step-by-step state mutation, as in variables are changing their 'states'
return result
print(f"\033[31mThe factorial is: {factorial(5)}")
The factorial is: 120
Functional Computational Model¶
The Functional Model is all about immutability, and pure functions (GeeksforGeeks, 2020). Python supports functional programming through higher order functions for the most part but it also has pure functions and lambda expressions. Python may not be a purely functional language but its flexibility allows for a functional paradigm when required. Just to be clear, here is a quick summary of pure functions and higher order functions in case you guys need to revise it.
Pure functions have 2 primary traits:
- Always produces the same output for the same inputs.
- Has no side effects (does not modify global state or interact with external systems like I/O).
Higher Order Functions also have 2 major traits:
- Takes other functions as arguments, or
- Returns a function as its result, or
- both of the above
As for Lambda expressions, its a bit more complicated but the main traits to keep in mind are:
- Lambda functions are anonymous (they don't have a name basically).
- They are usually (almost always) single-line.
Another type of function that is part of the functional paradigm and is implemented in Python is the recursive function but those have already been covered in-depth in the previous semesters so we won't be going into detail on that.
# Here is a simple example of Pure Functions in Python
def add(x, y):
return x + y
# Always returns the same result for the same inputs.
a = 10
b = 20
result = add(a, b)
print(f"\033[31mThe sum of {a} and {b} is: {result}")
The sum of 10 and 20 is: 30
# Here is an example of Higher Order Functions in Python matee
# this function is ganna accept a function as an argument (a trait of higher order functions)
def apply_to_each(function, data):
result = [] # this is a list, lists are a simple collection data type in Python, like arrays are in C++. This documentation doesn't cover data structures so I'm not going to go into details
for item in data: # oh look, that looks like a foreach loop doesn't it?
result.append(function(item)) # append is basically adding the element into the list, just like how we insert stuff at the end of a queue
return result
# Defining the secondary function that we will pass in as the argument
def square(x):
return x ** 2 ## the ** is for exponentiation, as in power, btw
# Example data
numbers = [1, 2, 3]
# Use the higher-order function
squares = apply_to_each(square, numbers) # square is passed as an argument, which itself is a function bro
print(f"\033[31mThe list contains the squares of the numbers that we passed(1,2,3), which are: {squares}") # Output: [1, 4, 9]
The list contains the squares of the numbers that we passed(1,2,3), which are: [1, 4, 9]
# The following is an example of lambda expressions in python; remember, lambda expressions were the ones that didn't have a name and are only a single-line of code
x = 5
double = lambda x: x ** 2 # look, it doesn't have any explicit name, and its in a single-line
print(f"\033[31mSquare of {x} is: {double(x)}") # Output: 10
Square of 5 is: 25
Logical Computational Model¶
Logical Model doesn't necessarily focus on the control flow, instead it focuses on defining relationships and logic. Python supports logical programming through various libraries (modularity) such as pyswip and sympy or even through the prolog library. Just keep in mind that logical programming focuses primarily on the "what" to solve rather than "how" to solve it. Logical Programming is a declarative programming paradigm btw, and declarative programming paradigm is just another way of saying that we describe the "what" and not the "how". Think of how SQL works, You specify the desired result or rules; The system figures out the solution for you.
Here, some SQL code so you get the idea, its more about the "what" and less about the "how" (ik ik, mentioned it just about a thousand times, apologies for the repetition)
SELECT * FROM students WHERE grade > 90;
# We will import in some logical operators like Or and Not
from sympy import symbols, Or, Not
# But why? Doesn't Python already have the And, Or, Not operators etc? Well, yes it does, here is the issue though:
# Python’s and, or, not:
# Designed for immediate execution.
# Works directly with Boolean values like True or False.
# Cannot handle symbols or expressions.
# The "A AND B" assumes that both A and B are booleans and evaluates that expression immediately
#################################################
# Logical Programming’s And, Or, Not:
# Designed for symbolic computation.
# Works with symbols that don’t have specific values (e.g., A, B).
# Useful for defining rules and constraints in declarative programming.
# The "A AND B" is a logical statement and A and B can be other then 0 and 1.
# Define some symbols:
Rain = symbols('Rain')
Umbrella = symbols('Umbrella') # we are ganna try to simulate a simple "if it rains, you need an umbrella"
# Now we define the logical rule.
rule = Or(Not(Rain), Umbrella) # this just means "[(NOT rain) OR (Umbrella)]" or like "either its not raining or I need an umbrella"
print(f"\033[31m{rule}")
Umbrella | ~Rain
Just so you can read the above example easily:
- | = OR
- ~ = NOT
- & = AND
from sympy import symbols, And, Or, Not
# lets run through a simple traffic light example.
# lets define our "what": "Only one light can be ON at one time"
# so we basically need 3 rules/constraints, one where only the Green light is on, one where only the red light is on, and one where only the yellow light is on. We just need to build logical rules where that would become true
# Define symbols like before:
Green = symbols('Green') # symbol to represent the green light
Yellow = symbols('Yellow') # the yellow light symbol
Red = symbols('Red') # The red light symbol
# Logical rules:
rule = Or(And(Not(Yellow), Not(Red), Green), # It's only green : [(NOT Yellow) AND (NOT Red) AND (Green)] so If both yellow and Red are OFF and Green is ON (hence only Green is on)
And(Not(Green), Not(Red), Yellow), # Or it's only yellow : [(NOT green) AND (NOT Red) AND Yellow] so if both Red and Green are OFF and Yellow is ON (hence only yellow is on)
And(Not(Yellow), Not(Green), Red)) # or its only red : [(NOT Yellow) AND (NOT Green) and (Red)] so if both Green and Yellow are OFF and Red is ON (hence only Red is on)
print(f"\033[31m{rule}")
# Output: (Green & ~Yellow & ~Red) | (Yellow & ~Green & ~Red)
(Green & ~Red & ~Yellow) | (Red & ~Green & ~Yellow) | (Yellow & ~Green & ~Red)
Object Oriented Computational Model¶
The Dominant Model of Python¶
The Object-Oriented Model (OOP) is a programming paradigm that revolves around the concept of "objects". These objects are instances of classes, which define a blueprint for their behavior and attributes, the same stuff as OOP that we have studied before. Python is inherently designed to support and encourage OOP Yigitaliev Ruzimatjon. (2024), making it the dominant computational model in the language. Although its important to point out that Python heavily follows the functional paradigm too, as you can probably tell from the above examples in the Functional Programming section.
Why is Object Oriented Model the dominant model in Python?¶
- Everything in Python, from a simple integer dtype variable to functions is implemented in the form of objects.
- OOP is fundamental to Python's Modularity and most libraries in Python are written in OOP style (eg: tensorflow is written in an OOP style)
Python implements all the concepts of OOP whether it be Encapsulation, Inheritance, Polymorphism, or Abstraction. I am going to provide examples of each but I won't explain each of them in-depth since these concepts have been covered in previous semesters, but I'll provide additional resources that will help you learn if you so wish.
Encapsulation¶
class BankAccount:
def __init__(self, balance=0): # constructors normally have the same name as the class name, but not in Python. in Python, all constructors use a keyword __init__
self.__balance = balance # Private attribute (it will take forever if I start explaining the code line by line so yeah, just ganna provide some resources for you guys later on, this is taking too long)
# methods being defined
def deposit(self, amount):
self.__balance += amount
def withdraw(self, amount):
if amount <= self.__balance:
self.__balance -= amount
else:
print("Insufficient funds!")
def get_balance(self):
return self.__balance
# Using the class
account = BankAccount() # object created
account.deposit(1000) # method called
account.withdraw(500) # another method called
print(f"\033[31m{account.get_balance()}")
500
Inheritance¶
class Vehicle:
def __init__(self, brand, wheels):
self.brand = brand
self.wheels = wheels
def display_info(self):
print(f"\033[31mBrand: {self.brand}, Wheels: {self.wheels}")
class Car(Vehicle): # Inheriting from Vehicle
def __init__(self, brand, wheels, fuel_type):
super().__init__(brand, wheels) # Calling the parent constructor
self.fuel_type = fuel_type
def display_info(self): # Method overriding
super().display_info()
print(f"Fuel Type: {self.fuel_type}")
# Using the classes
my_car = Car("Toyota", 4, "Petrol")
my_car.display_info()
Brand: Toyota, Wheels: 4
Fuel Type: Petrol
Polymorphism¶
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof Woof!"
class Cat(Animal):
def speak(self):
return "Meow Meow!"
animals = [Dog(), Cat()]
for animal in animals:
print(f"\033[31m{animal.speak()}")
Woof Woof! Meow Meow!
Abstraction¶
from abc import ABC, abstractmethod
class Shape(ABC): # Abstract Base Class
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
class Rectangle(Shape):
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
def perimeter(self):
return 2 * (self.length + self.width)
# Using the classes
rect = Rectangle(5, 3)
print("\033[31mArea:", rect.area())
print("\033[31mPerimeter:", rect.perimeter())
Area: 15 Perimeter: 16
Advanced OOP Concepts¶
Python implements almost all advanced OOP concepts as well, I will provide resources if you wanna learn about these. Here is the list of advanced OOP Concepts that are implemented in Python:
- Multiple Inheritance
- Interfaces
- Generics
- Operator/Method Overloading
- Method Overriding
- Composition
- Metaclasses
If you want to learn about these advanced concepts (among others) then here are some useful links:
Syntax of Python
Python's syntax heavily emphasizes readability and writeability. It tries to make life a lot easier for developers with syntax that resembles normal English words (for the most part) (W3Schools.com, 2024). The points regarding syntax that we will try to cover are as follows:
- Case Sensitivity (Var and var are not the same)
- Statement Endings
- Comments
- Indentation
- Dynamic Typing
- Multiple Assignment Statement
- Conditional Statements
- Loops
- Functions
- Importing Libraries
- String Manipulation
- Classes and Objects
- Operators
- Precedence
- Associativity
- Implicit and Explicit Type Casting
We will try to just cover all of these in code since thats much easier to explain, will also provide some theory where applicable. Lets try to make a tiny, and simple project like we used to make in our OOP Labs.
Here is what we will make:
Fantasy RPG (Role-Playing Game) Inventory and Battle System We will cover the following functionalities:
- There will be two classes: Player and Enemy.
- Player will have attributes like health, name, and also an inventory. Player will start off with a default inventory.
- Enemy will also have a name and health attribute.
- Define a function where the Player and the Enemy will fight and their healths will be dynamic, maybe let the player heal somehow too.
That will cover most of the basic syntax of Python.
import random as rd # this is how we import libraries in Python (10 and 2✅)
# you can see we don't need to use semi-colon or anything as a statement terminator
class Player: # this is a class in Python, and you can also see indentation in play here, the block of code is represented using indentation instead of braces (like in c++) (12 and 4✅)
"""This is a class that is going to represent the players in our RPG game.
Oh, and this is a multi-line comment btw, you just enclose multiple lines into 6 quotation marks, this multi-line comment is usually called a docstring""" # (3✅)
def __init__(self, name): # this is a constructor, which is a method in a class, which has identical syntax to functions for the most part (9 and 4✅)
self.name = name
self.health = 120
self.inventory = {"Sword": 1, "Battle Axe": 1, "Potion": 2} # default inventory
def drink_potion(self):
if (self.health < 200) and (self.inventory.get("Potion", 0) > 0):
self.health += 20
self.inventory["Potion"] -= 1
if self.health > 200:
self.health = 200
print(f"The mighty {self.name} just used a potion, their health has been increased to: {self.health}")
else:
print(f"My apologies Dear Knight, it seems as if you have ran out of potions.")
def show_inventory(self):
print("\nThis is your inventory good sir:")
for item, count in self.inventory.items(): # A for loop applied on the inventory dictionary (data structures are not in the scope of the documentation so not going to cover dictionaries)
print(f"{item}: {count}") # (8✅)
class Enemy:
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
def fight(player, enemy): # (9✅)
print(f"A frightening {enemy.name} has appeared out of the blue")
while player.health > 0 and enemy.health > 0: # while loop in action
print(f"\033[32m{player.name}'s health is {player.health}.")
print(f"\033[31m{enemy.name} has a health of {enemy.health}.")
action = input("Choose an action {player.name} (attack, heal, run)").strip().lower() # taking input from user, strip takes away the blank spaces and lower makes the entire input string lower case
if action == "attack": # (7✅)
damage = rd.randint(10, 30) # Dynamic typing
enemy.health -= damage
print(f"\033[32m{player.name} attacked {enemy.name} for {damage} damage!!!")
elif action == "heal":
player.drink_potion()
elif action == "run":
print(f"\033[35m{player.name} fled the battle! What a fricking coward am I right?")
return
else:
print("Invalid action. Try again.")
continue
# Enemy's turn
if enemy.health > 0:
damage = rd.randint(5, 45)
player.health -= damage
print(f"\033[31m{enemy.name} attacked {player.name} for {damage} damage!")
if player.health <= 0:
print(f"\n\033[31m{player.name} has been defeated by {enemy.name}...")
elif enemy.health <= 0:
print(f"\n\033[32m{enemy.name} has been defeated! Victoryyyy!")
def main():
print("\033[34mWelcome to the Fantasy RPG!\033[0m")
# Multiple assignment and also dynamic typing (5 and 6✅)
player_name, enemy_name = "Galileo", "Friedrich Nietzsche"
player = Player(player_name)
enemy = Enemy(enemy_name, health=100, attack=10)
# Swapping inventory items as an example (6✅)
player.inventory["Bow"], player.inventory["Sword"] = 1, player.inventory.pop("Sword")
# Show initial inventory
player.show_inventory()
# Start battle
fight(player, enemy)
# Run the program
if __name__ == "__main__":
main()
Welcome to the Fantasy RPG! This is your inventory good sir: Battle Axe: 1 Potion: 2 Bow: 1 Sword: 1 A frightening Friedrich Nietzsche has appeared out of the blue Galileo's health is 120. Friedrich Nietzsche has a health of 100. Galileo attacked Friedrich Nietzsche for 30 damage!!! Friedrich Nietzsche attacked Galileo for 41 damage! Galileo's health is 79. Friedrich Nietzsche has a health of 70. Galileo attacked Friedrich Nietzsche for 19 damage!!! Friedrich Nietzsche attacked Galileo for 9 damage! Galileo's health is 70. Friedrich Nietzsche has a health of 51. Galileo fled the battle! What a fricking coward am I right?
Lets quickly cover the few points that the above example didn't cover now¶
### Operators in Python (13✅)
# Arithmetic Operators
x = 10
y = 3
print("\033[34mArithmetic Operators\033[0m")
print("\033[31mAddition:", x + y) # 13
print("Subtraction:", x - y) # 7
print("Multiplication:", x * y) # 30
print("Division:", x / y) # 3.3333
print("Floor Division:", x // y) # 3
print("Modulus:", x % y) # 1
print("Exponentiation:", x ** y) # 1000
print("\033[32m=================================================================================")
# Comparison Operators
print("\033[34mComparison Operators\033[0m")
print("\033[31mEqual:", x == y) # False
print("Not Equal:", x != y) # True
print("Greater than:", x > y) # True
print("Less than:", x < y) # False
print("Greater or Equal:", x >= y) # True
print("Less or Equal:", x <= y) # False
print("\033[32m=================================================================================")
# Logical Operators
print("\033[34mLogical Operators\033[0m")
a = True
b = False
print("\033[31mAND:", a and b) # False
print("OR:", a or b) # True
print("NOT:", not a) # False
print("\033[32m=================================================================================")
# Assignment Operators
print("\033[34mAssignment Operators\033[0m")
z = 5
z += 2 # z = z + 2
print("\033[31mz after +=:", z) # 7
z *= 3 # z = z * 3
print("z after *=:", z) # 21
print("\033[32m=================================================================================")
# Membership Operators
print("\033[34mMembership Operators\033[0m")
my_list = [1, 2, 3, 4]
print("\033[31m2 in list:", 2 in my_list) # True
print("5 not in list:", 5 not in my_list) # True
print("\033[32m=================================================================================")
# Identity Operators
print("\033[34mIdentity Operators\033[0m")
a = [1, 2, 3]
b = a
c = [1, 2, 3]
print("\033[31ma is b:", a is b) # True
print("a is c:", a is c) # False
print("a == c:", a == c) # True
print("\033[32m=================================================================================")
### Operator Precedence (14✅)
print("\033[34mOperator Precedence\033[0m")
# Example of precedence
result = 2 + 3 * 5 ** 2 - 4 / 2
# Exponentiation first, then multiplication, division, addition, subtraction
print("\033[31mPrecedence result:", result) # 75.0
print("\033[32m=================================================================================")
### Associativity (15✅)
print("\033[34mAssociativity\033[0m")
# Left-to-right example
print("\033[31mLeft-to-right:", 5 - 3 - 1) # 1
# Right-to-left example (exponentiation)
print("Right-to-left:", 2 ** 3 ** 2) # 512
print("\033[32m=================================================================================")
### Implicit and Explicit Type Casting (16✅)
# Implicit Type Casting
print("\033[34mType Casting\033[0m")
x = 5 # Integer
y = 2.5 # Float
z = x + y # Python converts x to float implicitly
print("\033[31mImplicit Casting:", z, type(z)) # 7.5, float
# Explicit Type Casting
x = 10.7
y = "42"
z = int(x) + int(y) # Explicit conversion to int
print("\033[31mExplicit Casting:", z) # 52
print("\033[32m=================================================================================")
### String Manipulation (11✅)
print("\033[34mBasic String Manipulation\033[0m")
# Basic Operations
my_str = "This is taking foreverrrr!"
print("\033[31mUppercase:", my_str.upper())
print("Lowercase:", my_str.lower())
print("Replace:", my_str.replace("foreverrrr", "MOST CERTAINLY TAKING FOREVERRRRRRRRRRRRR")) # "Hello, World!"
print("Slice:", my_str[7:13])
# F-Strings
name = "David Corenswet"
age = 31
print(f"Superman's new name is {name} and he is {age} years old.")
# String Concatenation
first = "Good Morning!"
second = "How are you doing today Lois?"
print("Concatenation:", first + " " + second)
Arithmetic Operators Addition: 13 Subtraction: 7 Multiplication: 30 Division: 3.3333333333333335 Floor Division: 3 Modulus: 1 Exponentiation: 1000 ================================================================================= Comparison Operators Equal: False Not Equal: True Greater than: True Less than: False Greater or Equal: True Less or Equal: False ================================================================================= Logical Operators AND: False OR: True NOT: False ================================================================================= Assignment Operators z after +=: 7 z after *=: 21 ================================================================================= Membership Operators 2 in list: True 5 not in list: True ================================================================================= Identity Operators a is b: True a is c: False a == c: True ================================================================================= Operator Precedence Precedence result: 75.0 ================================================================================= Associativity Left-to-right: 1 Right-to-left: 512 ================================================================================= Type Casting Implicit Casting: 7.5 <class 'float'> Explicit Casting: 52 ================================================================================= Basic String Manipulation Uppercase: THIS IS TAKING FOREVERRRR! Lowercase: this is taking foreverrrr! Replace: This is taking MOST CERTAINLY TAKING FOREVERRRRRRRRRRRRR! Slice: takin Superman's new name is David Corenswet and he is 31 years old. Concatenation: Good Morning! How are you doing today Lois?
The point about precedence can be further clarified I think so here is a simple table:
Precedence | ||
---|---|---|
** Exponentiation | ||
Unary Plus, Minus | Multiplication, Division, Floor Division, Modulus | Addition, Subtraction |
Comparison (==, !=, >, >=, <=) | Identity (is, is not) | Membership (in, not in) |
Logical AND | Logical OR | Assignment Operators (=, +=, -=, *=, /=, //=, %=, &=, ^=, |=, <<=, >>=) |
Hopefully you understood most of the basic syntax of Python from the above example. Here is a quick summary of each point:
- Case Sensitivity: Var ≠ var, Python differs between upper and lower case.
- Statement Endings: The statement terminator is simply a new-line instead of a semi-colon.
- Comments: Uses # for single-line comment and a docstring ("""comment here""") for multi-line comments.
- Indentation: Scope of the blocks of codes is defined using indentation instead of braces.
- Dynamic Typing: Variable dtype doesn't need to be specified and it can be changed on run-time (without any type casting).
- Multiple Assignment Statement: You can initialize as well as swap multiple variables on a single line.
- Conditional Statements: Simple if cases and elif keyword is used for else if. Indentation is used here as well
- Loops: Both for and while loop syntaxes are given above in the example so just check from there.
- Functions: def keyword is used to define a function.
- Importing Libraries: import keyword is used to import in any libraries.
- String Manipulation: Strings can be concatenated with +, formatted with f"", or manipulated using built-in methods like .lower() or .split().
- Classes and Objects: Syntax for classes and objects is provided both in the above example and also in the OOP part of the Computational Models section.
- Operators: Python supports arithmetic (+, -), comparison (==, !=), logical (and, or), and bitwise (&, |) operators.
- Precedence: Operators follow a precedence order (e.g., * > +), as defined above in the documentation.
- Associativity: Operators are evaluated based on associativity; most arithmetic operators are left-associative.
- Implicit and Explicit Type Casting: Implicit casting occurs automatically (e.g., int to float during division). Explicit casting uses functions like int(), str(), or float().
Semantics of Python
Semantics define the meaning of the code just like syntax defines the structure and pragmatics defines the implementation of that code. Semantics ensures that programs behave as expected during execution. It is about understanding how the language interprets expressions, functions, and structures etc. We covered 4 types of semantics in the course so we will cover all 4 of them through a more Pythonic lens.
Algebraic Semantics¶
Algebraic semantics describes the meaning of code using mathematical structures like algebraic equations. Python’s semantics can often be explained through operations defined on data types.
x = 5
y = 3
z = x + y # Addition as defined mathematically
print(z) # Output: 8
Python adheres to well-defined rules for operations based on the type of data, ensuring that the output aligns with expected algebraic properties.
Axiomatic Semantics¶
Axiomatic semantics expresses program properties using mathematical logic assertions (preconditions and postconditions) to guarantee correctness. In Python, this concept can be integrated using commands like assert.
x = 10
y = 5
assert x > y, "x should be greater than y"
print("Assertion passed!")
Axiomatic semantics ensures logical soundness and is commonly used in Python for debugging and validating conditions.
Denotational Semantics¶
Denotational semantics translates Python code into mathematical functions or mappings that define program behavior. This is often used to describe functions and mappings between inputs and outputs.
def square(x):
return x ** 2
# Mapping: f(x) = x^2
result = square(4)
print(result) # Output: 16
Python functions and constructs often follow mathematical mappings, making it easier to reason about the behavior.
Operational Semantics¶
Operational semantics focuses on the step-by-step execution of Python code, emphasizing how instructions are carried out by the Python interpreter.
x = 0
while x < 5:
print(x)
x += 1 # Increment
# Output: 0, 1, 2, 3, 4
Python’s dynamic execution model interprets and executes instructions sequentially, making operational semantics essential for debugging and tracing.
Summary of Semantics¶
Just think that each type of semantics highlights a unique perspective:
- Algebraic: Mathematical rules for operations.
- Axiomatic: Logical correctness and preconditions.
- Denotational: Mapping inputs to outputs.
- Operational: Step-by-step execution.
The Compilation Process of a Python Script
One of the most common misconceptions about Python is that it is purely an interpreted language. While Python uses an interpreter, it also involves a compilation step. This is why the heading above mentions "Compilation" — Python scripts undergo a compilation phase before execution. Let’s explore the backend process of how Python scripts are compiled and executed.
Step-by-Step Compilation Process¶
Source Code (Written by Developer):
- Python programs start as source code written by developers.
- The source code consists of human-readable instructions written in
.py
files.
Lexical Analysis:
- The first step in the backend process is lexical analysis, where the source code is broken into smaller components called tokens by the scanner.
- Tokens include keywords, identifiers, operators, and symbols. This phase ensures that the structure of the code adheres to Python’s syntax rules.
Parsing (Abstract Syntax Tree - AST):
- The tokens are then passed to the parser, which creates an Abstract Syntax Tree (AST).
- The AST is a tree representation of the structure of the source code. It organizes the code into a hierarchical format for easier processing.
- This step ensures that the logical structure of the code is correct.
Bytecode Compilation:
- After parsing, the AST is converted into bytecode — a low-level representation of the code.
- Bytecode is platform-independent and consists of instructions that can be understood by the Python Virtual Machine (PVM).
- Python saves the compiled bytecode into
.pyc
files in the__pycache__
directory.
Python Virtual Machine (PVM) Execution:
- The PVM is the heart of Python’s interpreter.
- The bytecode is fed to the PVM, which executes it step-by-step (GeeksforGeeks, 2018). This involves translating the bytecode into machine instructions that the underlying hardware can execute.
Result:
- Finally, the PVM executes the code and produces the desired output or results.
Points of Confusion¶
Why Compilation in an "Interpreted" Language?
- The compilation process in Python (to bytecode) improves execution speed because the source code doesn’t need to be re-analyzed every time it is run.
- The presence of
.pyc
files in the__pycache__
directory is evidence of this compilation step.
Difference Between Compilation and Interpretation in Python specifically:
- Compilation: Converts high-level code into an intermediate format (bytecode).
- Interpretation: Executes the bytecode line-by-line, making Python a hybrid language.
Why Is Bytecode Important? Can't we just skip the conversion to bytecode step?
- Bytecode allows Python to remain platform-independent.
- By using bytecode, Python avoids repetitive parsing and reduces execution time for subsequent runs of the same script.
- There are ways to bypass the bytecode step, it would come with several tradeoffs but it is possible. Its a much bigger topic so I will simply provide a link to some articles if you wanna explore it further.
Are Scanner and Parser language specific?:
- Yes, every programming language typically implements its own scanner and parser because:
- Syntax: Each language has unique syntax and rules, so the scanner and parser are designed accordingly.
- Efficiency: They are optimized for that specific language's grammar.
- Yes, every programming language typically implements its own scanner and parser because:
Is the PVM related to the virtualization and virtual machines that are discussed in Operating Systems?
- Nope, the PVM is not related to VMware or WSL2. PVM is a language runtime environment that translates bytecode into machine instructions. It has nothing to do with creation of virtual instances of an entire OS.
If Python has its own VM, does that mean all other languages also have their own VMs?
- Well, yes and no. Some languages do have their own VMs while others don't. A VM here is like a helper that usually deals with the bytecode. Lets quickly go over a few languages
- Java: Yes, Java has the Java Virtual Machine (JVM), which executes Java bytecode.
- C#: C# uses the Common Language Runtime (CLR), part of .NET and is used for languages like C# and F#.
- C/C++: No, these two typically compile directly to machine code since they are already quite low-level and do not use a virtual machine and therefore have no intermediate bytecode conversion step (just one reason why C and C++ beat most other languages when it comes to performance.).
- Well, yes and no. Some languages do have their own VMs while others don't. A VM here is like a helper that usually deals with the bytecode. Lets quickly go over a few languages
Is bytecode the same as assembly language?
- Nope, bytecode is at a higher-level as compared to assembly language.
- Bytecode is specifically for the VMs to use.
- Bytecode is abstract and need to be translated into machine instructions through the VM.
- Think of it like this:
- Machine Code: The language your CPU speaks directly.
- Assembly Language: A human-readable form of machine code (e.g., MOV R1, 10).
- Bytecode: A universal language understood by the virtual machine (e.g., LOAD_CONST, BINARY_ADD).
Step-by-Step Example¶
- Source Code
x = 10
y = 20
z = x + y
print(z)
- Lexical Analysis (Scanner)
Token(Type: IDENTIFIER, Value: 'x')
Token(Type: ASSIGNMENT, Value: '=')
Token(Type: INTEGER, Value: '10') ... Token(Type: PRINT, Value: 'print')
Token(Type: LEFT_PAREN, Value: '(')
Token(Type: IDENTIFIER, Value: 'z')
Token(Type: RIGHT_PAREN, Value: ')')
# No need to try to understand the code, its GPT generated, just to show you an example of an AST, move to the output and the explanation
import ast
from anytree import Node, RenderTree
# Python source code
code = """
x = 10
y = 20
z = x + y
print(z)
"""
# Parse the code into an AST
parsed_ast = ast.parse(code)
# Function to recursively build the tree
def build_tree(node, parent=None):
node_name = type(node).__name__
tree_node = Node(node_name, parent=parent)
for child in ast.iter_child_nodes(node):
build_tree(child, tree_node)
return tree_node
# Build the AST tree
root = build_tree(parsed_ast)
# Print the tree
for pre, fill, node in RenderTree(root):
print(f"{pre}{node.name}")
Module ├── Assign │ ├── Name │ │ └── Store │ └── Constant ├── Assign │ ├── Name │ │ └── Store │ └── Constant ├── Assign │ ├── Name │ │ └── Store │ └── BinOp │ ├── Name │ │ └── Load │ ├── Add │ └── Name │ └── Load └── Expr └── Call ├── Name │ └── Load └── Name └── Load
This tree is ganna be really hard to read so I'll try to explain it level by level.
- level 1 (top):
- This is the root of the tree, representing the entire Python program.
- level 2:
- The root has 4 children, each representing one line of the code in this example (3 assignment statements and one expr node representing the line that has the print function)
- Explaining Assignment Nodes:
- These are the assignment operators. Each Assign node represents an assignment operation. We did 3 total assignments in the code so there should be 3 Assign children in the tree. Let's break them down:
- x = 10:
- Name: The variable x is being assigned a value.
- Store: Indicates that x is being stored (assigned a value).
- Constant: Represents the value being assigned (10).
- y = 20:
- Name: The variable y is being assigned a value.
- Store: Indicates that y is being stored (assigned a value).
- Constant: Represents the value being assigned 20
- z = x + y:
- Name: The variable z is being assigned a value.
- Store: Indicates z is being stored.
- BinOp: Represents the binary operation x + y.
- Name: Refers to x and y.
- Load: Indicates these variables are being loaded (used).
- Add: Represents the addition operation.
- x = 10:
- These are the assignment operators. Each Assign node represents an assignment operation. We did 3 total assignments in the code so there should be 3 Assign children in the tree. Let's break them down:
- Explaining Expr Node:
- These are the expressions, expressions as in any statement in Python that produces a value.
- Call: Represents a function call (in this case, print).
- Name: Refers to the print function.
- Load: Indicates print is being called/loaded.
- Name: Refers to the variable z.
- Load: Indicates z is being loaded for the function.
- These are the expressions, expressions as in any statement in Python that produces a value.
It might be that it is still kinda hard to visualize the tree in your head since that above output is not what we are used to. Here is me trying to draw the tree, maybe it will help you visualize it.
- AST to Bytecode
import dis
def example():
x = 10
y = 20
z = x + y
print(z)
dis.dis(example)
3 RESUME 0 4 LOAD_CONST 1 (10) STORE_FAST 0 (x) 5 LOAD_CONST 2 (20) STORE_FAST 1 (y) 6 LOAD_FAST_LOAD_FAST 1 (x, y) BINARY_OP 0 (+) STORE_FAST 2 (z) 7 LOAD_GLOBAL 1 (print + NULL) LOAD_FAST 2 (z) CALL 1 POP_TOP RETURN_CONST 0 (None)
The Python code is broken down into bytecode (which is platform independent). The code you see above like RESUME or LOAD_FAST or STORE_FAST are called opcode, as in operation code. Think of opcodes as "instructions" in a recipe. Each opcode tells the PVM what step to perform next, like "add two numbers" or "store this value in memory."
- From here the PVM will take over and execute the bytecode
- LOAD_CONST 0 (10): Load the constant 10 onto the stack.
- STORE_NAME 0 (x): Store the value 10 in the variable x.
- The process continues for all instructions until the final result is produced.
The Pragmatics of Python
The Zen of Python¶
Pragmatics focuses on the real-world usage of the language, the design trade-offs, and how the language is used in practice. Its about the implementation of the language whereas syntax was about the structure, and semantics was about the meaning of the language.
Python's guiding philosophy, "The Zen of Python," is a collection of principles that aim to make the language pragmatic, clean, and intuitive. It was written by Tim Peters who was one of the major contributors to the original implementation of Python, as in CPython. Access the "Zen of Python" by importing this
in Python.
# Importing The Zen of Python
import this
The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!
Key principles include:
- Explicit is better than implicit: Python aims for clarity.
- Readability counts: Python’s syntax is intentionally designed to be readable.
- There should be one-- and preferably only one --obvious way to do it: This minimizes confusion for developers.
Although, Python sometimes violates its own principles. For example, there are multiple ways to achieve the same result (e.g., map()
vs. list comprehensions).
Readability¶
Python is known for its clear and concise syntax, prioritizing readability. You have seen plenty of Python code above but here is a tiny example, and we will cover this further in the Comparison section:
def add(a, b):
return a + b
print(add(5, 3))
Dynamic Typing¶
Python’s dynamic typing allows variables to change types on the fly. The type of the variable is assigned on runtime:
# Dynamic Typing Example
x = 42 # x is an integer
print(type(x)) # Output: <class 'int'>
x = "hello" # x is now a string
print(type(x)) # Output: <class 'str'>
- Strengths: Speeds up development and allows flexibility.
- Challenges:
- Errors are caught only at runtime.
- Ambiguity in large codebases.
- We will discuss potential counters to this challenge in the design issues section.
Interoperability¶
Interoperability in programming languages is the ability of multiple programming languages to work together in the same system and exchange data and objects. It's an important skill for developers who work with multiple languages. For interoperability, we need a glue languages that enable seamless integration of multiple languages. Python serves as a great glue language because of its easy-to-use syntax, extensive libraries, and ability to seamlessly integrate with code written in other languages, making it ideal for connecting different components of a system together
Here are some examples:
- Calling C Libraries:
import ctypes
# Load a C library (e.g., libc on Unix-based systems)
libc = ctypes.CDLL("libc.so.6")
# Call the printf function
libc.printf(b"Hello from C!\n")
- Using Java Libraries via Jython (example):
# Sample (for Jython)
from java.util import Date
date = Date()
print(date)
Modularity¶
Python’s modular design allows developers to build reusable and maintainable code. Python offers more then 200 modules as well as the functionality to build user-defined modules. Keep in mind that the word modularity doesn't just refer to modules/libraries only, it also refers to constructs like functions etc. Although, our focus here is on the modules/libraries that Python offers:
# Importing Modules
import math
print(math.sqrt(16)) # Output: 4.0
# Custom Module Example
# File: mymodule.py
def greet(name):
return f"Oyi, its, {name}!"
# Main File
from mymodule import greet
print(greet("Agent 47, RUN!!")) # Output: Oyi, its, Agent 47, RUN!!
Lets quickly cover a few of Python's popular libraries:
- NumPy: Provides support for large, multi-dimensional arrays and matrices, along with a collection of mathematical functions.
- Pandas: Offers powerful data structures (pandas dataframe and pandas series) for data analysis and manipulation.
- Matplotlib: A 2D plotting library for creating static, interactive, and animated visualizations.
- Scikit-learn: A machine learning library offering tools for classification, regression, and clustering.
- TensorFlow: An end-to-end open-source platform for machine learning.
- Flask: A lightweight web application framework.
- Django: A high-level framework for building robust web applications quickly.
- Beautiful Soup: A library for scraping data from HTML and XML files.
- Requests: A simple HTTP library for making web requests.
- PyTorch: A machine learning library focused on deep learning.
Another thing to note is how Python manages all these modules through pip
; Python's default package manager. Pip
is extremely easy to use and install/uninstall packages with and it is downloaded with Python so no need to smash your head around trying to download Mingw separately for the millionth time. We can simply hit commands like 'pip install pandas' to install a new library.
Dedicated Interactive Environment¶
Python’s Jupyter Notebook is one of the most impactful tools in modern programming, particularly in the fields of data science, machine learning, education, and research. Jupyter has become a standard for interactive development.
- Jupyter allows code to be written, executed, and debugged in blocks called cells.
- You can run individual cells instead of executing the entire codebase, making it easier to test ideas.
- You can combine executable code with formatted text, visuals, and markdown. This enables the creation of detailed, interactive documents that include:
- Code
- Explanations
- Visualizations
- Formulas
- Jupyter supports over 40 programming languages, not just Python, making it versatile for multi-language projects (making Python more interoperable I suppose).
This entire documentation includes everything from images to code blocks and their outputs to formatted text and hyperlinks, all of it is done in jupyter notebooks.
Applications of Python
Python has established itself as one of the most versatile programming languages, being used across a wide range of industries. This section will highlight some of the major fields where Python is used extensively, along with real-world examples of its applications.
Major Fields Where Python Excels¶
1. Data Science and Artificial Intelligence (AI)¶
Python has become the de facto language for data analysis, machine learning, and AI due to its simplicity and the availability of powerful libraries.
- Libraries Used: Pandas, NumPy, Scikit-learn, TensorFlow, PyTorch, Matplotlib, Seaborn.
- Examples:
- Spotify: Spotify uses Python for its recommendation system, including the famous Wrapped feature that analyzes user data.
- Netflix: Python is a key component of Netflix’s machine learning pipeline, helping with content recommendations and predictive analytics.
- Uber: Uber leverages Python for data visualization and geospatial analysis, enabling optimized routing and ride pricing.
2. Web Development¶
Python’s simplicity, along with frameworks like Django and Flask, makes it an excellent choice for developing robust web applications.
- Frameworks Used: Django, Flask, FastAPI, Pyramid.
- Examples:
- Instagram: Instagram’s backend is powered by Python and Django, ensuring scalability and fast feature development.
- Reddit: Reddit is built on Python, allowing the platform to handle massive traffic loads while being easily extensible.
- Dropbox: Dropbox uses Python to manage its server-side functionality and client applications.
3. Automation and Scripting¶
Python is often the go-to choice for automating repetitive tasks and writing scripts due to its straightforward syntax.- Libraries Used: Selenium, PyAutoGUI, Paramiko, Beautiful Soup.
- Examples:
- Google: Python is used at Google extensively. Python scripts automate repetitive tasks, such as data scraping and report generation.
- NASA: NASA employs Python scripts to automate various engineering workflows and simulations.
- Excel Automation: Businesses use Python (via libraries like OpenPyXL) to process and analyze massive Excel files efficiently.
4. Cybersecurity and Ethical Hacking¶
Python’s flexibility and extensive libraries make it ideal for developing penetration testing tools and cybersecurity solutions.
- Libraries Used: Scapy, Nmap, Impacket, Requests.
- Examples:
- Wireshark Extensions: Wireshark is a free, open-source network analyzer that allows users to capture and analyze network packets in real time. Python is used to write scripts for analyzing packet data at Wireshack.
- Automated Vulnerability Scanning: Security professionals use Python for developing custom scripts for vulnerability testing.
5. Scientific Computing and Research¶
Researchers widely use Python for simulations, modeling, and analyzing data due to its robust mathematical and statistical libraries.
- Libraries Used: SciPy, SymPy, Biopython.
- Examples:
- Large Hadron Collider (CERN): Python is used to analyze particle physics experiments.
- SpaceX: Python supports various simulation tools and real-time decision-making during launches.
Python's Impact in the Real-World¶
The examples mentioned in the above 4 sections are more then enough to solidify Python as one of the main programming languages shaping the world's technological ecosystem. But just for the sake of completion, lets cover a few more general examples:
Google: Python is a core language at Google, used in applications like YouTube and Google App Engine.
Facebook: Python is utilized for infrastructure management and machine learning pipelines.
Instagram: Leveraging Python’s Django framework to maintain high scalability and performance.
Amazon: Python supports Amazon’s fulfillment center automation and Alexa’s AI engine.
Quora: The platform is built on Python and uses it to scale its user experience.
Pinterest: Pinterest’s backend is powered by Python for handling billions of pins.
Robinhood: The financial trading app’s backend is built using Python for secure transactions.
Quantum Computing: Libraries like Cirq and Qiskit are enabling Python’s entry into quantum programming.
Blockchain Development: Python is used in developing blockchain solutions like smart contracts and cryptocurrency tools.
Python’s simplicity, readability, and vast ecosystem of libraries have made it indispensable across numerous domains. Its use by industry leaders like Google, Netflix, and NASA only reinforces its dominance and applicability in solving modern technological challenges.
Comparison (Python VS C++ VS Java) and Potential Improvements
Syntax¶
Syntax: Simple and readable. Uses indentation for block structures.
def greet(): print("Hello, World!")greet()
Syntax: Verbose and complex. Uses braces {} for block structures.
#includeusing namespace std; void greet() { cout << "Hello, World!" << endl; }
int main() { greet(); return 0; }
Syntax: Verbose but structured. Also uses braces {} for blocks.
public class Main { public static void main(String[] args) { greet(); }static void greet() { System.out.println("Hello, World!"); }
}
The criteria commonly used while talking about syntax is readability and writeability. It doesn't take much to see who wins here, Python has the cleanest, easiest and shortest lines of code out the 3 languages. No improvements are needed here more or less, the syntax is extremely easy already.
Performance¶
Performance: Slower execution. Great for prototyping and non-critical tasks.
import time start = time.time() total = sum(range(1, 10000001)) end = time.time()print(f"Sum: {total}, Time: {end - start}")
Performance: Compiled directly to machine code, offering high execution speed.
#include#include #include int main() { const int size = 1000; std::vector<std::vector
> a(size, std::vector (size)); std::vector<std::vector > b(size, std::vector (size)); std::vector<std::vector > result(size, std::vector (size, 0)); // Initialize matrices a and b for (int i = 0; i < size; ++i) { for (int j = 0; j < size; ++j) { a[i][j] = i * j; b[i][j] = j * i; } } auto start = std::chrono::high_resolution_clock::now(); // Perform matrix multiplication for (int i = 0; i < size; ++i) { for (int j = 0; j < size; ++j) { for (int k = 0; k < size; ++k) { result[i][j] += a[i][k] * b[k][j]; } } } auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration<double> elapsed = end - start; std::cout << "C++ Execution Time: " << elapsed.count() << " seconds\n"; return 0;
}
Performance: JIT compilation improves speed, but slower than C++.
import java.time.Duration; import java.time.Instant;public class MatrixMultiplication { public static void main(String[] args) { int size = 1000; int[][] a = new int[size][size]; int[][] b = new int[size][size]; int[][] result = new int[size][size];
// Initialize matrices a and b for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { a[i][j] = i * j; b[i][j] = j * i; } } Instant start = Instant.now(); // Perform matrix multiplication for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { for (int k = 0; k < size; k++) { result[i][j] += a[i][k] * b[k][j]; } } } Instant end = Instant.now(); Duration timeElapsed = Duration.between(start, end); System.out.println("Java Execution Time: " + timeElapsed.toMillis() / 1000.0 + " seconds"); }
}
</pre> </div>
- Python took about 179.2 seconds to execute that code (on my laptop specifically)
- C++ took about 2.4 seconds.
- Java took only 3.36 seconds.
Although it is worth noting that this isn't a concrete performance benchmark, it is still very obvious that Python's execution time is really terrible compared to C++ and Java. Java surpassed both Python and C++ by a considerable margin. The main point to highlight here is that Python is extremely slow because of the high level of abstraction.
Potential Performance Improvements¶
The solution here is quite simple, and it is going to be the big theme here. The use of modules is what makes Python unique. For instance, the numpy
library in Python is written in C, so it would be reasonable to assume that numpy
would execute at a speed close to if not faster then C++. Lets put that claim to the test.
import numpy as np
import time
# Using NumPy
start = time.time()
# Create matrices a and b
a = np.arange(1000)[:, None] * np.arange(1000)
b = np.arange(1000)[:, None] * np.arange(1000).T
# Perform matrix multiplication
result = np.dot(a, b)
end = time.time()
print(f"NumPy Execution Time: {end - start:.4f} seconds")
numpy_time = end - start
print(f"NumPy took {numpy_time} seconds to run this code")
NumPy Execution Time: 2.1261 seconds NumPy took 2.126063585281372 seconds to run this code
As you can see, using the numpy
library for this task gave Python such a significant boost that it even surpassed Java. So, in conclusion, YES, we can improve the performance of Python quite significantly if we learn to use the right libraries for the right tasks. But it is still worth noting that as far as pure Python is concerned, it is much MUCH slower relative to other languages like C++ or Java.
Memory Consumption¶
Memory Consumption: Higher due to dynamic typing and abstraction layers.
import sysx = 1000 print(f"Memory usage of x: {sys.getsizeof(x)} bytes")
Memory Consumption: Optimized for low-level memory management.
#includeint main() { int x = 1000; std::cout << "Size of x: " << sizeof(x) << " bytes\n"; return 0; }
Memory Consumption: Garbage collection adds overhead.
public class Main { public static void main(String[] args) { int x = 1000; System.out.println("Memory usage of x: " + Integer.BYTES + " bytes"); } }
Python deals with all data types as objects and has a lot more built-in features with its data types which results in Python's data types being much larger as compared to C++ and Java data types. Just to highlight how much more space Python takes as compared to other languages, here is a tiny table comparing common dtypes.
Data Type | Python (Bytes) | Java (Bytes) | C++ (Bytes) |
---|---|---|---|
Integer | ~28 (varies by size) | 4 (primitive int ) |
4 (primitive int ) |
Float | ~24 | 4 (primitive float ) |
4 (primitive float ) |
String (1 char) | ~50 | ~40 | 1 (character literal) |
List (Empty) | ~64 | N/A (ArrayList is dynamic) | Varies by implementation |
Dictionary | ~240 (Empty) | N/A | N/A |
So, this is a clear area where Python lacks quite severely once again. But again, we have ways to mitigate this.
Potential Ways to Reduce Memory Consumption¶
Once again, Python's modularity comes in for the save. If we use the dtypes in Python libraries instead of the dtypes in native Python, then the size can be considerably reduce. Lets see this through an example:
import sys
import numpy as np
# Python list
python_list = [i for i in range(10000)]
print(f"Memory used by Python list: {sys.getsizeof(python_list)} bytes")
# NumPy array
numpy_array = np.arange(10000)
print(f"Memory used by NumPy array: {numpy_array.nbytes} bytes")
Memory used by Python list: 85176 bytes Memory used by NumPy array: 40000 bytes
So, we were able to halve the size of a list by simply using the Numpy Array data structure from the numpy
library instead of using the List data structure that we have in native Python. Although, it is worth noting that languages like C++ and Java would still surpass Python in-terms of practical usage of memory in real-world projects. But at the same time, due to modern technological advances, increased capacity in computers has become the norm so memory is not as big of a factor as it once was.
Type System¶
Type System: Dynamic typing allows flexibility but increases runtime errors.
x = "Hello" x = 10 # Allowed, dynamic typing print(x)
Type System: Static typing provides stricter error checking.
#includeint main() { int x = 10; // x = "Hello"; // Compile-time error std::cout << x; return 0; }
Type System: Static typing with robust error checking.
public class Main { public static void main(String[] args) { int x = 10; // x = "Hello"; // Compile-time error System.out.println(x); } }
Python's dynamic typing serves as a double-edged sword here. Dynamic Typing makes life a lot easier for developers as keeping track of dtypes is not as important and they don't need to explicitly give dtypes to variables and declare them. But it also leads to situations where dtpes may not be compatible with an operator and that would lead to an errors. What's of note here is that when that scenario does occur (eg: adding integers to strings), languages like C++ and Java have very strict error checking and they provide hints in the terminal about what exactly the error was and what dtype was expected. Python has none of that because of the dynamic typing feature.
Ways to Potentially mitigate runtime errors due to Dynamic Typing¶
One way to improve this is by using an extension from VS Code named MyPy, and integrating that extension with Type Hints. Type Hints are just tiny syntactical elements that tell the Python Interpreter that a certain dtype was expected at a certain place. Here is an example of myPy and Type Hints in action.
The colons (:) after the parameters and the -> before the "int:" are both Type Hints. MyPy uses the Type Hints and provide a nice errors message with a proper hint of which dtype was expected. If used properly, this would help mitigate or at least, easily debug the bugs caused by dynamic typing.
Compilation/Execution¶
Compilation/Execution: Compiled to bytecode and then interpreted by the PVM.
Compilation/Execution: Compiled directly to machine code.
Compilation/Execution: Compiled to bytecode and executed on the JVM.
Java and C++ using a compiler does, undoubtedly makes them faster in many scenarios. Python id predominantly interpreted and an interpreter goes through the code line-by-line, which does make it relatively slower. But the use of ann interpreter in Python is not necessarily a design flaws. Several features in Python are enabled specifically because it uses an interpreter. For instance, the checking and assigning of data types to variables would not be possible using a compiler, so the interpreter is basically enabling the dynamic typing feature of Python.
Concurrency¶
One thing that needs to be clarified off the bat is the difference between concurrency and parallelism. Concurrency does not necessarily mean true simultaneous execution of tasks. It only refers to the ability to manage several tasks at a time, a single-core processor is capable of concurrent operations while it it not capable of parallelism. Parallelism is true simultaneous computing where we have several cores and each core is working on a different tasks (like how we used pthread library in OS). Think of it like this: Concurrency is like a single chef working on multiple dishes but switching between them quickly while parallelism is like multiple chefs working on different dishes simultaneously.
Concurrency: Python supports concurrency through threading. The Global Interpreter lock only lets one thread run at one time but as explained above, that still meets the conditions for concurrency.
import threadingdef print_numbers(): for i in range(5): print(i)
thread = threading.Thread(target=print_numbers) thread.start() thread.join()
Concurrency: C++ offers robust support for concurrency via its standard library, including #include <thread>
and other synchronization primitives. Developers have fine-grained control over thread behavior and synchronization.
#include#include void print_numbers() { for (int i = 0; i < 5; ++i) std::cout << i << std::endl; }
int main() { std::thread thread(print_numbers); thread.join(); return 0; }
Concurrency: Java has extensive concurrency support with its java.util.concurrent
package, which includes classes like Thread
, ExecutorService
, and Future
. Java's thread model is powerful and designed for building concurrent applications.
public class Main { public static void main(String[] args) { Thread thread = new Thread(() -> { for (int i = 0; i < 5; i++) { System.out.println(i); } }); thread.start(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }
- It is clear tha C++ and Java are much more efficient and robust in-terms of multi-threading. This is because the default Python Interpreter (CPython) has a Global Interpreter Lock which is basically a mutex lock that prevents multiple threads from running simultaneously (Дейбук & Gil, n.d.). This still does not mean that multiple threads cannot progress concurrently and Python is a completely sequential language. Threads do achieve concurrently even with the Global Interpreter Lock. The area where Python really suffers is Parallelism which we will explore next.
Parallelism¶
Parallelism: Python doesn't support true parallelism when it comes to threads, multiple threads cannot run simultaneously due to the GIL. Although we could uses processes for parallelism which bypass the GIL and do achieve true parallelism.
from multiprocessing import Processdef worker(): print("Worker process")
if name == "main": processes = [] for _ in range(4): p = Process(target=worker) processes.append(p) p.start()
for p in processes: p.join()
Parallelism: C++ provides robust parallelism support through the Standard Library, including parallel algorithms (#include <execution>
) and threads. It allows fine-grained control for high-performance applications.
#include#include #include void worker(int id) { std::cout << "Worker " << id << "\n"; }
int main() { std::vectorstd::thread threads; for (int i = 0; i < 4; ++i) { threads.emplace_back(worker, i); }
for (auto& t : threads) { t.join(); } return 0;
}
Parallelism: Java supports parallelism via the Fork/Join Framework and the parallelStream
API, designed for parallel execution of tasks on multi-core processors.
import java.util.stream.IntStream;public class Main { public static void main(String[] args) { IntStream.range(0, 4).parallel().forEach(id -> { System.out.println("Worker " + id); }); } }
- Java and C++ again have a clear edge here, they both almost specialize in parallelism whereas Python seems to completely lack it. This is a major issue in Python as multiple threads running simultaneously would massively boost the performance of a language. But it is worth pondering that if Python does parallelism so poorly then how is it that some of the tasks that demand the most parallel processing (tasks like image processing using OpenCV) are done using Python. Why is that?
Ways to bypass the GIL and enable Parallelism in Python¶
- Firstly, as of December 3, 2024, Python Version 3.13.1 was released where the GIL is being disabled (still in experimental stages) and multithreading (parallelism) is being enabled in Python.
- Secondly, processes can be used instead of threads to bypass the GIL and enable parallelism in Python. But it is to be noted that processes have more overhead as compared to threads so it would still not be as perfect as the parallelism in Java or C. But it would be a massive improvement nonetheless.
- Thirdly, libraries that are implemented using C, C++, and CUDA like
numpy
,openCV
,tensorflow
can be used which work completely outside of the GIL, enabling true parallelism. Python is not achieve parallelism on its own, so it leverages other languages to achieve what it cannot.
Platform Independence¶
Platform Independence: Python is platform-independent due to its interpreter, which is available for multiple platforms like Windows, macOS, and Linux. Code written in Python can run seamlessly across these platforms without modification.
Platform Independence: C++ is not inherently platform-independent. While the language itself is portable, developers need to recompile their code for each platform, and system-specific libraries may introduce compatibility issues.
Platform Independence: Java is platform-independent due to the JVM (Java Virtual Machine). Java programs are compiled into bytecode, which can run on any system with a JVM, making it "write once, run anywhere."
Python and Java have the clear edge here as their respective Virtual Machines makes them platform independent while C++ might not be considered platform independent. Although, it is of note that this isn't a major negative for C++ since the execution time is so fast that even if you do have to execute the code again on another device, it is not that big of a hit to the overall performance.
Error Handling¶
Error Handling: Python uses exceptions for error handling. Developers use try
, except
, and finally
blocks to catch and manage exceptions gracefully.
try: num = int(input("Enter a number: ")) print(f"You entered: {num}") except ValueError: print("That's not a valid number!") finally: print("Execution complete.")
Error Handling: C++ uses exceptions for error handling. The try
, catch
, and throw
keywords are used to manage errors and ensure program stability.
#include#include int main() { try { int num; std::cout << "Enter a number: "; std::cin >> num; if (std::cin.fail()) { throw std::invalid_argument("Invalid input"); } std::cout << "You entered: " << num << std::endl; } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; } return 0; }
Error Handling: Java uses exceptions for error handling with try
, catch
, and finally
blocks. It has checked and unchecked exceptions for better error categorization.
import java.util.Scanner;public class Main { public static void main(String[] args) { try (Scanner scanner = new Scanner(System.in)) { System.out.print("Enter a number: "); int num = scanner.nextInt(); System.out.println("You entered: " + num); } catch (Exception e) { System.out.println("Error: " + e.getMessage()); } finally { System.out.println("Execution complete."); } } }
Not much to say here, all three languages have this basic capability, they might use different keywords but the job is the same for the most part.
Use Cases¶
Use Cases: Python is widely used for web development, data science, artificial intelligence, scripting, and automation. Its simplicity and vast library ecosystem make it a versatile choice for rapid development.
Use Cases: C++ is heavily used in system-level programming, game development, embedded systems, and performance-critical applications. It provides fine-grained control over hardware and memory management.
Use Cases: Java is widely used for enterprise applications, Android development, and large-scale systems. Its robustness and scalability make it ideal for handling complex backend systems.
Python is used in almost every field you can think of, and what's even more surprising is that its used at the highest level in many of those fields which is unheard of usually. C++ is a much more low-level language and therefore, its applications are mainly in system-level programming instead of things like app or web development. Java is much more 'commercial' then Python and C++ as its focus is specifically on the bigger organizations and their needs.
Python VS R¶
One other comparison that is often made is how Python ranks against R (R is another programming language btw) in the fields of Data Analytics, Data Science, and Mathematical Computation in general (Hill, Du, Johnson, & McCullough, 2024). Python is much higher in-demand in the market and has way more use cases but R does have certain advantages too as it is uniquely made for statistical computing. Here is a simple comparison: Data Analysis:
- Python offers excellent libraries like pandas and NumPy for efficient data manipulation, cleaning, and computation. Python also integrates seamlessly with tools like Dask for parallel processing and handling large datasets.
- R, however, shines when it comes to statistical modeling and has built-in methods for hypothesis testing, regression, and time series analysis. Libraries like dplyr and tidyr provide a straightforward way to manipulate data, often with simpler syntax than Python. Visualizations:
- Python has powerful libraries like Matplotlib, Seaborn, and interactive tools like Plotly and Bokeh for creating visually stunning and dynamic plots. It is ideal for creating production-level dashboards and web-app visualizations.
- R’s ggplot2 remains the gold standard for static, publication-quality graphics. With less code, you can create layered, complex, and professional plots. Additionally, Shiny allows R users to build interactive web applications easily. Niche functions and capabilities:
- Python is a general-purpose language that extends beyond analytics and statistics, excelling in areas like machine learning, automation, and web development. Libraries like scikit-learn, TensorFlow, and PyTorch dominate the AI/ML space, while tools like Beautiful Soup and Selenium enable web scraping and automation.
- R is specifically tailored for statistical computing, making it a preferred choice for academic research and domains like biostatistics, ecology, and econometrics. Packages like Bioconductor and vegan provide specialized functions that Python does not easily replicate.
References
Contents Articles. (n.d.). Retrieved from https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=81f30a9a5aacc84a1b92f3333c6d3cd45bb5cb3a#page=17
Tulchak, L., & Маrchuk, А. (n.d.). History of Python Ключові слова. Retrieved from https://ir.lib.vntu.edu.ua/bitstream/handle/123456789/10471/461.pdf
alalqab.com. (2024). Python (programming language). Retrieved December 29, 2024, from alalqab.com website: https://alalqab.com/en/Python_(programming_language)
Srinath, K. (n.d.). Python -The Fastest Growing Programming Language. In International Research Journal of Engineering and Technology. Retrieved from https://d1wqtxts1xzle7.cloudfront.net/61651202/IRJET-V4I126620200101-109100-1nbuifw-libre.pdf?1577884565=&response-content-disposition=inline%3B+filename%3DPython_The_Fastest_Growing_Programming_L.pdf&Expires=1735474327&Signature=GZND1DbHyJDhBjKdqg-4wJRAyOzaDhTgBBhq4McDkB5Ptvhp~4WA4-43jlVzA~OKneInCcaVWWloILi2VwMeWUQJrcxwTuiZrNJ2wXIjJMNZ-pb4o7dt9F~wG-rRRXUnDtfxvxXFUibcllUL35eVm~FhphaX5NWjKG2dSz0nOfFCNxDsK3V9ELHkjz4pjH-LPlqGBczbXFUxpREqNWz29KVUtdOO99Mn4ZplRM3n~sveCRvkvIIqxw1kua1B18y8kLax8WuIThiduS7-Zkr2lxsbIfrDHQSLCYay5aR1uL7xh7HrjpBjwdWr68N7KLpMFhL9BmEDmXfNBDFKmTfWPg__&Key-Pair-Id=APKAJLOHF5GGSLRBV4ZA
GeeksforGeeks. (2020, March 11). Programming Paradigms in Python. Retrieved December 29, 2024, from GeeksforGeeks website: https://www.geeksforgeeks.org/programming-paradigms-in-python/
Denys Volokh. (2024, July 9). Functional vs Imperative Programming in Python: A Practical Guide. Retrieved December 29, 2024, from Medium website: https://medium.com/@denis.volokh/functional-vs-imperative-programming-in-python-a-practical-guide-aba1eb40652d
GeeksforGeeks. (2020, January 20). Functional Programming in Python. Retrieved December 29, 2024, from GeeksforGeeks website: https://www.geeksforgeeks.org/functional-programming-in-python/
Yigitaliev Ruzimatjon. (2024). PYTHON AND C++ ARE PROGRAMMING LANGUAGES DESIGNED FOR OOP. University Research Base, 578–583. Retrieved from https://scholar.kokanduni.uz/index.php/rb/article/view/397
W3Schools.com. (2024). Retrieved December 29, 2024, from W3schools.com website: https://www.w3schools.com/python/python_syntax.asp
GeeksforGeeks. (2018, September 11). Internal working of Python. Retrieved December 29, 2024, from GeeksforGeeks website: https://www.geeksforgeeks.org/internal-working-of-python/
Дейбук, Д., & Gil, W. (n.d.). THE FUTURE OF THE PYTHON PROGRAMMING LANGUAGE. Retrieved from https://ir.lib.vntu.edu.ua/bitstream/handle/123456789/42329/20032.pdf?sequence=3&isAllowed=y
Hill, C., Du, L., Johnson, M., & McCullough, B. D. (2024). Comparing programming languages for data analytics: Accuracy of estimation in Python and R. Wiley Interdisciplinary Reviews Data Mining and Knowledge Discovery, 14(3). https://doi.org/10.1002/widm.1531