Verwendung von ausgelagerten Funktion und Instanziierungen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
__LC__
User
Beiträge: 32
Registriert: Dienstag 1. März 2011, 14:08
Wohnort: 127.0.0.1
Kontaktdaten:

Hallo liebe Python-Gemeinde,

seit kurzer Zeit beschäftige ich mich nun mit Python und bin auch schon recht gut vorangeschritten. Nun allerdings hänge ich gerade an einem Problem, bei dem ich nicht weiß wie ich es angehen soll bzw. lösen kann. Und zwar habe ich in einem eigenen Skript Funktionen deklariert, auf die ich in einem anderen Skript später gerne zurückgreifen will. Die Einbindung des Funktionsskriptes mache ich wie folgt:

Code: Alles auswählen

import name_des_funktionsskriptes
Nun habe ich allerdings innerhalb einer in diesem Skript ausgelagerten Funktion auch Objekt-Instanziierungen aus einer Klasse implementiert. Auf das Objekt und seine Methoden möchte ich nun allerdings auch im einbindenen Skript möglichst einfach zurückgreifen können. Das scheiterte allerdings bisher immer mit einem NameError. Gibt es Möglichkeiten die Objekte und seine Methoden global zugänglich zu machen oder muss ich den Prozess der Instanziierung immer im einbindenden Skript selbst machen? Würde mich über ein paar Anregungen freuen.

Schöne Grüße
LC
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

In Python gilt: Alles ist ein Objekt. Auf dem Level macht absolut keinen Unterschied wie etwas erzeugt wurde.

Zeig den Code an dem es haengt, sonst kann man dir da schlecht helfen.
Benutzeravatar
__LC__
User
Beiträge: 32
Registriert: Dienstag 1. März 2011, 14:08
Wohnort: 127.0.0.1
Kontaktdaten:

Hallo cofi,

aber gerne doch. Ich spare mir das mal in aller Ausführlichkeit und kopiere nur die wichtigsten Teile heraus. Falls doch was fehlen sollte, einfach Bescheid geben.

Zunächst functions.py:

Code: Alles auswählen

class Events:
        def __init__(self, name):
                self.name = name
        def __call__(self, x = None):
                if (x == None):
                        print "Aufruf ohne Parameteruebergabe"
                else:
                        print "Aufruf mit Parameteruebergabe "

def connect()
        ...
        for event in events:
                exec event + " = Events("+ `event` +")"
        ...
Jetzt main.py

Code: Alles auswählen

import functions
connect()
...
# und hier möchte ich nun gerne den Objektaufruf machen
huepfe_hoch()
...
Das klappt nur dann, wenn ich die Instanziierung mit in main.py einbinde:

Code: Alles auswählen

import functions.py
events = connect()
for event in events:
        exec event + " = Events("+ `event` +")"

...
huepfe_hoch()
...
Schöne Grüße
LC
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Woher kommt das `huepfe_hoch`? Und was tut es? Aber ich glaube ich erahne was das Problem ist: Die Objekte werden nicht in den globalen Namensraum des aktuellen Moduls kopiert.
Du solltest dir auch abgewoehnen, dass Funktionen auf globale Namen zugreifen, und ihnen stattdessen die Objekte als Argument uebergeben.

Noch ein paar Anmerkungen:
1. Backticks um `repr` auf ein Objekt aufzurufen sind deprecated.
2. Der 2. `import`-Aufruf ist falsch, das ".py" gehoert da nicht hin
Benutzeravatar
__LC__
User
Beiträge: 32
Registriert: Dienstag 1. März 2011, 14:08
Wohnort: 127.0.0.1
Kontaktdaten:

cofi hat geschrieben:Woher kommt das `huepfe_hoch`?
Das 'huepfe_hoch' ist die Instanz, die eigentlich in der Funktion 'connect()' erzeugt werden soll. Aber dann habe ich keinen Zugriff auf diese Instanz.
Aber ich glaube ich erahne was das Problem ist: Die Objekte werden nicht in den globalen Namensraum des aktuellen Moduls kopiert.
Ok soweit so gut. Aber welche Konsequenz ergibt sich aus dieser Schlußfolgerung für mich? Oder anders gefragt, wie bekomme ich die dahin?
Du solltest dir auch abgewoehnen, dass Funktionen auf globale Namen zugreifen, und ihnen stattdessen die Objekte als Argument uebergeben.
Ich stehe gerade auf der Leitung. Du meinst was?

1. Backticks um `repr` auf ein Objekt aufzurufen sind deprecated.
Wie wäre es denn besser? Ohne funktioniert es scheinbar nicht.

Vielen Dank erst einmal für deine Hilfestellungen.

Schöne Grüße
LC
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Zeig mal den kompletten Code von `connect`, daran laesst sich das evtl besser erklaeren.

Code: Alles auswählen

`foo` == repr(foo)
Uh. Also das `exec` solltest du mal ganz schnell loswerden und durch etwas sauberes ersetzen. Das sieht fuer mich da reichlich unnoetig aus.
Benutzeravatar
__LC__
User
Beiträge: 32
Registriert: Dienstag 1. März 2011, 14:08
Wohnort: 127.0.0.1
Kontaktdaten:

Ok, noch einmal ein gesamtes Beispiel, welches mein Problem beschreibt.

Zuerst das aufrufende Skript dobject.py:

Code: Alles auswählen

#!/usr/bin/python

# Importiere die Funktionen
from dobject_functions import *

# Rufe als erstes Schritt die Init-Funktion aus dobject_functions.py auf
init()

# Rufe das in init() erzeugte Objekte huepf_hoch() auf -> Das Ergebnis sollte die Ausgabe "Aufruf mit Parameter" sein, was aber nicht funktioniert.
huepf_hoch(1)

# Zum Schluss die close-Funktion aus dobject_functions.py
close()
Die dobject_functions.py sieht nun wie folgt aus:

Code: Alles auswählen

#!/usr/bin/python

# Die Klasse dobject
class dobject:
	def __init__(self, name):
		self.name = name
	def __call__(self, x = None):
		if x == None:
			print "Aufruf ohne Parameter"
		else:
			print "Aufruf mit Parameter"

# Funktion zur initialisierung, hier werden aus den Werten der Liste obj_names gleichnamige Objekte der Klasse dobject erzeugt
def init():
	obj_names = ["huepf_hoch", "huepf_runter", "irgendwas_beliebiges"]
	for obj_name in obj_names:
		exec obj_name + " = dobject(" + repr(obj_name) + ")"
	print " ... initialised ..."
	return 1

# dummy Funktion
def close():
	print " ... closed ..."
	return 1
Vielen Dank für eure Unterstützung.

Schöne Grüße
LC
BlackJack

@[LC]: ``from modulname import *`` importiert das Modul und bindet die Objekte, die nach dem Importieren in dem Modul existieren, auch im importierenden Modul an die gleichen Namen. Deine Exemplare existieren zu dem Zeitpunkt halt noch nicht.

Aber sowohl Dein Wunsch, dass das so funktioniert, als auch die Verwendung von ``exec`` halte ich für ziemlich "unpythonisch". Das ``exec`` würde ich auf jeden Fall loswerden. Quelltext zur Laufzeit zusammen zu bauen ist unsauber. Genau so wie das zusammenführen wollen von Namensräumen auf diese Weise. Sternchen-Importe sollte man an sich schon vermeiden, aber dann auch noch wollen, dass so ein Import auch auf zukünftig gebundene Namen in einem Objekt Auswirkungen auf ein anderes Objekt haben -- dann kann man sich Module ja gleich komplett sparen, wenn man die "Trennwände" wieder einreisst, für die Module eigentlich da sind.

Zusammen mit den komischen ``return 1`` am Ende der Funktionen habe ich den Eindruck Du möchtest nicht in Python programmieren, sondern versuchst die Semantik einer anderen Programmiersprache irgendwie in Python-Syntax quetschen zu wollen.
Benutzeravatar
__LC__
User
Beiträge: 32
Registriert: Dienstag 1. März 2011, 14:08
Wohnort: 127.0.0.1
Kontaktdaten:

BlackJack hat geschrieben:@[LC]: ``from modulname import *`` importiert das Modul und bindet die Objekte, die nach dem Importieren in dem Modul existieren, auch im importierenden Modul an die gleichen Namen. Deine Exemplare existieren zu dem Zeitpunkt halt noch nicht.
Ok, das ist eine interessante Aussage.
Aber sowohl Dein Wunsch, dass das so funktioniert, als auch die Verwendung von ``exec`` halte ich für ziemlich "unpythonisch". Das ``exec`` würde ich auf jeden Fall loswerden. Quelltext zur Laufzeit zusammen zu bauen ist unsauber.
Das ist aber gerade die Idee dahinter, weil ich zur Laufzeit des Skriptes erst die wesentlichen Dinge zusammensetzen kann. Genau genommen werden die obj_namen, anders als in dem Beispiel mittels TCP/IP abgefragt. Erst wenn ich die habe, kann ich die Objekte daraus erzeugen. Jedes Objekt für sich stellt dann augenscheinlich nichts anderes als ein Befehl dar, der bei Aufruf mit seinem Argument über das Netzwerk in eine andere Applikation getunnelt wird. Was ich von Python nutzen möchte ist, dass ein Nutzer sich mit diesen (dynamischen) Befehlen und mittels der einfach Python-Syntax für Schleifen und Kontrollstrukturen einfache Skripte schreiben kann, die einen Ausführungsfluss in der anderen Applikation widerspiegeln. Wie die Kommunikation zwischen Python und der anderen Applikation aussieht braucht er gar nicht zu wissen, denn das soll alles in der dobject_function.py umgesetzt sein. Ich könnte auch einen anderen Weg beschreiten und jeden Befehl als Funktion hardcodiert umsetzen. Inhaltlich wären alle Funktionen identischen, nur eben mit einen anderen Namen versehen.
Zusammen mit den komischen ``return 1`` am Ende der Funktionen habe ich den Eindruck Du möchtest nicht in Python programmieren, sondern versuchst die Semantik einer anderen Programmiersprache irgendwie in Python-Syntax quetschen zu wollen.
Ja dein Eindruck geht in die richtige Richtung. Ich möchte weniger in Python explizit programmieren, vielmehr möchte ich mit Python scripten.

Schöne Grüße
LC
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Anweisungen zusammen zu basteln, welche auch noch über Netzwerk verschickt werden und dann per ``exec`` ausgeführt werden, das ist quasi genau das Musterbeispiel für alle Dinge, welche man im Zusammenhang mit ``exec`` falsch machen kann. Genau in so einem Fall sollten die Daten selber geparst und verarbeitet werden. Mittels des ast-Moduls ist das sogar nicht einmal schwer.
[LC] hat geschrieben:Ja dein Eindruck geht in die richtige Richtung. Ich möchte weniger in Python explizit programmieren, vielmehr möchte ich mit Python scripten.
Da gibt es keinen Unterschied. Was nützt dir irgend ein unsauberes und unwartbares Stück Code, welches eine Sprache nicht richtig verwendet und voller "Unarten" anderer Sprachen steckt? Dann kannst du Python auch ganz sein lassen und die entsprechende Sprache nutzen.

Sebastian
Das Leben ist wie ein Tennisball.
Benutzeravatar
__LC__
User
Beiträge: 32
Registriert: Dienstag 1. März 2011, 14:08
Wohnort: 127.0.0.1
Kontaktdaten:

EyDu hat geschrieben:Hallo.

Genau in so einem Fall sollten die Daten selber geparst und verarbeitet werden.
Ich habe nur die Idee beschrieben, nicht wie es letztlich aussieht. Und ja, stimme dir vollkommen zu, was auch explizit und sogar 2x passiert. Exec brauche ich eigentlich nur, um aus einer Liste von Namen zur Laufzeit Synonyme zu generieren, die bei ihrem Aufruf immer dieselbe Routine enthalten. Mehr eigentlich nicht. Dabei verwende ich absolut keinerlei Semantik von einer anderen Programmiersprache. Ich habe mein eigenes Datenprotokoll, packe das was ich dann brauche dahingehend explizit zusammen. Ich will nur einen Möglichkeit schaffen, dem Nutzer eine einfach Semantik zur Verfügung zu stellen.
Da gibt es keinen Unterschied. Was nützt dir irgend ein unsauberes und unwartbares Stück Code, welches eine Sprache nicht richtig verwendet und voller "Unarten" anderer Sprachen steckt? Dann kannst du Python auch ganz sein lassen und die entsprechende Sprache nutzen.
Schon richtig Sebastian.

Schöne Grüße
LC
Zuletzt geändert von __LC__ am Dienstag 1. März 2011, 19:57, insgesamt 1-mal geändert.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Schau dir Dictionaries an. Damit wirst du sowohl `exec` als auch das Problem mit dem Objekten los.
Benutzeravatar
__LC__
User
Beiträge: 32
Registriert: Dienstag 1. März 2011, 14:08
Wohnort: 127.0.0.1
Kontaktdaten:

cofi hat geschrieben:Schau dir Dictionaries an. Damit wirst du sowohl `exec` als auch das Problem mit dem Objekten los.
Wenn ich das richtig überblicke, verliere ich dann aber die Möglichkeit ein Argument zu übergeben, oder?
BlackJack

@[LC]: Kannst Du das nicht als Klasse umsetzen die dynamisch gesetzte Attribute besitzt? Mal so ein Minimalbeispiel:

Code: Alles auswählen

from functools import partial


class RemoteCaller(object):
    def __init__(self):
        method_names = ['huepf_hoch', 'huepf_runter', 'irgendwas_beliebiges']
        for method_name in method_names:
            setattr(self, method_name, partial(self._remote_call, method_name))
    
    def _remote_call(self, method_name, argument=None):
        print ('Aufruf von %s %s Parameter'
                % (method_name, 'ohne' if argument is None else 'mit'))


def main():
    caller = RemoteCaller()
    caller.huepf_hoch()
    caller.huepf_runter(42)


if __name__ == '__main__':
    main()
Das `RemoteCaller`-Objekt würde dann auch die Verbindung zum Server kapseln. Sonst würde `_remote_call()` als Methode keinen Sinn machen.

Objekte dynamisch zusammen zu setzen, erfordert bei einer dynamischen Sprache eben gerade nicht, dass man Quelltext zur Laufzeit generieren muss.
Antworten