Forum: PC-Programmierung Python: Problem: Funktion aus anderer Pythondatei aufrufen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Max A. (checkeranonym)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe ein Problem. Ich will eine Funktion aus einer anderen Python 
Datei aufrufen. Der Code sieht folgendermaßen aus:

import Functions as fts ##Importieren der Datei.
...

Button1 = Button(window, text="Move", bg="white", fg="black", 
command=fts.moveforward)
Button1.grid(column=3, row=1)

In der Functions.py sieht es so aus:

def moveforward():
....

Wenn ich das Programme starte, kommt folgende Fehlermeldung:

AttributeError: module 'Functions' has no attribute 'moveforward'

So wie ich die Fehlermeldung verstehe, findet er die Funktion nicht, 
obwohl diese eindeutig vorhanden ist. Woran könnte es liegen? Danke für 
eure Hilfe im Voraus.

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Die Ursache liegt möglicherweise in dem was Du uns nicht erzählst.

Poste doch einfach mal den vollständigen Code so wie er nicht 
funktioniert, gerne um alles unnötige gekürzt, soweit gekürzt bis nur 
noch das Nichtfunktionieren als solches enthalten und klar ersichtlich 
ist, poste nicht in umständlicher Prosa umschrieben was Du denkst was im 
Code steht sondern poste einfach den echten nackten Code unverfälscht.

Es könnte auch geschehen daß Du beim Kürzen aufs Wesentliche, beim 
Vorbereiten eines Minimalbeispiels den Fehler selbst findest. Deshalb 
ist das immer der erste Schritt den man tun sollte!

: Bearbeitet durch User
von Max A. (checkeranonym)


Bewertung
0 lesenswert
nicht lesenswert
hier der relevante Quellcode:

gui.py:

from tkinter import *
import Functions as fts
from config import *

window = Tk()
window.title("Steuerung")
window.geometry("640x480")

       # Steigung1= Steigung

Label1 = Label(window, text="Motor, Position:", font=("Arial", 14))
Label1.grid(column=0, row=1)
Labelp1 = Label(window, text=pos1, font=("Arial", 14))
Labelp1.grid(column=1, row=1)
Spin1 = Spinbox(window, from_=-20, to=20, width=5)
Spin1.grid(column=2,row=1)
Button1 = Button(window, text="Move", bg="white", fg="black",
command=fts.moveforward)
Button1.grid(column=3, row=1)

Functions.py:

from config import *
import gui
import sys
import os
import re

def moveforward():
        textfile = open("Position_Motor_1.txt","r")
        pos1= textfile.read()
        pos1 = int(pos1)
        print('Motor 1 bewegt sich',spin1.get(), 'mm')
        mm=int(spin1.get())
        pos= check(pos1, mm,pinsM1)
        Labelp1.configure(text=pos)
        textfile = open('Position_Motor_1.txt','w')
        pos = str(pos)
        textfile.write(pos)
        textfile.close()
        pos = int(pos)


Fehlermeldung:

AttributeError: module 'Functions' has no attribute 'moveforward'


Es gibt noch eine dritte Pythondatei: Config.py. Dort sind nur 
Variablen.

: Bearbeitet durch User
von Brezel (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Es wird lauffähiger Code zur Analyse benötigt.
In deinem Dump fehlt Config.py.

von Chris M. (chris_appment)


Bewertung
-1 lesenswert
nicht lesenswert
Max A. schrieb:
>Button1=Button(window,text="Move",bg="white",fg="black",command=fts.mov eforward)

Du rufst hier moveforward als Variable und nicht als Funktion bzw. 
Methode auf.

Button1 = Button(window, text="Move", bg="white", fg="black", command=fts.moveforward())


Probier das mal
Edit: Deine Funktion moveforward hat keinen Rückgabeparamter?!

: Bearbeitet durch User
von imonbln (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Max A. schrieb:
> Fehlermeldung:
>
> AttributeError: module 'Functions' has no attribute 'moveforward'

was gibt denn dir(Function) aus ? ist in der liste moveforward dabei.

von Brezel (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Chris M. schrieb:
> Du rufst hier moveforward als Variable und nicht als Funktion bzw.
> Methode auf.

Das ist vollkommen in Ordnung.
Der Fehler liegt in Code, der wieder nicht gezeigt wird.

von Max A. (checkeranonym)


Bewertung
0 lesenswert
nicht lesenswert
Chris M. schrieb:
> Max A. schrieb:
>>Button1=Button(window,text="Move",bg="white",fg="black",command=fts.mo v 
eforward)
>
> Du rufst hier moveforward als Variable und nicht als Funktion bzw.
> Methode auf.
>
> Button1 = Button(window, text="Move", bg="white", fg="black",
> command=fts.moveforward())
>
> Probier das mal
> Edit: Deine Funktion moveforward hat keinen Rückgabeparamter?!

Hab ich versucht. Hat leider nicht funktioniert. Nein meine Funktion hat 
keinen Rückgabewert.

Brezel schrieb:
> Es wird lauffähiger Code zur Analyse benötigt.
> In deinem Dump fehlt Config.py.


Zum Testen braucht man noch folgende Funktion auf aus der Function.py

import gui


def check(pos,mm,pin):

        P=pos+mm

        if min<=P<=max:
            print("ok")

        else:
            print("nicht ok")
            pass
        return pos

config.py:

min=0
max=30

#
pinsM1=[17, 4,27,22]
textfile = open("Position_Motor_1.txt","r")
pos1= textfile.read()
pos1 = int(pos1)
textfile.close()

Ich habe noch die entsprechende Textdatei angelegt. Dort hab ich z.B 12 
eingetragen

imonbln schrieb:
> Max A. schrieb:
>> Fehlermeldung:
>>
>> AttributeError: module 'Functions' has no attribute 'moveforward'
>
> was gibt denn dir(Function) aus ? ist in der liste moveforward dabei.

Die Funktion soll erstmal überprüfen, ob die neue Position im 
gewünschten Bereich liegt. Wenn ja, soll sich der Motor bewegen. 
Anschließend wird die neue Position in die Textdatei geschrieben. Einen 
Rückgabewert gibt es nicht. Welche Liste?

: Bearbeitet durch User
von Karl (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Max A. schrieb:
> command=fts.moveforward)

Wie wär es mit einer Klammer?
command=fts.moveforward()

von Eric B. (beric)


Bewertung
0 lesenswert
nicht lesenswert
Max A. schrieb:
> hier der relevante Quellcode:

Nein, der relevante Code würde wie folgt aussehen:

gui.py
from tkinter import *
import functions as fts

window = Tk()
window.title("Steuerung")
window.geometry("640x480")

button_mv = Button(window, text="Move", bg="white", fg="black", command=fts.move_fwd)
button_mv.pack()

window.mainloop()

functions.py
def move_fwd():
    print("Boom!")
Und das funktioniert anstandslos

von Max A. (checkeranonym)


Bewertung
0 lesenswert
nicht lesenswert
Karl schrieb:
> Max A. schrieb:
>> command=fts.moveforward)
>
> Wie wär es mit einer Klammer?
> command=fts.moveforward()

Hab ich versucht. Geht leider nicht.

Eric B. schrieb:
> Max A. schrieb:
>> hier der relevante Quellcode:
>
> Nein, der relevante Code würde wie folgt aussehen:
>
> gui.pyfrom tkinter import *
> import functions as fts
>
> window = Tk()
> window.title("Steuerung")
> window.geometry("640x480")
>
> button_mv = Button(window, text="Move", bg="white", fg="black",
> command=fts.move_fwd)
> button_mv.pack()
>
> window.mainloop()
>
> functions.pydef move_fwd():
>     print("Boom!")
> Und das funktioniert anstandslos

Hier nochmal wie meine drei Pythondateien aufgebaut sind:

gui.py:

from tkinter import *
import Functions as fts
from config import *

window = Tk()
window.title("Steuerung")
window.geometry("640x480")

       # Steigung1= Steigung

Label1 = Label(window, text="Motor, Position:", font=("Arial", 14))
Label1.grid(column=0, row=1)
Labelp1 = Label(window, text=pos1, font=("Arial", 14))
Labelp1.grid(column=1, row=1)
Spin1 = Spinbox(window, from_=-20, to=20, width=5)
Spin1.grid(column=2,row=1)
Button1 = Button(window, text="Move", bg="white", fg="black",
command=fts.moveforward)
Button1.grid(column=3, row=1)
Button1.pack()

window.mainloop()


Functions.py:

from config import *
import gui
import sys
import os
import re

def check(pos,mm,pin):

        P=pos+mm

        if min<=P<=max:
            print("ok")

        else:
            print("nicht ok")
            pass
        return pos


def moveforward():
        textfile = open("Position_Motor_1.txt","r")
        pos1= textfile.read()
        pos1 = int(pos1)
        print('Motor 1 bewegt sich',spin1.get(), 'mm')
        mm=int(spin1.get())
        pos= check(pos1, mm,pinsM1)
        Labelp1.configure(text=pos)
        textfile = open('Position_Motor_1.txt','w')
        pos = str(pos)
        textfile.write(pos)
        textfile.close()
        pos = int(pos)

config.py:

min=0
max=30


pinsM1=[17, 4,27,22]
textfile = open("Position_Motor_1.txt","r")
pos1= textfile.read()
pos1 = int(pos1)
textfile.close()


Fehlermeldung:

AttributeError: module 'Functions' has no attribute 'moveforward'



Wenn ich die Funktion in die gui.py kopiere, funktioniert es problemlos. 
Nur wenn diese Funktion in einer anderen Pythondatei ist dann 
funktioniert es nicht. Bei meinem Programm will ich die Oberfläche, 
Funktionen und Variablen getrennt haben.

von Chris M. (chris_appment)


Bewertung
0 lesenswert
nicht lesenswert
Liegen alle Dateien in einem Ordner? Oder hast du irgendeine Pythondatei 
in Unter/Überordner?

von Max A. (checkeranonym)


Bewertung
0 lesenswert
nicht lesenswert
Die Dateien sind alle im selben Ordner.

von Karl (Gast)


Bewertung
2 lesenswert
nicht lesenswert
Max A. schrieb:
>gui.py:
>
>from tkinter import *
>import Functions as fts

> Functions.py:
>
> from config import *
> import gui

Du hast eine Zirkelbezug gebaut!

von Chris M. (chris_appment)


Bewertung
0 lesenswert
nicht lesenswert
Karl schrieb:
> Max A. schrieb:
>>gui.py:
>>
>>from tkinter import *
>>import Functions as fts
>
>> Functions.py:
>>
>> from config import *
>> import gui
>
> Du hast eine Zirkelbezug gebaut!


Stimmt.

importiere alle Module in der Config und greife von deinen Dateien dann 
über die Config drauf zu. Dafür ist sie da.


Beispiel config.py:
import Functions as fts

Beispiel in gui.py
import config

config.fts.moveforward() #Funktionsaufruf ueber config

von Karl (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Chris M. schrieb:
> importiere alle Module in der Config und greife von deinen Dateien dann
> über die Config drauf zu. Dafür ist sie da.

Nein struckturiere deine Module, sodass man so einen Quatsch nicht 
braucht, oder mach gleich ordentlich objektorientiert!

von Max A. (checkeranonym)


Bewertung
0 lesenswert
nicht lesenswert
Karl schrieb:
> Max A. schrieb:
>>gui.py:
>>
>>from tkinter import *
>>import Functions as fts
>
>> Functions.py:
>>
>> from config import *
>> import gui
>
> Du hast eine Zirkelbezug gebaut!

Auf die gui muss ich nur zugreifen, da ich den Wert von der Spinbox 
brauche

von Karl (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Max A. schrieb:
> Auf die gui muss ich nur zugreifen, da ich den Wert von der Spinbox
> brauche

Dann übergibt der Funktion die Spinbox oder den Wert!
def moveforward(MySpin):
        textfile = open("Position_Motor_1.txt","r")
        pos1= textfile.read()
        pos1 = int(pos1)
        print('Motor 1 bewegt sich',MySpin.get(), 'mm')
        mm=int(MySpin.get())
        pos= check(pos1, mm,pinsM1)
        Labelp1.configure(text=pos)
        textfile = open('Position_Motor_1.txt','w')
        pos = str(pos)
        textfile.write(pos)
        textfile.close()
        pos = int(pos)

Insgesamt wirkt das alles sehr unstrukturiert! Trenne Datenverarbeitung, 
Daten und Gui richtig voneinander!

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Karl schrieb:
> Dann übergibt der Funktion die Spinbox oder den Wert!

Ganz so einfach wirds nicht gehen, Python kann erstmal kein currying, da 
braucht er noch ne Hilfsfunktion die eine neue Funktion erzeugt und 
zurückliefert.

von Karl (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Bernd K. schrieb:
> Ganz so einfach wirds nicht gehen, Python kann erstmal kein currying, da
> braucht er noch ne Hilfsfunktion die eine neue Funktion erzeugt und
> zurückliefert.

Ich verstehe dein Problem nicht. Natürlich geht das, so wie ich es 
geschrieben habe. Man kann in Python einer Funktion einfach ein Objekt 
übergeben.
Und in Python gibt es noch lambda, was man hier aber nicht braucht.

von Karl (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Abgesehen davon hat der Zugriff auf die Spinbox eigentlich nichts in der 
Funktion zu suchen, es würde reichen, wenn man den Wert übergibt. Auch 
der Dateiname hat in der Funktion nichts zu suchen, sonst schreibt man 
für jeden Motor eine Funktion.

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Karl schrieb:
> Ich verstehe dein Problem nicht. Natürlich geht das, so wie ich es
> geschrieben habe. Man kann in Python einer Funktion einfach ein Objekt
> übergeben.

Du sollst dem Button-Konstruktor aber eine Funktion übergeben und dabei 
nicht die selbige schon aufrufen. Also mußt Du die Funktion mitsamt 
ihrem Argument aber im noch nicht aufgerufenen Zustand übergeben! Also 
eine Funktion die den Parameter schon hat aber noch nicht aufgerufen 
wurde! So daß der Button sie später aufrufen kann!

Der Fachbegriff dafür ist Currying und partielle Funktionsauswertung

Google das! Und dann probier Deinen eigenen naiven Vorschlag ruhig 
einfach mal aus und schau Dir die Fehlermeldungen an!

Ich hab Deinen Code mal entsprechend geändert so daß er die 
teilangewendete Funktion zurückliefert, so könnts gehen:

def moveforward_curry(MySpin):
    def moveforward():
        textfile = open("Position_Motor_1.txt","r")
        pos1= textfile.read()
        pos1 = int(pos1)
        print('Motor 1 bewegt sich',MySpin.get(), 'mm')
        mm=int(MySpin.get())
        pos= check(pos1, mm,pinsM1)
        Labelp1.configure(text=pos)
        textfile = open('Position_Motor_1.txt','w')
        pos = str(pos)
        textfile.write(pos)
        textfile.close()
        pos = int(pos)
    return moveforward

Das liefert eine Closure zurück die den korrekten Wert für MySpin 
bereits enthält aber selbst noch nicht aufgerufen wurde. Diese kann dann 
wiederum an den Button übergeben werden so daß er sie später bei Klick 
aufrufen kann.

Und schon hast Du wieder was gelernt, man lernt nie aus! Das macht die 
Sache interessant!

: Bearbeitet durch User
von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
geht übrigens auch so:
from functools import partial

...

Button1 = Button(window, text="Move", bg="white", fg="black", 
command=partial(fts.moveforward, MySpin))

Dann muss moveforward ein Argument akzeptieren (so wie in Deinem 
Beispiel) und partial() liefert dann eine neue Funktion zurück bei der 
das erste Argument schon ausgefüllt ist und die man dann später ohne 
Argumente aufrufen kann.

von Karl (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Bernd K. schrieb:
> Du sollst dem Button-Konstruktor aber eine Funktion übergeben und dabei
> nicht die selbige schon aufrufen.

Jetzt verstehe was du von mir willst. Ich habe mir das Programm nicht 
wirklich angeschaut, dass command= eine Funktion sein soll habe ich 
nicht angenommen, ich dacht command soll ein (Rückgabe-)Wert sein.

Bernd K. schrieb:
> Und dann probier Deinen eigenen naiven Vorschlag ruhig
> einfach mal aus und schau Dir die Fehlermeldungen an!

War nicht mein Vorschlag, wie gesagt hätte ich das ganze Programm 
komplett anders strukturiert. Ich hätte wahrscheinlich irgendein Objekt 
dem man beim Initialisieren die Spinbox mitgibt und dann halt eine 
Funktion, die man einfach aufrufen kann wenn der Button gedrückt wird.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.