2013-08-06 15 views
21

Sto scrivendo una famiglia di script Python all'interno di un progetto; ogni script è all'interno di una sottodirectory del progetto, in questo modo:Python: condivisione di codice comune tra una famiglia di script

projectroot 
    | 
    |- subproject1 
    | | 
    | |- script1.main.py 
    | `- script1.merger.py 
    | 
    |- subproject2 
    | | 
    | |- script2.main.py 
    | |- script2.matcher.py 
    | `- script2.merger.py 
    | 
    `- subproject3 
     | 
     |- script3.main.py 
     |- script3.converter.py 
     |- script3.matcher.py 
     `- script3.merger.py 

Ora molti degli script condividono un certo codice. Il codice condiviso è considerato come parte del progetto stesso, e non qualcosa che vorrei compilare separatamente e creare una libreria, o inserire un PYTHONPATH in tutto il sito. Potrei inserire quel codice in vari punti, come nella stessa directory projectroot o in una directory secondaria di projectroot chiamata common (forse).

Tuttavia, la maggior parte dei modi che ho pensato finora implicano la realizzazione di pacchetti di fuori delle mie sottoprogetti con vuoto __init__.py file o usare le importazioni relative (o ridondante scherzi con sys.path in ogni sottoprogetto. Peggio ancora, sembra che la costruzione di una struttura del pacchetto intorno a questa famiglia di script viene eseguito in conflitto con il seguente avviso dal respinto PEP-3122:

Attention! This PEP has been rejected. Guido views running scripts within a package as an anti-pattern.

Se gli script all'interno di un pacchetto è anti-patternish, come posso impostare le cose in un modo che mantiene il codice comune nella stessa progetto? O è accettabile un modulo e un sistema basato su pacchetti? Qual è l'approccio più pulito? (FWIW preferirei t o avere un file come shared.py o common.py nella directory radice del progetto, piuttosto che fare una directory di utilità che è un fratello per i sottoprogetti "reali".)

+1

credo che django usi un punto di accesso centralizzato 'manage.py' per eseguire tutti i suoi script. Fare qualcosa del genere potrebbe consentire di trasformare il 'subprojectX' in pacchetti e gestire l'importazione centralizzata all'interno dello script" 'manage.py'" (entry point). Come pacchetti, credo, sarà facile supportare un modulo 'comune' in cui la vostra funzionalità condivisa potrebbe vivere. – dm03514

+1

Credo che dovrebbe essere [PEP-3122] (https://www.python.org/dev/peps/pep-3122/), non PEP-32122. – user1071847

risposta

19

Suggerisco di inserire degli script "di avvio" banali al livello più alto del progetto e di trasformare ciascuna delle cartelle del sottoprogetto in pacchetti. I moduli nei pacchetti possono importare l'un l'altro o il codice comune può essere scomposto in un pacchetto common.

Ecco cosa la struttura sarà simile, se assumiamo i vari merger moduli possono essere riscritta in una versione condivisa:

projectroot 
    |- script1.py # launcher scripts, see below for example code 
    |- script2.py 
    |- script3.py 
    | 
    |- common 
    | |- __init__.py 
    | |- merger.py # from other packages, use from ..common import merger to get this 
    | 
    |- subproject1 
    | |- __init__.py # this can be empty 
    | |- script1_main.py 
    | 
    |- subproject2 
    | |- __init__.py 
    | |- script2_main.py 
    | |- script2_matcher.py 
    | 
    |- subproject3 
     |- __init__.py 
     |- script3_main.py 
     |- script3_converter.py 
     |- script3_matcher.py 

Gli script di avvio può essere molto semplice:

from subproject1 import script1_main 

if __name__ == "__main__": 
    script1_main.main() 

Quello è, tutto ciò che fa è importare il modulo appropriato "scriptN_main" ed eseguire una funzione al suo interno. L'utilizzo di uno script semplice può anche avere alcuni piccoli vantaggi per la velocità di avvio dello script, dal momento che il modulo main può avere il suo codice bytecode compilato nella cache in un file .pyc, mentre gli script non vengono mai memorizzati nella cache.

Nota: ho rinominato i moduli, scambiando i caratteri _ per i caratteri .. Non è possibile avere un . in un identificatore (come un nome di modulo), poiché Python si aspetta che indichi l'accesso agli attributi. Ciò significava che quei moduli non potevano mai essere importati. (Immagino che questo sia un artefatto solo dei file di esempio, non qualcosa che hai nel tuo codice reale.)

+0

Sembra buono, ma cosa posso dire all'interno di 'subproject1.script1_main.py' per accedere a' common'? Ho provato 'import common' ma ho ricevuto' File "movie/main.py", riga 1, in import Common ImportError: nessun modulo chiamato common' Non voglio impostare manualmente 'sys.path'. Mi sto perdendo qualcosa? –

+0

Penso che "import ..common" dovrebbe funzionare (un'importazione relativa esplicita). Assicurati di eseguire gli script al livello più alto anziché eseguire direttamente i file del sottoprogetto, altrimenti potrebbe non accorgersi che si trova in un pacchetto (riceverai un errore riguardo alla parte '..' dell'importazione in quel Astuccio). – Blckknght

+1

1. La directory "comune" ha bisogno di un file '__init __. Py' all'interno. 2. Il comando 'import ..common', mentre' from .. import common' è corretto. Richiede che projectroot contenga anche '__init __. Py' ed è anche importato come il pacchetto genitore. 3. Se non si importa projectroot ma si esegue uno script al suo interno, si può facilmente "import common", perché "." la directory dello script viene automaticamente aggiunta al percorso python all'avvio. – hynekcer

0

La mia preferenza sarebbe un "bidone" separato o "script" directory, con sottoprogetti come biblioteche/pacchetti:

projectroot 
    | 
    |- scripts 
    | 
    |- lib 
    | | 
    | `- matcher.py 
    | `- merger.py 
    | `- subproject1 
    | `- subproject2 
    | `- subproject3 

L'idea di essere i vostri scritti possono ogni riferimento a eventuali sottoprogetti necessarie come pacchetti usuali. E i sottoprogetti possono anche fare riferimento l'un l'altro con le importazioni.

È inoltre possibile disporre di uno script principale o condiviso che imposta automaticamente i pacchetti del sottoprogetto, se questo è utile.

+0

Mi piace quella divisione ma ho modificato la mia risposta per mostrare il motivo per cui ogni script è stato inserito in un sottoprogetto separato, poiché ogni "script" ha più parti. Voglio avere un codice di libreria condivisa - funzioni (e classi) utilizzabili da ciascuno degli script tra i diversi sottoprogetti. –

+0

Penso che il codice condiviso dovrebbe andare nella radice di libs. Si prega di consultare la mia modifica. –

+1

Matt - Come importerebbe qualcosa da 'lib' negli script sotto' scripts'? Non saresti in grado di utilizzare le importazioni relative, dal momento che non sono tecnicamente i moduli. –

0

Please use setuptools per distribuire entrambi gli script e biblioteche:

esempio

from setuptools import setup 

setup(
    # other arguments here... (e.g. packages/package_dir) 
    entry_points = { 
     'console_scripts': [ 
      'script1 = subproject1.script1:main', 
      'script2 = subproject2.script2:main', 
     ], 
    } 
) 

Se è possibile scrivere tutto quello che codice come librerie, e non hanno bisogno moduli separati per avere i punti di ingresso allora questo è lo strumento per voi. Se hai degli script, va bene, ma avrai bisogno di una funzione main che puoi fare riferimento (vedi esempio sopra)

Problemi correlati