Seite 1 von 1
Instanzmethode wird nicht gefunden
Verfasst: Sonntag 27. September 2015, 20:49
von RogerWilco77
Hallo zusammen,
ich fange gerade mit Python an und verstehe einfach nicht, warum die Klassenmethode "testen" nicht gefunden wird.
Die Fehlermeldung lautet:
AttributeError: 'Datenbank' object has no attribute 'testen'
(es wundert mich schon, dass die Fehlermeldung auf ein Attribut hinweist...)
Die IDE (Spyder) erkennt die Methode, dennoch muss ich irgendetwas mit "db." übersehen...
Python: 3.4.3 (Anaconda 2.3.0)
Mac OsX 10.10.5
Zum Testen der Klasse verwende ich ein Modul namens Test.py:
Code: Alles auswählen
import Datenbank
db = Datenbank.Datenbank('testdatei.db')
db.testen()
Die Klasse:
Der SQL Teil dient nur zu Testzwecken und ist nicht Teil des späteren Programms, daher bitte ignorieren.
Code: Alles auswählen
import sqlite3
class Datenbank:
def __init__(self, datenbankname):
self.datei = datenbankname
self.connection = sqlite3.connect(datenbankname)
def testen(self):
self.cursor = self.connection.cursor()
self.sql_command = """
CREATE TABLE employee (
staff_number INTEGER PRIMARY KEY,
fname VARCHAR(20),
lname VARCHAR(30),
gender CHAR(1),
joining DATE,
birth_date DATE);"""
self.cursor.execute(self.sql_command)
self.sql_command = """INSERT INTO employee (staff_number, fname, lname, gender, birth_date)
VALUES (NULL, "William", "Shakespeare", "m", "1961-10-25");"""
self.cursor.execute(self.sql_command)
self.sql_command = """INSERT INTO employee (staff_number, fname, lname, gender, birth_date)
VALUES (NULL, "Frank", "Schiller", "m", "1955-08-17");"""
self.cursor.execute(self.sql_command)
# never forget this, if you want the changes to be saved:
self.connection.commit()
self.connection.close()
Vielen Dank für Eure Hilfe oder zumindest einen Wink in die richtige Richtung...
die komplette Fehlermeldung: (einige Zeilen habe ich entfernt, daher stimmen die Zeilennummern nicht)
>>> runfile('/Users/Roger/anaconda/Nebenkosten/Test.py', wdir='/Users/Roger/anaconda/Nebenkosten')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/Roger/anaconda/lib/python3.4/site-packages/spyderlib/widgets/externalshell/sitecustomize.py", line 685, in runfile
execfile(filename, namespace)
File "/Users/Roger/anaconda/lib/python3.4/site-packages/spyderlib/widgets/externalshell/sitecustomize.py", line 85, in execfile
exec(compile(open(filename, 'rb').read(), filename, 'exec'), namespace)
File "/Users/Roger/anaconda/Nebenkosten/Test.py", line 11, in <module>
db.testen()
AttributeError: 'Datenbank' object has no attribute 'testen'
Re: Instanzmethode wird nicht gefunden
Verfasst: Sonntag 27. September 2015, 21:03
von BlackJack
@RogerWilco77: Verstehe ich auch nicht. Bist Du sicher das Du die Datei gespeichert hast und die Methode nicht nur in der ungespeicherten Datei im Editor existiert? Hast Du noch andere `Datenbank`-Module/Klassen?
Falls Dich das Wort Attribut wundern sollte: Alles was man mit dem Punktoperator von Objekten abfragen kann sind Attribute. Es gibt an der Stelle keinen relevanten Unterschied zwischen ”Daten” und ”Methoden”, denn auch eine Methode ist ein Wert wie jeder andere. In Python ist halt alles was man an einen Namen binden kann ein Objekt.
In der `testen()`-Methode wird viel zu viel an das Objekt gebunden. Da sollte überhaupt kein Attribut neu eingeführt werden, das sollten alles lokale Namen sein.
Die Code- und Datenwiederholung für die beiden Datensätze ist unschön. Man würde da auch die Daten von der SQL-Anweisung trennen und in der SQL-Anweisung durch Platzhalter ersetzen. Dann kann man auch `executemany()` verwenden um beide Datensätze mit einerm Aufruf hinzuzufügen.
Auch bei Datenbankspalten sollte man vernünftige Namen wählen und nicht willkürlich nicht allgemein gebräuchliche Abkürzungen verwenden. Gerade im Hinblick auf ORMs müsste man sonst mit unschönen Attributnamen leben, oder die extra nochmal auf einen sprechenden Namen abbilden. (Apropos ORM, als nächstes sollte man sich nach SQL-Grundlagen dann mit SQLAlchemy auseinander setzen, statt sich das selber nochmal nachzuprogrammieren.

)
Die numerische, künstliche Primärschlüsselspalte heisst üblicherweise `id`. Und statt `NULL` als Wert anzugeben, könnte man die Spalte beim Eintrage auch einfach weglassen.
Re: Instanzmethode wird nicht gefunden
Verfasst: Sonntag 27. September 2015, 21:10
von RogerWilco77
Danke für die schnelle Antwort.
Ich habe den Code für den Datenbankteil erstmal von
http://www.python-kurs.eu/sql_python.php übernommen.
Es ging mir erstmal darum irgendwas in irgendeine Datenbank zu schreiben.
Sollte die Verbindung nicht stehen, hätte ich mit einer entsprechenden Fehlermeldung weiter arbeiten können.
Beide Dateien sind definitiv gespeichert und auch über den Finder/Explorer auffindbar.
Ich kann nochmal die gesamte Fehlermeldung oben ergänzen....
Re: Instanzmethode wird nicht gefunden
Verfasst: Sonntag 27. September 2015, 21:45
von Sirius3
@RogerWilco77: die verlinkten Seite ist nicht zu empfehlen. Die anderen "Kurs"-Teile sind genauso schlecht. Was passiert wenn Du Test.py direkt, nicht aus der IDE heraus startest?
Re: Instanzmethode wird nicht gefunden
Verfasst: Sonntag 27. September 2015, 21:45
von BlackJack
@RogerWilco77: Der Code auf der Webseite steht allerdings nicht sinnloserweise in einer Klasse. Klassen sind kein Selbstzweck, man sollte sie nur einsetzen wenn man das gleiche nicht deutlich einfacher mit einer Funktion machen kann. Willst Du die Verbindung in der `test()`-Methode tatsächlich schliessen‽ Dann ist die Klasse noch sinnfreier als sie das ohnehin schon ist.
Die Begrenzungen von Vor- und Nachname sind IMHO ein bisschen zu klein. Es gibt Leute mit mehreren Vornamen und langen Doppelnamen als Nachnamen. Und das jetzt nur für unseren Kulturkreis gesehen.
Das ganze in ein bisschen vernünftiger, wobei die Klasse auch hier noch nicht wirklich Sinn macht, da sie nur die Verbindung kapselt und eine einzige tatsächliche Methode bietet, was man wie schon gesagt auch einfach mit einer Funktion lösen kann:
Code: Alles auswählen
import sqlite3
from contextlib import closing
from sqlite3 import Date
class Database(object):
def __init__(self, filename):
self.connection = sqlite3.connect(filename)
def __enter__(self):
return self
def __exit__(self, *_args):
self.close()
def close(self):
self.connection.close()
def test(self):
with closing(self.connection.cursor()) as cursor:
try:
cursor.execute(
'CREATE TABLE employee ('
' id INTEGER PRIMARY KEY,'
' firstname VARCHAR(200) NOT NULL,'
' lastname VARCHAR(200) NOT NULL,'
" gender CHAR(1) NOT NULL DEFAULT '?'"
" CHECK (gender IN ('m', 'f', '?')),"
' joined DATE NOT NULL DEFAULT CURRENT_DATE,'
' birth_date DATE NOT NULL'
');'
)
cursor.executemany(
'INSERT INTO employee (firstname, lastname, gender,'
' birth_date)'
' VALUES (?, ?, ?, ?);',
[
['William', 'Shakespeare', 'm', Date(1961, 10, 25)],
['Frank', 'Schiller', 'm', Date(1955, 8, 17)],
]
)
self.connection.commit()
except:
self.connection.rollback()
raise
def main():
with Database('test.db') as database:
database.test()
if __name__ == '__main__':
main()
Re: Instanzmethode wird nicht gefunden
Verfasst: Sonntag 27. September 2015, 21:53
von RogerWilco77
Sorry, vielleicht war es wirklich eine ungünstige Wahl.
Eigentlich wollte ich nachher damit Nebenkosten erfassen und kein Adressbuch.
Es ging mir erstmal darum eine Datenbankverbindung zu öffnen.
Ich hätte den Teil besser komplett herausgenommen oder abgekürzt.
Entschuldigt bitte die Verwirrung.
Die Datenbank wurde angelegt, daher wollte ich nur sehen, ob auch etwas hinein geschrieben wird.
Der Inhalt war mir erstmal vollkommen egal.
Re: Instanzmethode wird nicht gefunden
Verfasst: Montag 28. September 2015, 19:46
von RogerWilco77
Sirius3 hat geschrieben: Was passiert wenn Du Test.py direkt, nicht aus der IDE heraus startest?
@Sirius3
In diesem Fall ergibt es
NameError: name 'Test' is not defined
Nachtrag:
Nachdem ich heute neu gestartet und der Empfehlung von Sirius gefolgt bin, habe ich es nochmal in Spyder probiert.
Und siehe da: es funktioniert.
Aber warum?
Re: Instanzmethode wird nicht gefunden
Verfasst: Montag 28. September 2015, 19:49
von BlackJack
@RogerWilco77: Was hast Du denn da gestartet? Oder versuchst Du `Test.py` in einer *Python*-Shell einzugeben? Das geht natürlich nicht, denn die interpretiert das natürlich als Python-Code und nicht als Dateiname der gestartet werden soll. Du musst das aus einer Shell Deines Betriebssystems aus starten.
Re: Instanzmethode wird nicht gefunden
Verfasst: Montag 28. September 2015, 19:55
von RogerWilco77
Nein, mit python3 eine Konsole geöffnet und dann Test.py
Re: Instanzmethode wird nicht gefunden
Verfasst: Montag 28. September 2015, 20:08
von BlackJack
@RogerWilco77: Naja das ist halt falsch. Einfach *nur* eine Konsole öffnen und da drin dann Test.py starten.
Re: Instanzmethode wird nicht gefunden
Verfasst: Montag 28. September 2015, 21:32
von RogerWilco77
Ups... natürlich:
$python3
>>>import Test.py
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/Axel/anaconda/Nebenkosten/Test.py", line 11, in <module>
db.testen()
File "/Users/Axel/anaconda/Nebenkosten/Datenbank.py", line 26, in testen
self.cursor.execute(self.sql_command)
sqlite3.OperationalError: table employee already exist
... und hier wurde jetzt offenbar eine Verbindung zur Datenbank erstellt.
Aber das hat mittlerweile auch in Spyder funktioniert.
Offenbar hat sich zwischen gestern Abend und heute etwas verändert (von einem Neustart abgesehen?).
PS:
Danke, dass Du nicht direkt die Lösung geschrieben hast. So muss man doch mal nachdenken...

Re: Instanzmethode wird nicht gefunden
Verfasst: Dienstag 29. September 2015, 11:38
von BlackJack
@RogerWilco77: Noch mal etwas zum verlinkten Tutorial: Da wird etwas gemacht was man wirklich *nie* machen sollte: Werte selbst als Zeichenkette in eine SQL-Anweisung hineinformatieren. Das ist gefährlich im Sinne von wenn man Pech hat fällt das auf die Nase bis zu wenn man ganz viel Pech hat nutzt das jemand als Angriffsfläche für eine SQL-Injection.
Die Werte im Tutorial sind auch nicht so ganz nachvollziehbar. In der Abfrage am Ende gibt es zwei Datensätze die vorher nie eingetragen wurden und aus „Jane Wall“ ist plötzlich „Jane Thunder“ geworden.
SQLAlchemy hatte ich ja schon mal erwähnt und möchte dafür nochmal ”Werbung” machen. Das vereinfacht einiges und man muss keine SQL-Anweisungen mehr aus Zeichenketten zusammenstückeln, bekommt also ein robusteres Programm:
Code: Alles auswählen
#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
from datetime import date as Date
from dateutil.relativedelta import relativedelta
from sqlalchemy import CHAR, Column, create_engine, DATE, INTEGER, VARCHAR
from sqlalchemy.orm import Session
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Employee(Base):
__tablename__ = 'employee'
id = Column(INTEGER, primary_key=True)
firstname = Column(VARCHAR(200), nullable=False)
lastname = Column(VARCHAR(200), nullable=False)
gender = Column(CHAR(1), nullable=False, default='?')
joined = Column(DATE, nullable=False, default=Date.today)
birth_date = Column(DATE, nullable=False)
@property
def age(self):
return relativedelta(Date.today(), self.birth_date).years
def print_employees(employees):
for employee in employees:
print(
' {0.firstname} {0.lastname} ({0.age}),'
' geb. {0.birth_date:%d.%m.%Y}'.format(employee)
)
print()
def main():
#
# Datenbank-Engine erstellen: Hier kann man sehr einfach die URL austauschen
# und damit ein andere DBMS wählen ohne das man am Rest des Beispiels etwas
# ändern muss.
#
engine = create_engine('sqlite:///test.db')
#
# Alle Tabellen für von `Base` abgeleitete Klassen erstellen sofern die
# Tabelle nicht bereits existiert:
#
Base.metadata.create_all(engine)
session = Session(engine)
#
# Ein paar Daten eintragen:
#
for firstname, lastname, gender, birth_date in [
('William', 'Shakespeare', 'm', Date(1961, 10, 25)),
('Frank', 'Schiller', 'm', Date(1955, 8, 17)),
('Jane', 'Wall', 'f', Date(1989, 3, 14)),
]:
session.add(
Employee(
firstname=firstname,
lastname=lastname,
gender=gender,
birth_date=birth_date,
)
)
session.commit()
#
# Jane Wall wird Profiwrestlerin und ändert ihren Nachnamen in „Thunder“:
#
jane = (
session.query(Employee)
.filter_by(firstname='Jane', lastname='Wall')
.one() # Wenn es mehr als einen Datensatz gibt auf den das zutrifft
# dann führt `one()` zu einer Ausnahme!
)
jane.lastname = 'Thunder'
session.commit()
#
# Ein paar Abfragen:
#
employee_query = session.query(Employee)
print('Alle Datensätze (nach Nachname sortiert):')
print_employees(employee_query.order_by(Employee.lastname))
print('Nur männliche Angestellte:')
print_employees(employee_query.filter_by(gender='m'))
print('Alle ab 1960 geborenen:')
print_employees(
employee_query.filter(Employee.birth_date >= Date(1960, 1, 1))
)
#
# Tabelle(n) wieder entfernen (sofern sie vorhanden sind):
#
Base.metadata.drop_all(engine)
if __name__ == '__main__':
main()
Ausgabe:
Code: Alles auswählen
Alle Datensätze (nach Nachname sortiert):
Frank Schiller (60), geb. 17.08.1955
William Shakespeare (53), geb. 25.10.1961
Jane Thunder (26), geb. 14.03.1989
Nur männliche Angestellte:
William Shakespeare (53), geb. 25.10.1961
Frank Schiller (60), geb. 17.08.1955
Alle ab 1960 geborenen:
William Shakespeare (53), geb. 25.10.1961
Jane Thunder (26), geb. 14.03.1989