Skip to content

python quickstart

Marcel Schmalzl edited this page Apr 7, 2025 · 1 revision

Python - a quick start

Python Fundamentals

Variables, Data Types, and Basic Operations

# Game Variables
player_name = "Hero"    # String
player_level = 5        # Integer
player_health = 100.0   # Float
has_sword = True        # Boolean

# Game Logic
damage = 20
player_health -= damage  # Subtract damage
# Printing and formatting strings
# Many ways exist but f-strings are usually preferred in terms of readability and performance
print(f"{player_name} took {damage} damage! Health: {player_health}")

message = f"{player_name} is level " + str(player_level) + "."     # Not using f-strings can be more cumbersome but sometimes there are reasons for it
print(message)

Control Flow: If/Elif/Else

Use for conditions; elif (else-if) is optional.

enemy_level = 7

if player_level >= enemy_level:
    print("You are strong enough to fight!")
elif player_level + 2 >= enemy_level:
    print("You can try your luck!")
else:
    print("You are too weak!")

Loops

# For loop (inventory)
inventory = ["sword", "potion", "shield"]       # List of strings
for item in inventory:
    print(f"You have: {item}")

# While loop (leveling up)
experience = 0
level_up_threshold = 100
while experience < level_up_threshold:
    experience += 25
    print(f"Current experience: {experience}")
print("Level up!")

# While loop with break statement
count = 0
while True:
    print(f"enemy attacked {count} times")
    count +=1
    if count >=3:
        break # break out of the attack loop.

Functions

def calculate_damage(attack_power, defense_power):      # Arguments passed; feels as passed by value (=copy) (but pay attention about mutability!)
    """Calculates damage dealt."""
    return max(0, attack_power - defense_power)

damage_dealt = calculate_damage(30, 15)
print(f"Damage dealt: {damage_dealt}")

Function pointers with LUT

def attack_with_sword():
    print("You slash with your sword!")

def attack_with_magic():
    print("You cast a magic spell!")

def attack_with_bow():
    print("You shoot an arrow!")

# Lookup table for attack types
attack_lut = {
    "sword": attack_with_sword,
    "magic": attack_with_magic,
    "bow": attack_with_bow,
}

attack_type = "magic"
attack_function = attack_lut.get(attack_type, lambda: print("Invalid attack type."))
attack_function()

Pattern Matching

def describe_item(item):
    match item:
        case ("sword", level) if level > 10:                # Guard condition
            print(f"A powerful sword of level {level}!")
        case ("sword", level):
            print(f"A sword of level {level}.")
        case ("potion", health) if health > 50:
            print(f"A major healing potion that heals {health} health!")
        case ("potion", health):
            print(f"A potion that heals {health} health.")
        case ("shield", durability, "metal"):               # Matching multiple items in tuple
            print(f"A metal shield with {durability} durability.")
        case ("shield", durability):
            print(f"A shield with {durability} durability.")
        case {"name": str(name), "level": int(level)}:      # Matching a dictionary
            print(f"A named item {name} of level {level}")
        case ["monster", *rest]:                            # Catching lists, and rest of list
            print(f"A monster encountered, with {len(rest)} associated modifiers.")
        case _:
            print("Unknown item.")

describe_item(("sword", 15))
describe_item(("potion", 60))
describe_item(("potion", 20))
describe_item(("shield", 100, "metal"))
describe_item(("shield", 50))
describe_item({"name": "Fireball", "level": 3})
describe_item(["monster", "fire", "fast"])
describe_item(("unknown", 1))

Error handling

Know where something can fail and make sure you deal with it. Depending on the context you might want to raise yourself exceptions and let others handle it (e.g. libraries). Unit tests help covering all cases.

Good:

def load_game_data(filename):
    try:
        with open(filename, 'r') as file:
            return file.read()
    except FileNotFoundError:
        print(f"Error: Game data file '{filename}' not found.")
        return None

Bad:

def load_game_data(filename):
    # No error handling; loading the file could fail
    file = open(filename)
    return file.read()

Object Oriented Programming (OOP)

Classes and Objects

class Character:
    def __init__(self, name, level, health):
        self.name = name
        self.level = level
        self.health = health

    def take_damage(self, damage):
        damage = self._apply_defense(damage)    # Apply defense before taking damage
        self.health -= damage
        print(f"{self.name} took {damage} damage! Health: {self.health}")

    def _apply_defense(self, damage):           # Private function (in Python by naming convention)
        defense_modifier = self.level * 2
        reduced_damage = max(0, damage - defense_modifier)  # Damage must be positive
        return reduced_damage

player = Character("Hero", 5, 100)
enemy = Character("Goblin", 7, 50)

enemy_damage = 25
player.take_damage(enemy_damage)

Inheritance

class Enemy(Character): #Enemy inherits from character
    def __init__(self, name, level, health, attack_power):
        super().__init__(name, level, health) # call parent constructor.
        self.attack_power = attack_power

    def attack(self, target):
        damage = calculate_damage(self.attack_power, target.level * 5)
        target.take_damage(damage)

goblin = Enemy("Goblin", 7, 50, 20)
goblin.attack(player)

Abstract Base Classes and Methods

from abc import ABC, abstractmethod
from typing import List, Any

class Character:
    def __init__(self, name: str, health: int, mana: int):
        self.name = name
        self.health = health
        self.mana = mana

    def __str__(self) -> str:
        # Overwrite methode string representation of the object
        return f"{self.name} (H:{self.health}, M:{self.mana})"

class Action(ABC):
    # Abstract base class (ABC) to define a common interface for actions
    @abstractmethod # type: ignore
    def execute(self, caster: Character, target: Character) -> Any:
        # Abstract method (pure virtual function) that must be implemented by subclasses
        ...

    def perform(self, caster: Character, target: Character) -> Any:
        # Concrete method in the base class providing a common algorithm
        print(f"{caster.name} -> {self.__class__.__name__} -> {target.name}.")
        self.execute(caster, target)
        print(f"{caster} | {target}")

class Attack(Action):
    # Subclass implementing the abstract method, demonstrating polymorphism
    def execute(self, caster: Character, target: Character) -> Any:
        target.health -= 20
        print(f"{target.name} -20 HP.")
        return None # Explicit return None to avoid type errors.

class Heal(Action):
    # Another subclass implementing the abstract method, demonstrating polymorphism
    def execute(self, caster: Character, target: Character) -> Any:
        target.health += 30
        print(f"{target.name} +30 HP.")
        return None

class ManaDrain(Action):
    # Yet another subclass implementing the abstract method, demonstrating polymorphism
    def execute(self, caster: Character, target: Character) -> Any:
        target.mana -= 15
        caster.mana += 15
        print(f"{target.name} -15 Mana, {caster.name} +15 Mana.")
        return None

def apply_actions(caster: Character, target: Character, actions: List[Action]) -> Any:
    # Function using the abstract class to apply a list of actions (demonstrates polymorphism)
    for action in actions:
        action.perform(caster, target)

player = Character("Hero", 100, 50)
enemy = Character("Goblin", 80, 20)
actions = [Attack(), Heal(), ManaDrain()]

apply_actions(player, enemy, actions)

Polymorphism:

Explanation: Polymorphism allows objects of different classes to be treated as objects of a common type. This is often used in combination with inheritance.

Example:

class Character:
    def __init__(self, name, health):
        self.name = name
        self.health = health

    def attack(self, target):
        print(f"{self.name} attacks {target.name}!")

class Warrior(Character):
    def attack(self, target):
        print(f"{self.name} swings their sword at {target.name}!")

class Mage(Character):
    def attack(self, target):
        print(f"{self.name} casts a spell at {target.name}!")

player1 = Warrior("Arthur", 100)
player2 = Mage("Merlin", 80)

characters = [player1, player2]

for character in characters:
    character.attack(player2) # Polymorphic call.

Different character classes (Warrior, Mage, Archer) can all have an attack() method, but each class implements it differently. Polymorphism lets you treat them all as Character objects and call attack() without knowing the specific class.

Advanced Language Features

Decorators

Decorators modify the behaviour of functions or classes. It acts similarly like a wrapper.

def log_action(func):
    def wrapper(*args, **kwargs):
        print(f"Action: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_action
def use_potion(character):
    print(f"{character.name} uses a potion.")

use_potion(player1)

You could use decorators to log player actions, apply status effects, or add special effects to abilities.

Type Hinting

Type hints specify the expected data types of variables and function parameters. Note that type hinting is no type enforcement (like in strongly typed languages (C++)).

def calculate_damage(attack_power: int, defense: int) -> int:
    return max(0, attack_power - defense)

damage = calculate_damage(30, 10)

Type hints make it clear what types of data functions expect and return (e.g., int for damage, str for player names).

Data Classes

Data classes simplify the creation of data-centric classes.

from dataclasses import dataclass

@dataclass
class Item:
    name: str
    value: int
    weight: float

sword = Item("Excalibur", 100, 5.0)
print(sword)

Code Style and Best Practices

In general check the coding guidelines of your project; also PEP8 and most widespread guidelines (like the Google Style Guides) are a good choice. The following only highlights some important points.

Naming

As simple as it sounds good naming is very important and you should spend some time to think about it.

Good:

player_health = 100
enemy_attack_power = 20
class GameCharacter:
    pass
def calculate_damage(attack, defence):
    pass

Good naming makes code self-explanatory...

Bad:

a = 100
b = 20
class GC:
    pass
def calc_dmg(x,y):
    pass

... bad naming requires extra mental effort to understand.

Function length and complexity

Ideally one function should do one thing.

Good:

def validate_player_input(input_string):
    # simple input validation
    pass

def apply_status_effect(player, effect):
    # apply status effect logic
    pass

def handle_player_turn(player, command):
    if validate_player_input(command):
        apply_status_effect(player, command)
        #more simple logic

Bad:

def handle_player_turn(player, command):
    # 50 lines of complex logic here...
    if ...:
        ...
    for ...:
        ...
    return result

Short, focused functions are easier to read and test. Long functions are harder to maintain and understand.

Comments and Documentation

Code should be self-explanatory through good naming and structure. When this is not enough add comments where appropriate (often you explain the why of what you are doing if it's not clear). Good codes should always include docstings.

Good:

def calculate_damage(attack_power: int, defense: int) -> int:
    """
    Calculates the damage dealt to a target.

    Args:
        attack_power: The attacker's attack power.
        defense: The target's defense.

    Returns:
        The damage dealt.
    """
    return max(0, attack_power - defense)

Bad:

def calc_dmg(x,y):
    # does stuff
    return max(0, x-y)

Design Patterns

Design patterns help to solve common problems.

Check out any favourite book about design patterns and/or the standard work Design Patterns: Elements of Reusable Object-Oriented Software from the Gang of Four (GoF).

Design patterns with gaming context

Python 3

(un)fold
Snippets
General
Libs

Linux/bash

(un)fold
Guides
Scripts

Git

(un)fold

C/C++

(un)fold

Video

(un)fold

Databases

(un)fold

Misc

(un)fold

Windows

(un)fold

Mac

(un)fold

SW recommendations

(un)fold

(Angular) Dart

(un)fold
Clone this wiki locally