Last updated: August 20, 2025

If you’ve spent a lot of time with Ruby, C, or Java, this might just be the most efficient way to wrap your head around getting used to Python as a daily driver. Because it is for me.

Python is used for a lot of the interesting practical work around AI. But I couldn’t make it stick for reasons that others have articulated:

“Python to me, it’s ugly. It’s just ugly because it’s full of superfluous instructions that are necessary for legacy reasons, and my brain can’t cope with that.

When you make a class in Python, the Initializer method, the starting method is def. Okay, fair enough. That’s actually the same as Ruby. D-E-F: definition of a method. Then it is underscore. Not one underscore. Two. init, underscore underscore, parentheses start, self, comma, and then the first argument.

I look at that and go, “I’m sorry, I’m out. I can’t do it.”

David Heinemeier Hansson on the Lex Fridman podcast

This is me trying to make it happen anyway. I last tested this on Python 3.13.6.

Boolean and Empty Values: True, False, and None

Use the following:

  • True instead of true
  • False instead of false
  • None instead of nil, null, or NULL
  • is instead of ==
  • and instead of &&
  • or instead of ||
  • not instead of !

Use parentheses to improve readability where there are more complex conditions.

Conditionals

It’s an elif kind of language.

def validate_input(user_input):
    if user_input is None:
        print("Error: Input is None (not provided)")
    elif not user_input:  # Checks for empty/zero/falsy values (but not None)
        print("Error: Input is empty or falsy (e.g., '', 0, [])")
    elif isinstance(user_input, int) and user_input < 0:
        print("Error: Negative integer provided")
    elif isinstance(user_input, str) and not user_input.isalpha():
        print("Error: String contains non-alphabetic characters")
    else:
        print(f"Valid input: {user_input} (Type: {type(user_input).__name__})")

# Test cases
validate_input(None)          # Case 1: None
validate_input("")            # Case 2: Empty string (falsy)
validate_input(0)             # Case 3: Zero (falsy)
validate_input(-5)            # Case 4: Negative integer
validate_input("Hello123")    # Case 5: Invalid string
validate_input("Python")      # Case 6: Valid string
validate_input(42)            # Case 7: Valid integer
validate_input([1, 2, 3])     # Case 8: Valid non-empty list

Strings

Interpolation

f-strings are the recommended way in Python 3.6+. They’re pretty nice.

name = "Alice"
age = 30
print(f"Hello, {name}! You are {age} years old.")

Classes

At some point, your prototype spaghetti Python script will grow unwieldy. Time to get more organized.

Declaration

class Dog:  # Class definition
    def __init__(self, name, breed):  # Constructor
        self.name = name   # Instance attribute
        self.breed = breed # Instance attribute

    def bark(self):  # Method
        print(f"{self.name} says: Woof!")

Instantiation

my_dog = Dog("Rex", "Labrador")

print(my_dog.name)   # Output: Rex
print(my_dog.breed)  # Output: Labrador
my_dog.bark()        # Output: Rex says: Woof!

Static Methods (@staticmethod)

Key things to note:

  • No access to the class or instance (cls/self).
  • Behave like regular functions, but belong to the class namespace.
class MathUtils:
    @staticmethod
    def add(a, b):
        return a + b

# Call without instantiating
print(MathUtils.add(5, 3))  # Output: 8

Class Methods (@classmethod)

Key things to note:

  • Access the class itself (via cls) but not instance-specific data. (How would you even do that, anyway?)
  • Can modify class-level state (namely, class variables).
class Pizza:
    base = "Thin crust"

    @classmethod
    def change_base(cls, new_base):
        cls.base = new_base  # Modifies class-level state

# Call without instantiating
print(Pizza.base)            # Output: "Thin crust"
Pizza.change_base("Deep dish")
print(Pizza.base)            # Output: "Deep dish"

Lists

Declare Empty List

empty_list = []

# Less common, still valid.
another_empty_list = list() 

Declare a Populated List

my_list = ["apple", "banana", "cherry"]
length = len(my_list)
print(length)  # Output: 3

Add Objects to List

Use .append() to add a single item.

my_list.append("apple")

A couple of ways to add lists together: .extend() and +:

my_list.extend(["banana", "cherry"]) 

my_list = my_list + ["date", "elderberry"]

Size / Length

Use len() to get the length of a list. It’s not to be called on the list itself.

# Check if list is empty
if len(my_list) == 0:
    print("List is empty")

# Or more Pythonically:
if not my_list:
    print("List is empty")

# Get the last item safely
if len(my_list) > 0:
    last_item = my_list[len(my_list) - 1]
    # Or: last_item = my_list[-1]

len() actually works with any sequence or collection (lists, tuples, strings, dictionaries, sets, etc.)

Traversal

Looking for that Pythonic for loop syntax? Look no further.

my_array = [1, 2, 3, 4, 5]

# Iterate over each element
for element in my_array:
    print(element)

If you need an index, use enumerate():

for index, element in enumerate(my_array):
    print(f"Index: {index}, Value: {element}")

Reversal

Reverse a list:

# Will happen in-place
my_list = [1, 2, 3, 4, 5]
my_list.reverse()  # Reverses the list in place
print(my_list)    # Output: [5, 4, 3, 2, 1]

Sorting

Deep Sorting

Sort objects by some property of those objects.

# Using sorted() returns a new sorted list
sorted_list = sorted(list_of_objects, key=lambda obj: obj.foo)

# Sorts in-place
list_of_objects.sort(key=lambda obj: obj.foo)

# Descending order
sorted_objects_desc = sorted(objects, key=lambda obj: obj.foo, reverse=True)

# Sort by 'foo' first, then by 'bar'
sorted_objects = sorted(objects, key=lambda obj: (obj.foo, obj.bar))

Style Guide

The cool kids will all point you to PEP 8, which is the authoritative style guide for Python code.