Name verliert Gültigkeit

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
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Hallo,

vor einiger Zeit hatte ich einmal nach dynamischen imports gefragt, bzw. wie auch immer der Fachausdruck heißen mag. Die von mir verwendete Lösung war dann "exec foo in bar" welche ich hier fand: http://www.python-forum.de/viewtopic.ph ... 56#p189656

Nun das Problem ist jetzt, nachdem ich auf Python 2.7.2 umgestiegen bin (von 2.7), dass Namen die ich in diesem Fall auf Modulebene definiere, auf Funktionsebene nicht "immer" gültig sind. Das heißt, es kann vorkommen dass ein Name ganz plötzlich "None" ist.

Kleines Beispiel: (..das nur das Verhalten darstellt. Funktionieren tut es nämlich..)

Code: Alles auswählen

from types import ModuleType

code = """import os
def print_os():
    print os"""

module = ModuleType('exectest')
exec code in module.__dict__
module.print_os()

>>> None
Die Sache ist allerdings die, dass ich den Grund dafür nicht lokalisieren kann. Ich habe versucht anhand dieses Artikels darauf zu kommen, war aber nicht erfolgreich. Es hat wohl etwas mit der Art und Weise zu tun, wie "exec" Namensräume behandelt. Aber diese Vermutung reicht mir nicht, ich möchte wissen/verstehen warum es so ist wie es ist... Kann mir da jemand helfen, es etwas direkter formulieren?

Außerdem gefällt mir das nicht so recht, als "Lösung" die Importe auf Funktionsebene durchzuführen.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Nich der Name ist `None` sondern der Rückgabewert der Funktion `print_os`.

Zeige beim nächsten Problem auch Code, welcher ausführbar ist und den Fehler produziert. Code zu schreiben, welcher in etwa so aussieht wie das Problem aber keinen Fehler hat ist relativ unnütz. Das Problem liegt dann typischerweise in dem Code, den du ausgelassen hast ;-)

Sebastian
Das Leben ist wie ein Tennisball.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Gremlin hat geschrieben:Außerdem gefällt mir das nicht so recht, als "Lösung" die Importe auf Funktionsebene durchzuführen.
Muss aber nicht grundsätzlich schlecht sein. In Fällen, wo beispielsweise ein Fremdmodul nur im Sinne einer Erweiterung gebraucht wird, d.h. wo das Programm immer noch ohne die Abhängigkeit laufen kann, da möchte man manchmal den Import erst bei tatsächlichem Bedarf (resp. Funktionsaufruf) machen. Die Alternative wäre natürlich, schon zu Beginn bei den Imports zu prüfen, ob das benötigte Modul vorhanden ist (häufig: `except ImportError: modulname = None`).

Bottle und Flask sind mit ihren Extensions übrigens gute Beispiele, wie man optionale Abhängigkeiten integrieren kann.
BlackJack

@EyDu: Deine Erklärung greift nicht, weil der Rückgabewert der Funktion nirgends ausgegeben wird. Es wird aber beim Aufruf der Wert von `os` ausgegeben, und der sollte ja ausgegeben werden und eine Ausgabe produzieren die nicht `None` ist.

Wäre das wirklich eine interaktive Sitzung in einer Python-Shell würde auch dort der Rückgabewert vom Funktionsaufruf nicht ausgegeben, denn Python-Shells unterdrücken `None` bei der Ausgabe.
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Richtig BlackJack. Das war nur ein "schnell schnell" Minimalbeispiel, um eben grob das Verhalten darzustellen. Hab in diesem Moment nicht darauf geachtet, wie das Resultat tatsächlich aussehen müsste.

Ich würde ja gerne ein funktionierendes Minimalbeispiel anbieten, aber ich kann nicht einmal den Fehler reproduzieren. Natürlich tritt er in meinem Programm auf, reproduzierbar, aber das nun zu posten wäre nicht mehr "minimal".

Hier hätte ich mal etwas detaillierteres, was "in etwa" den Ablauf im Programm widerspiegelt: (Natürlich wird auch hier wieder nicht "None" ausgegeben, leider.)

Code: Alles auswählen

# -*- coding: utf-8 -*-

from threading import Thread
from types import ModuleType
from time import sleep

# os wird in dieser Ebene nicht benötigt, jedoch importiere ich es in meinem
# Programm auf dieser Ebene. Also dachte ich das könnte notwendig sein, für
# die Reproduktion.
import os

# Auch hier nicht benötigte Argumente, aber die "Problem"-Funktion erhält
# nunmal welche.
code = """from __future__ import unicode_literals
import os

def print_os(a='a', b='b'):
    print repr(os)"""

module = ModuleType('exec_test')
exec code in module.__dict__

# Weiß nicht obs nötig ist, aber im Programm vergeht nunmal etwas Zeit bis
# zum Aufruf des neuen Moduls.
i = 0
while i < 1:
    i += .1
    sleep(0.1)

# Das Modul wird auf Mainthread-Ebene initialisiert, aber die Funktion in einem
# anderen aufgerufen.
t = Thread(target=module.print_os)
t.start()
BlackJack

@Gremlin: Module werden an `None` gebunden während der Interpreter „herunter fährt”. Kann es sein, dass das Programm gerade dabei ist sich zu beenden und während der Zeit Code in Threads noch versuchen auf Module/Werte zuzugreifen? Wartet der Hauptthread bis alle Threads mit ihrer Arbeit fertig sind, oder beendet sich das Programm einfach?
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Das hatte ich auch schon gelesen, aber ausgeschlossen, denn das Programm, also der Interpreter, beendet sich zu diesem Zeitpunkt nicht.

Der Vollständigkeit halber: Der Hauptthread bricht alle anderen Threads ab, bevor er sich selbst beendet.
BlackJack

@Gremlin: Wie werden die abgrebrochen? Hat das auch garantiert jeder mitbekommen und wartest Du auch wirklich bis alle fertig sind?
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Wie gesagt, wenn das Problem auftritt beendet sich der Interpreter nicht. Er läuft weiter. :wink:

Wenn ich Threads "abbreche" dann sag ich ihnen das mit "thread.abort()" und sie selbst entscheiden dann mit "if self.should_abort()" (indem ich die Instanz an das "target" übergebe) ob sie die Ausführung an bestimmten Stellen stoppen bzw. beschleunigen sollen oder nicht.
Antworten