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


von Max A. (checkeranonym)


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)


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)


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)


Lesenswert?

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

von Chris M. (chris_appment)


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.

1
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)


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)


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)


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)


Lesenswert?

Max A. schrieb:
> command=fts.moveforward)

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

von Eric B. (beric)


Lesenswert?

Max A. schrieb:
> hier der relevante Quellcode:

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

gui.py
1
from tkinter import *
2
import functions as fts
3
4
window = Tk()
5
window.title("Steuerung")
6
window.geometry("640x480")
7
8
button_mv = Button(window, text="Move", bg="white", fg="black", command=fts.move_fwd)
9
button_mv.pack()
10
11
window.mainloop()

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

von Max A. (checkeranonym)


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)


Lesenswert?

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

von Max A. (checkeranonym)


Lesenswert?

Die Dateien sind alle im selben Ordner.

von Karl (Gast)


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)


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:
1
import Functions as fts

Beispiel in gui.py
1
import config
2
3
config.fts.moveforward() #Funktionsaufruf ueber config

von Karl (Gast)


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)


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)


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!
1
def moveforward(MySpin):
2
        textfile = open("Position_Motor_1.txt","r")
3
        pos1= textfile.read()
4
        pos1 = int(pos1)
5
        print('Motor 1 bewegt sich',MySpin.get(), 'mm')
6
        mm=int(MySpin.get())
7
        pos= check(pos1, mm,pinsM1)
8
        Labelp1.configure(text=pos)
9
        textfile = open('Position_Motor_1.txt','w')
10
        pos = str(pos)
11
        textfile.write(pos)
12
        textfile.close()
13
        pos = int(pos)

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

von Bernd K. (prof7bit)


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)


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)


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)


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:

1
def moveforward_curry(MySpin):
2
    def moveforward():
3
        textfile = open("Position_Motor_1.txt","r")
4
        pos1= textfile.read()
5
        pos1 = int(pos1)
6
        print('Motor 1 bewegt sich',MySpin.get(), 'mm')
7
        mm=int(MySpin.get())
8
        pos= check(pos1, mm,pinsM1)
9
        Labelp1.configure(text=pos)
10
        textfile = open('Position_Motor_1.txt','w')
11
        pos = str(pos)
12
        textfile.write(pos)
13
        textfile.close()
14
        pos = int(pos)
15
    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)


Lesenswert?

geht übrigens auch so:
1
from functools import partial
2
3
...
4
5
Button1 = Button(window, text="Move", bg="white", fg="black", 
6
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)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.