@Chris71: Ob man Klassen oder Funktionen nimmt, hängt nicht von irgendwelchen Themen ab.
Eine Funktion repräsentiert eine einzelne Berechnung, im allgemeinsten Sinne. Etwas, das der Rechner tun soll und das ich als logische Einheit betrachte. Funktionen sollten so programmiert werden, dass klar ist, ob sie den Zustand ihrer Laufzeitumgebung modifizieren. Eine Funktion, die etwas in eine Datei schreibt, sollte nicht 
check_something() oder 
read_the_other_thing() heißen, sondern eher 
write_the_stuff(). Der Name sollte im Imperativ stehen um die Funktion als Befehl kenntlich zu machen. Funktionen, die Werte berechnen, sollten keine Seiteneffekte haben, dh., sie sollten den Zustand ihrer Laufzeitumgebung nicht ändern. Der Name einer solchen Funktionen sollte klar machen, dass es sich um eine Abfrage handelt. In anderen (Nicht-Objekt-Orientierten) Sprachen als Python verwendet man gerne 
get_ als Präfix, also 
get_state(something), 
get_total_price(order), 
get_customer_address(customer_id). In Python dagegen würde man sich Klassen definieren. Diese modularisieren den Zustand über eine Menge von Daten. Die Klassen sollte man sich allerdings nicht als bloße Datentöpfchen vorstellen, in die man zusammengehörige Daten reinwirft, um sie dann von außen ab und zu umzurühren, zu vermehren oder zu vermindern. Sondern man sollte sich eine Klasse als Beschreibung von Objekten (den sog. Exemplaren oder Instanzen) vorstellen, wobei diese Objekte es sind, die sich in je irgendeinem Zusand befinden, den sie einkapseln. zB. en File-Objekt. Es hat die logischen Zustände 
geöffnet oder 
geschlossen. Ein File-Objekt kann sich zu jedem Zeitpunkt nur in genau einem dieser Zustände befinden, dh. nie zugleich geöffnet und geschlossen sein. Wenn es sich im geöffnet-Zustand befindet, kann man Daten lesen oder schreiben, je nachdem, ob man die Datei zum Lesen oder Schreiben geöffnet hat. Im geschlossen-Zustand kann man weder lesen noch schreiben. Als Klasse könnte das ungefähr so aussehen:
Code: Alles auswählen
class File:
    def __init__(self, name):
        self.name = name
        self._is_open = False
        self._mode = None
    def open(self, mode):
        if self._is_open:
            raise Exception('file already open')
        # <-- hier sollte das tatsächliche Öffnen der Datei stattfinden
        self._mode = mode
        self._is_open = True
    def close(self):
        if not self._is_open:
            raise Exception('file is not open')
        # <-- hier sollte das tatsächliche Schließen der Datei stattfinden
        self._mode = None
        self._is_open = False
    def read(self):
        if not self._is_open:
            raise Exception('file is not open')
        if not self._mode == "r":
            raise Exception('file was not opened in read mode')
        # <-- hier sollte das tatsächliche lesen aus der Datei stattfinden
        return ...
    def write(self, data):
        if not self._is_open:
            raise Exception('file is not open')
        if not self._mode == "w":
            raise Exception('file was not opened in write mode')
        # <-- hier sollte das tatsächliche Schreiben in die Datei stattfinden
Die tatsächliche Schnittstelle von Dateien ist natürlich etwas komplexer. Mit Schnittstelle meine ich die Gesamtheit aller auf Dateiobjekten definierten Methoden zusammen mit ihren Parametern. Jedes Objekt, das dieselbe Schnittstelle definiert, kann dann an jeder Stelle verwendet werden, wo ein Datei-Objekt erwartet wird. Hier zB.:
Code: Alles auswählen
def write_some_state_to_file(state, out_file):
    data = str(state)
    out_file.open()
    out_file.write(data)
    out_file.close()
Wenn ich jetzt diese Klasse schreiben würde:
Code: Alles auswählen
class PseudoFileThatWritesToScreen:
    def open(self, mode):
        pass
    def close(self):
        pass
    def write(self, data):
        print(data)
dann könnte ich folgendes machen:
Code: Alles auswählen
some_number = 123
screen_with_file_interface = PseudoFileThatWritesToScreen()
write_some_state_to_file(some_number, screen_with_file_interface)
Diese Austauschbarkeit der Implementiertung unter Beibehaltung der öffentlichen Schnittstelle nennt man Polymorphismus. Sie ist das zentrale Konzept der Objektorientierten Programmierung.
Was sollte man alles zur Klasse machen? Üblicherweise das, was man mittels Nomina referenziert. Also zB. Kunde, Artikel, Bestellung, Warenkorb, usw. Der Code könnte vielleicht so aussehen:
Code: Alles auswählen
from datetime import datetime
from uuid import uuid4
class Order:
    def __init__(self, customer_id, line_items):
        self._id = uuid4()
        self._customer_id = customer_id
        self._line_items = tuple(line_items)
        self._date_placed = datetime.now()
class Customer:
     def __init__(self, id, name, address):
        self._id = id
        self._name = name
        self._address = address
        self._cart = []
        self._orders = []
    def add_line_item_to_cart(self, article_id, amount):
        if amount < 1:
            raise Exception('cannot add zero or negative amount to cart.')
        self._cart.append([article_id, amount])
    def place_order(self):
        self._orders.append(Order(self._id, self._cart))
        self._cart = []
    def get_all_ordered_articles(self, db):
        article_ids = set()
        for order in self._orders:
            for article_id, amount in order.line_items:
                article_ids.add(article_id)
        articles = ...  # <-- get articles by id from db
        return articles
class Article:
    ...
...
customer = Customer(uuid4(), 'Joe Shmoe', 'Somewhere Street 123 in Sometown')
article = Article(...)
customer.add_line_item_to_cart(article.id, 3)
customer.place_order()
Aller Code ist ungetestet und dient nur zur Illustration.
 
            
			
									
						
							In specifications, Murphy's Law supersedes Ohm's.