2012-05-24 14 views
7

Próbuję określić aktualny moduł prądowy funkcji (jak widać, jeśli został zaimportowany z innych źródeł), nawet jeśli bieżącym modułem jest "top level scripting environment" __main__.Python: określ rzeczywisty moduł prądowy (nie __main__)

Może to zabrzmieć dziwnie, ale tło polega na tym, że muszę serializować funkcję i odserializować ją (w tym argumenty) na innej maszynie, dla której potrzebuję upewnić się, że właściwy moduł ORAZ NIE __main__ jest importowany przed deserializacją (w przeciwnym razie pojawia się komunikat o błędzie: AttributeError: 'module' object has no attribute my_fun).

Dotychczas próbowałem inspection:

import inspect 
print inspect.getmodule(my_fun) 

który daje mi

<module '__main__' from 'example.py'> 

oczywiście. Próbowałem również znaleźć coś użytecznego przy użyciu globals(), bez powodzenia.

Tym, czego naprawdę chcę, jest <module 'example' from 'example.py'>. Przypuszczam hacky sposób byłoby analizować je z nazwy pliku przy użyciu coś jak

m_name = __main__.__file__.split("/")[-1].replace(".pyc","") 

a następnie znaleźć moduł według nazwy sys.modules[m_name].

Czy istnieje lepszy/lepszy sposób na zrobienie tego?

EDIT: Po zapoznaniu się z „FakeModule” ipython i nieco więcej googling, doszedłem po drugiej this post, który opisuje dokładnie problem, że jestem stoi, w tym mojego obecnego rozwiązania nią (co jest wyraźnie importującego aktualny moduł import current_module i serializowanie current_module.my_fun zamiast my_fun). Próbuję tego uniknąć, ponieważ może to nie być intuicyjne dla użytkowników mojego pakietu.

+0

Czy patrzyłeś kiedyś na elementy wewnętrzne biblioteki 'nose', a konkretnie na moduł' importer'? 'nose' ma kilka rzeczy bardzo podobnych do tego, więc może być dobrym miejscem do szukania inspiracji. –

Odpowiedz

3

Rzeczywiście natknąłem się na ten sam problem.

Co kiedyś było:

return os.path.splitext(os.path.basename(__main__.__file__))[0] 

która jest faktycznie taka sama jak "hack". Szczerze mówiąc, uważam, że jest to najlepsze rozwiązanie.

+0

Dzięki Winston, to rozwiązanie, z którym współpracowałem i od tego czasu nie napotkam żadnych nieprzyjemnych problemów. – soramimo

+0

To nie działa, jeśli uruchamiasz skrypt w folderze, np. "Bar/foo.py" – jwayne

+0

@jwayne, co on zamiast tego produkuje? –

2

Jednym ze sposobów, w jaki możesz to zrobić - być może nie najlepszym sposobem, ale działa to dla mnie - jest zaimportowanie modułów za pomocą __import__ i użycie getattr w sposób podobny do poniższego.

(Tutaj używam kilka pomysłów opisanych w this post o dynamicznie loading modułów.)

def dynamic_import(name): 
    mod = __import__(name) 
    components = name.split('.') 
    for comp in components[1:]: 
     mod = getattr(mod, comp) 
    return mod 

tmodule = dynamic_import('modA') 
# Print the module name 
print tmodule 
# Use the module's contents 
t = tmodule.myObject() 
t.someMethod() 

Gdzie modA.py wygląda następująco:

class myObject(): 
    def someMethod(self): 
     print "I am module A" 

Więc widać, jesteśmy coraz nazwa modułu, który zaimportowaliśmy i nadal używamy obiektów i metod wewnątrz modułu w normalny sposób. Gdy uruchomię to uzyskać następujące:

python experiment.py 
<module 'modA' from 'modA.pyc'> 
I am module A 

Ponownie, to może być lub nie być „idealnym” sposób, ale działa dobrze iw miarę mogę powiedzieć, nie pociąga za sobą żadnych niepożądanych kompromisów w najbardziej przypadki. Mam nadzieję że to pomoże.

3

Myślę, że twoje oryginalne rozwiązanie jest najlepsze i najprostsze. Jeśli martwisz się tym, że wydaje się to nielogiczne dla twoich użytkowników, zapakuj je w ładną funkcję i udokumentuj, do czego to służy.

Po uruchomieniu modułu jako __main__, pyton naprawdę nie kojarzy go z jego normalną nazwą modułu: jeśli użytkownik import example, załaduje plik po raz drugi tak, jakby był oddzielnym modułem. Tak naprawdę dzieje się tak w twoim przypadku, inaczej nie będziesz mógł znaleźć swojego modułu po nazwie w sys.modules: Moduł example i moduł __main__ są naprawdę osobnymi obiektami środowiska wykonawczego, ponieważ dowiesz się, czy jawnie zmienisz zmienną modułu w jeden z nich.

3

Wiem, że to jest nieaktualne, ale znalazłem prostsze rozwiązanie w Pythonie3, które działało dla mnie. Krótko mówiąc, istnieje obiekt __spec__, który również przechowuje rzeczywistą nazwę modułu, zamiast być "__main__".

import inspect 
if obj.__class_.__module__ == "__main__": 
    print(inspect.getmodule(obj).__spec__.name) 
+0

Działa to tylko podczas działania jako moduł (z 'python -m'), w przeciwnym razie' __spec__' ma wartość 'Brak'. Ale to mi wystarczy! (Również w moim przypadku użycia mogę pominąć 'inspect' i po prostu użyć' import __main__; print (__ główny __.__ spec __. Name) '.) –

Powiązane problemy