2013-04-24 6 views
16

Meine Frage besteht darin, den "guten Weg" zu finden, eine Funktion zu definieren, deren Implementierung sich nach einem bestimmten Kriterium unterscheidet. Diese Funktion wird von mehreren Skripten aufgerufen, daher muss ich sie in ein Modul einfügen.Bedingte Definition einer Funktion in einem Modul

Zum Beispiel betrifft mein Kriterium, auf welcher Plattform das Skript ausgeführt wird, aber klar, dass der Test alles ertragen könnte. Meine Funktionen ermöglichen persistent Definition/retrieving von Umgebungsvariablen, so habe ich ein Modul namens persistenv.py nach dem folgenden Schema hergestellt (Lösung 1):

# CROSS-PLATFORM PART 
def my_function1() : 
    # ... body1 ... 

# WINDOWS-ONLY PART 
if sys.platform.lower().startswith('win') : 
    def my_function2() : 
     # ...body2 for Windows... 

# LINUX-ONLY PART 
elif sys.platform.lower().startswith('linux') : 
    def my_function2() : 
     # ...body2 for Linux... 

else : 
    raise ImportError('Your platform is not supported') 

Die oben stellt zwei mögliche Definitionen der gleichen Funktion in einem einzigen Modul. Wie auch immer, es klang ziemlich viel sauberer mir als Testen der Plattform jedes Mal die Funktion aufgerufen wurde (Lösung 2):

# CROSS-PLATFORM PART 
def my_function1() : 
    # ... body1 ... 


def my_function2() : 

    # WINDOWS-ONLY PART 
    if sys.platform.lower().startswith('win') : 
     # ...body2 for Windows... 

    # LINUX-ONLY PART 
    elif sys.platform.lower().startswith('linux') : 
     # ...body2 for Linux... 

fand ich eine andere approach, in dem jeder plattformspezifischen Teil in ein eigenes Modul extrahiert wurde, und dann bedingt in persistenv.py importiert (Lösung 3):

# CROSS-PLATFORM PART 
def my_function1() : 
    # ... body1 ... 

# WINDOWS-ONLY PART 
if sys.platform.lower().startswith('win') : 
    from persistenv_win import my_function2 

# LINUX-ONLY PART 
elif sys.platform.lower().startswith('linux') : 
    from persistenv_linux import my_function2 

else : 
    raise ImportError('Your platform is not supported') 

Bisher erreichte ich die folgenden Schlussfolgerungen (und bekam folgende Fragen):

  • SOLUTION 2 sollte kaum verwendet werden, insbesondere, wenn Sie mehrere plattformspezifische Funktionen haben my_function2, my_function3, my_function4 ... (wie Sie den Test in jedem von ihnen wiederholen würde)

  • SOLUTION 1 erscheint ganz einfach (keine zusätzlichen Dateien, ein einzelner Test), aber ich frage mich, wie verhält sich Python (intern), wenn man "von persistenv import my_function2" aufrufen muss?

  • SOLUTION 3 scheint Pythonic (verwendet in integrierten Implementierungen, os.path zum Beispiel), aber kann es nicht problematisch sein, wenn persistenv.py, persistenv_win.py und persistenv_linux.py Teil eines gleichen sind Paket (neben anderen Modulen), die ich global importiere, indem ich "import my_package" im Hauptskript?

Antwort

10

Wenn Sie einen Blick auf die Python-Standard lib haben, sehen Sie, dass alle drei von ihnen verwendet werden:

  • subprocess nutzt Lösungen 1 und 2; Es gibt mehrere Orte, an denen der Test stattfindet.

  • os verwendet eine Variante der Lösung 3 (import ... as path).

Verwenden Sie also die, die am geeignetsten und am einfachsten scheint.

[Lösung 1]

wie funktioniert Python verhalten (intern), wenn man "from persistenv import my_function2" zu nennen braucht?

Es verhält sich genau wie gewünscht.Die Funktion, die beim Import des Moduls definiert wurde, wird exportiert.

Technisch wird das Modul genau wie mit import module importiert, und dann wird der gegebene Name in seinem Namespace nachgeschlagen und in den aktuellen Namen eingefügt.

[Lösung 3]

kann es nicht problematisch sein, wenn persistenv.py, persistenv_win.py und persistenv_linux.py Teil eines gleichen Paket (unter anderem Module) sind, die ich Import global würde, indem Sie "import my_package" im Hauptskript?

Warum sollte es problematisch sein? Sie importieren my_package in das Hauptskript, und dann führt das Hauptskript (oder ein Modul des Pakets) den genannten Import von einem plattformabhängigen Modul aus. Das ist vollkommen richtig.

+0

Danke für Ihre Anmerkungen. Ich nehme an, dass es keine "schmutzige Praxis" ist, eines der 3 Muster zu verwenden, es hängt nur von der Situation ab. Ihren letzten Punkt verstehe ich jedoch nicht vollständig: Ich muss mich irren, aber wenn Sie my_package importieren, laden Sie persistenv.py (das entweder persistenv_win.py oder persistenv_linux.py lädt) UND persistenv_win.py UND persistenv_linux.py, nicht unbedingt in dieser Reihenfolge. Ist jedes dieser 3 Module an einen eigenen Namensraum gebunden? (zB 'my_package.persistenv_win.my_function2()'? – gromk13

+0

@ gromk13 Selbst wenn sie gleichzeitig geladen würden (was nicht wahr ist), hätten sie zwar ihren eigenen Namensraum, aber das Laden findet nur bei Bedarf statt, dh nur diese Module die ich explizit geladen habe, sind tatsächlich geladen. " – glglgl

+0

Mein Fehler, ich habe fälschlicherweise angenommen, dass das Importieren eines Pakets automatisch alle seine Module importiert. Eigentlich wäre die oben beschriebene problematische Situation eher das Ergebnis von' from my_package import * ', with' __all__ = [..., 'persistenv', 'persistenv_win', 'persistenv_linux', ...] ', das wäre ein ziemlich dummer Schachzug ... – gromk13