Python for Programmers: Fast Track to Productivity

📅 02/10/2025

Open In Colab

If you’re an experienced programmer looking to add Python to your toolkit, this guide is for you(writen for my wife who is a JS dev🤫😅). We’re skipping the “what is a variable” explanations and focusing on what makes Python different and powerful.

This comprehensive guide covers the essentials you need to be productive in Python: from basic data types and control flow, through lists and dictionaries, to functions, classes, and exception handling. We’ll also touch on Python-specific features like comprehensions and special methods that make the language elegant and expressive.

Let’s get started. To start execute in collab execeute a cell by using key (shift and enter or return).

Primitive Data Types

  • int: Whole numbers (e.g., 10, -5, 0)
  • float: Decimal numbers (e.g., 10.0, 3.14, -2.5)
  • str: Text/strings (e.g., “hello”, ‘world’)
  • bool: True/False values
  • NoneType: Python’s null value (None)

Using function type, we can fetch the type of a variable.

noi = 10
nof = 10.1
s = "i am string"
bool_d = True
none_d = None


type(noi), type(nof), type(s), type(bool_d), type(none_d)
(int, float, str, bool, NoneType)

Control flows

  • Indentation is mandatory - Python uses whitespace to define code blocks, not just for readability
  • Consistent indentation - All lines at the same block level must have the same indentation
  • Standard is 4 spaces (though tabs work, mixing them causes errors)
  • Colon usage - Control structures end with a colon : before the indented block

Conditional statements

age = 18
if age >= 18:
    print("Adult")
elif age >= 13:
    print("Teenager")
else:
    print("Child")
Adult

Loops

# For loop
for i in range(3):
    print(f"For: {i=}")

# While loop
count = 0
while count < 3:
    print(f"While: {count=}")
    count += 1
For: i=0
For: i=1
For: i=2
While: count=0
While: count=1
While: count=2

Basic Data Struct

List: Mutable, ordered collection - Can contain different data types - Supports indexing and slicing - Dynamic sizing (can grow/shrink) - use list keyword for init

Dictionary: Mutable, unordered key-value mapping - Keys must be immutable and unique - Fast key-based lookup - Dynamic sizing - use dict keyword for init

List

lis = list([1, 2, 3, 4])
print(f"{lis=}")
print(f"lenght of the lis : {len(lis)}")
# indexing starting for 0
print(f"{lis[0]=}")
lis=[1, 2, 3, 4]
lenght of the lis : 4
lis[0]=1
# adding new element to the list
lis.append(5)
print(f"{lis=}")
lis=[1, 2, 3, 4, 5]
lis2 = [6, 7]
lis1 = lis + lis2
print(f"{lis1=}")
lis1=[1, 2, 3, 4, 5, 6, 7]
# Get a portion of the list
print(f"{lis1[1:4]=}")  # elements from index 1 to 3
lis1[1:4]=[2, 3, 4]
print(f"{lis=}")
lis.insert(0, 99) # inserting value at the perticular index
print(f"{lis=}")
lis=[1, 2, 3, 4, 5]
lis=[99, 1, 2, 3, 4, 5]
print(f"{lis=}")
print(lis.pop()) # remove last element
print(f"{lis=}")
lis=[99, 1, 2, 3, 4, 5]
5
lis=[99, 1, 2, 3, 4]
print(f"{lis=}")
print(lis.remove(2)) # remove the 2nd element
print(f"{lis=}")
lis=[99, 1, 2, 3, 4]
None
lis=[99, 1, 3, 4]

Dictionary

dic = dict({'a':1, 'b':2})
dic
{'a': 1, 'b': 2}
dic = dict({'a':1, 'b':2}) # can be written as like {'a':1, 'b':2} without dict
print(f"{dic=}")
print(f"{dic['a']=}")     # indexing the dictionary with a key value
dic={'a': 1, 'b': 2}
dic['a']=1
# adding new entry to the dictionay
dic['c'] = 3
print(f"{dic=}")
dic={'a': 1, 'b': 2, 'c': 3}
print(f"{dic=}")
print(f"{dic.pop('c')=}")
print(f"{dic=}")
dic={'a': 1, 'b': 2, 'c': 3}
dic.pop('c')=3
dic={'a': 1, 'b': 2}

Iterating wrt the Dictionary keys

for k in dic.keys():
    print(f"{k=} -> {dic[k]=}")
k='a' -> dic[k]=1
k='b' -> dic[k]=2

Just fetching the values in the Dictionary

for v in dic.values():
    print(f"{v=}")
v=1
v=2

iterating wrt both key and values without explicitly indexing

for k, v in dic.items():
    print(f"{k=} -> {v=}")
k='a' -> v=1
k='b' -> v=2
#Safe key access uncommnet the below line and run
#print(dic['c'])
dic.get('item', "does not exists")
'does not exists'
# update a perticular value of a given key
dic['a'] = 1000
print(f"{dic=}")
dic={'a': 1000, 'b': 2}

There are couple of other Data struct below are those: - Tuple: immutable list,ref - Set: As name suggest it will store object, ref

List and Dictionary comprehension

It is more consise way to build list and dict with explicitly using those key words. First I will create a list from 0 to 10, then filter out only the positive number.

lis = list(range(10))
lis
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
odd = [i for i in lis if i % 2]
even = [i for i in lis if not i % 2 ]
even, odd
([0, 2, 4, 6, 8], [1, 3, 5, 7, 9])
dic = {chr(65 + i): i for i in range(5)}
dic
{'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4}

Function

  • starts with key word def
  • it should have a have and a list of args
  • we can add in types in the function definations, it will be used for type hinting. The compile don’t enforce types at runtime. The function accepts any types that support the + operator, even though we hinted int
def func():
    print("Hello world")
func()
Hello world
def add(a:int, b:int=0):
    return a+b
print(add(10, 1))
print(add(10))          # here b is defults to 0
print(add(10.5, 0))
print(add('10.5', '0'))
11
10
10.5
10.50

A gotcha By defult the function returns None. One should be keep those in mind.

def show(x):
    print(f"{x=}")

result = show(10)
print(f"{result=}")
x=10
result=None

Class

  • Used for bundling objects and functions

  • Creating new objects creates a new instance of the class

  • Each function have should have a reference to itself usally repesented by self

  • Helper functions wrapped in __ that need to be followed for special functionality below are few and respective usages

    name functionality
    __init__ Constructor called when creating a new instance, initializes attributes
    __repr__ and __str__ String representation of a class object (repr for developers, str for end users)
    __iter__ Returns an iterator object, makes the class iterable
    __next__ Fetches the next item from the iterator, raises StopIteration when done
    __getattr__ Called when accessing an attribute that doesn’t exist
    __getitem__ Enables indexing and slicing (e.g., obj[key])
    __setattr__ Called when setting an attribute (e.g., obj.attr = value)
    __setitem__ Enables item assignment (e.g., obj[key] = value)
    __del__ Destructor called when object is about to be destroyed
    __new__ Creates and returns a new instance before __init__ is called
    __enter__ Called when entering a context manager (with statement)
    __exit__ Called when exiting a context manager, handles cleanup

    Complete docs are present in docs

  • Classes also suppourt inheritace, a base example can be found here

class L:
    def __init__(self, lis):
        print(f"init is called")
        self.lis = lis
    
    def __str__(self):
        # it return length of the lis along with first 5 element
        return f"{len(self.lis)} {self.lis[:5]}"
    
    def __len__(self):
        return len(self.lis)

li = L(list(range(10)))
print(li)
print(f"{len(li)=}")
init is called
10 [0, 1, 2, 3, 4]
len(li)=10

Monkey patching is dynamically modifying a class or module at runtime by adding, replacing, or modifying its attributes or methods. While powerful, it should be used cautiously as it can make code harder to understand and maintain.

def iter(self):
    for i in self.lis:
        yield i

# monkey patching 
L.__iter__ = iter

for i in li:
    print(i, end=" ")
0 1 2 3 4 5 6 7 8 9 
def get_item(self, key):
    return self.lis[key]

def set_item(self, key, val):
    self.lis[key] = val


# monkey patching 
L.__getitem__ = get_item
L.__setitem__ = set_item

li[0] = -100
print(f"{li[0]=}")
str(li)
li[0]=-100
'10 [-100, 1, 2, 3, 4]'

Exception Handling

  • Purpose: Gracefully handle errors instead of crashing the program
  • try block: Contains code that might raise an exception
  • except block: Catches and handles specific exceptions
  • Multiple except blocks: Can catch different exception types separately
  • Exception as e: Captures the exception object for inspection
  • finally block (optional): Always executes, regardless of exceptions (useful for cleanup like closing files)
  • Raising exceptions: Use raise to throw exceptions intentionally
  • Common built-in exceptions: ValueError, TypeError, KeyError, IndexError, FileNotFoundError, ZeroDivisionError
try:
    result = 10 / 0  # This will raise ZeroDivisionError
    print(f"{result=}")
except ZeroDivisionError:
    print("Cannot divide by zero!")
except Exception as e:
    print(f"An error occurred: {e}")
finally:
    print("This always runs, error or not")
Cannot divide by zero!
This always runs, error or not

Conclusion

  • We have covered some basic part of Python programming language
  • The language is vast there is many other stuff are not covered below are few other imp for reference
    1. File I/O : Reading and writing files (especially the with statement for context managers)
    2. Lambda function : Anonymous functions for quick operations
    3. Decorators : A Python-specific feature that’s commonly used in frameworks
    4. Generators : generator for effienct way to iterate
  • There are many awsome stuff which I have not included, hope this blog acts as a launchpad for your python learning journey