Seite 1 von 2
Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 10:24
von Chris71
Hallo,
In meinen ersten Tests versuche ich mich an einer Klasse:
Code: Alles auswählen
#!/usr/bin/env python3
import time
class ausgabe():
def __init__(self, anzahl):
self.anzahl = anzahl
print("Start!")
def rechnen(self):
while anzahl > 0:
time.sleep(1)
if anzahl == 5:
print("Abbruch")
break
print(anzahl)
anzahl = anzahl + 1
else:
print("Fertig!")
ausgabe = ausgabe.rechnen(2)
Als Fehlermeldung kommt: UnboundLocalError: local variable 'anzahl' referenced before assignment
Leider verstehe ich die Fehlermeldugn nicht; Was will er mir sagen?
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 10:38
von Sirius3
@Chris71: im Klassenkörper sollte eigentlich nichts außer Definitionen stehen. Ein print gehört da nicht hin. Instanzattribute werden über "self" dereferenziert. Die while-Bedingung ist, wenn sie einmal erfüllt ist, immer erfüllt. Da wäre ein if besser.
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 10:45
von BlackJack
@Chris71: Zumal das semantisch so überhaupt gar keine Klasse ist. Wenn man nur eine `__init__()` und *eine* weitere Methode hat, dann ist das üblicherweise ein Warnzeichen das man eine Funktion viel zu kompliziert schreibt.
Klassennamen werden konventionell mit einem grossen Anfangsbuchstaben geschrieben und in MixedCase falls der Name aus mehreren Worten besteht. So kann man am Namen die Klasse und exemplare davon leicht unterscheiden, auch wenn man den ”gleichen” Namen für beides verwendet. Beispiel: ``parrot = Parrot()``. Hier ist `parrot` ein Exemplar das aus der Klasse `Parrot` erstellt wurde. Du erstellst in Deinem Code überhaupt gar kein Exemplar von `ausgabe`.
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 10:52
von cofi
Um es mal konkret zu machen.
Zeile 23 muesste so lauten:
Und alle `anzahl` in `rechnen` muessen `self.anzahl` sein.
Aber um es noch mal zu betonen: Das ist keine Klasse.
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 11:05
von garreth
Zudem solltest du dir Gedanken darüber machen was passiert wenn man deine Klasse mit einer Startzahl größer als fünf startet. Wenn Anfang und Ende bekannt sind solltest du lieber auf eine "for"-Schleife zurück greifen.
Code: Alles auswählen
#!/usr/bin/env python3
import time
def print_number(start):
print("Start!")
for i in range(start, 5):
time.sleep(1)
print(i)
print("Fertig!")
if __name__ == '__main__':
print_number(2)
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 11:53
von MagBen
Code: Alles auswählen
import time
class Ausgabe:
def __init__(self, anzahl):
self.anzahl = anzahl
print("Start!")
def rechnen(self):
while self.anzahl > 0:
time.sleep(1)
if self.anzahl == 5:
print("Abbruch")
break
print(self.anzahl)
self.anzahl += 1
else:
print("Fertig!")
a = Ausgabe(2)
a.rechnen()
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 12:17
von Chris71
wow! Vielen Dank für die vielen und schnellen Antworten!
Zuerst, ich möchte mit dieser Übung für mich nur mir selbst Python beibringen, um selbst zu verstehen wie das funktioniert, daher ist in er Bsp-Klasse auch nur eine "def" dabei usw.
Wie ist das zu verstehen, dass in eine Klasse kein Print hineingehört? Also grundsätzlich nicht oder nur in meinem Bsp wobei dies ja nur für mich zum lernen sein soll. Besser return verwenden?
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 12:29
von cofi
Uns ist schon klar, dass du damit lernen willst, wie Klassen funktionieren. Der Punkt ist nur, dass dazugehoert zu erkennen, wann Klassen noetig bzw hilfreich sind.
Das `print` an der Stelle wird ausgefuehrt, wenn `class` ausgefuehrt, d.h. erstellt, wird. Das ist der Fall, wenn das Modul das `ausgabe` enthaelt importiert wird.
Mit anderen Worten koenntest du das `print` auch vor die Klassendefinition schreiben und es aenderte sich nichts.
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 12:33
von MagBen
Die Technik die Du anwendest wird "Caveman Debugging" genannt, der Fehler wird mit print-statements totgeworfen. Eine bessere Möglichkeit zu sehen wie das Skript Zeile für Zeile abgearbeitet wird, ist ein Debugger. Mit einem guten Debugger in einer guten Entwicklungsumgebung kannst Du das Skript Zeile für Zeile laufen lassen und kannst dabei in alle Variablen hineinschauen.
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 12:48
von BlackJack
@MagBen: Wobei das in Python nicht unüblich ist mal kurz ein `print()` irgendwo einzufügen. Wenn das Projekt etwas grösser ist, dann stattdessen gleich `logging.debug()`. Oder das `q`-Modul für quick & dirty debugging.
Ein Debugger ist nicht immer ein guter Ersatz dafür weil man oft ja nicht Schritt für Schritt durchgehen will sondern nur an bestimmten Stellen sich bestimmte Werte anschauen möchte oder sehen möchte.
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 12:51
von Chris71
Dafür was ich mache gibts sogar einen Namen.
Stimmt ich mache dies um zu sehen wann und was es tut. Aber auch um später mal "sinnvolle" Funktionen dort einbauen zu können. Wenn ich das ganze mal besser kann möchte ich gerne per Webanwendung auf dem Raspberry über Pythonscripte etwas ausführen lassen. Ist es dafür sinnvoller Klassen mit Funktionen in eigenen Pythonscripten zu verwenden oder besser anders vorgehen?
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 13:00
von pillmuncher
MagBen hat geschrieben:"Caveman Debugging"
Ich sage Jehova:
Dave Beazley hat geschrieben:If you wanna do debugging, the only proper way to do it is with the print statement. Okay, well I just gonna say that upfront, you have to use the print statement, that's the only one and true way to debug.
Quelle:
http://www.youtube.com/watch?&v=sPiWg5jSoZI&t=518
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 13:02
von MagBen
Ich sage ja nicht, dass print-statements schlecht sind, ich meinte nur, ein Debugger ist besser. Wenn ein Debugger aber nicht möglich ist (Server-Anwendung, Multi-Threading, GUI-Event-Handling, ...) dann ist print oder logging oftmals die einzige Möglichkeit zu debuggen.
Ein Debugger muss auch nicht ständig im Einzelschritt Modus laufen. Das kann man über Breakpoints regeln.
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 13:09
von Kebap
Chris71 hat geschrieben:ich möchte mit dieser Übung für mich nur mir selbst Python beibringen, um selbst zu verstehen wie das funktioniert
Klassen und OOP allgemein werden nicht als die besten Anfängerthemen angesehen. Wie neu bist du denn bei Python? Hast du schon woanders programmiert, oder lernst du Programmieren auch noch neu? Das ist ja unabhängig von der jeweils verwendeten Sprache.
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 13:53
von BlackJack
@MagBen: Die Haltepunkte muss man aber verwalten und mir geht es oft so das ich einen bestimmten Wert sehen möchte der in einer Schleife existiert oder in einer Funktion/Methode die mehrfach aufgerufen wird und ich will dabei aber nicht jedes mal in einen Haltepunkt laufen und den per Klick bestätigen müssen.
Insgesamt finde ich die Handhabung eines Debuggers deutlich umständlicher als `print()`, `q`, oder `logging` (sofern man letzteres sowieso schon für das Programm benutzt).
Oft wüsste ich auch gar nicht wo ich Haltepunkte setzen sollte, weil man in Python ja eher selten Konstrukte wie beispielsweise in C oder Pascal hat, wo eine Funktion aus zwei bis drei verschachtelten Schleifen und jeder Menge Index- und Hilfvariablen besteht und man das alles nur schwer im Kopf nachvollziehen kann. Und Fehler fliegen einem dort nicht als Ausnahmen mit einem Typ, einer Nachricht, und einem Traceback um die Ohren, sondern als kommentarloser Programmabsturz oder fehlerhafte oder korrupte Daten(strukturen). Das wird in Python üblicherweise durch sicherere, ausdrucksstärkere Sprachmittel geregelt und Funktionen die das gleiche machen wie die C/Pascal-Gegenstücke sind in der Regel deutlich kürzer.
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 13:58
von BlackJack
@Chris71: Ob man Klassen verwendet hängt einzig und alleine davon ab ob Klassen für eine konkrete Aufgabe sinnvoll sind. Das gilt im Grunde auch für Aufgaben die über eine Webanwendung ausgeführt werden sollen. Also ob man das im Webserver direkt regelt, oder einen externen Prozess dafür aufruft, oder nebenher einen Daemon/Server dafür laufen hat mit dem der Webserver das kommuniziert. Ein Task-Server wie Celery käme eventuell auch in Betracht.
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 16:26
von Chris71
ich stelle mir mein Projekt so vor, dass die Webanwendung größtenteils "nur" die Oberfläche bildet und die Mess- und Regelarbeit inkl Daten in Datenbank schreiben dann die Webanwendung "beauftragt" und dann die Pythonycripte auf dem Raspberry ausführen. Kann man grob sagen für was Klassen sinnvoll sind und für welche Themen man eher dies oder das nimmt?
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 17:45
von BlackJack
@Chris71: Wann Klassen sinnvoll sind hat einzig und allein damit zu tun ob es Sinn macht eine Klasse zu schreiben, also ob man Daten und Funktionen die darauf operieren sinnvoll zu einem Objekt zusammenfassen kann. Ob man einen Zustand und Operationen die ihn verändern in einer Klasse kapseln kann. Wenn man das gleiche ganz einfach mit einer Funktion ausdrücken kann, wie Dein Beispiel, dann hat die Klasse offensichtlich keinen Sinn.
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 17:54
von pillmuncher
@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.
Re: Übergabe von Variablen in Klassen
Verfasst: Mittwoch 16. September 2015, 21:33
von Chris71
Vielen Dank für eure ausführlichen Antworten!
Super Forum!