2015-12-23 13 views
12

Le importazioni in un __init__.py sembrano comportarsi in modo diverso quando viene eseguito il file, quando viene importato.Le importazioni si comportano diversamente quando in __init__.py viene importata

Se abbiamo i seguenti file:

run.py:

import test 

test/b.py:

class B(object): 
    pass 

test/__init__.py:

from b import B 
print B 
print b 

Se corriamo __init__.py otteniamo un errore come mi aspetto:

% python test/__init__.py 

<class 'b.B'> 
Traceback (most recent call last): 
    File "test/__init__.py", line 6, in <module> 
    print b 
NameError: name 'b' is not defined 

Ma se run.py allora non lo fanno:

% python run.py 
<class 'test.b.B'> 
<module 'test.b' from '~/temp/test/b.py'> 

mi aspetterei che il comportamento sia lo stesso. Perché funziona?

Questo funziona solo se lo facciamo in un __init__.py. Se noi:

mv __init__.py a.py 
touch __init__.py 

e fare run.py:

import test.a 

Poi facciamo l'errore.

+1

scuse per la risposta sbagliata ... appena rimosso per non creare confusione. @ Poke grazie per indicarlo ... non ho notato inizialmente quello che hai citato. –

risposta

5

La situazione è la seguente: si dispone di uno script (run.py), un pacchetto test e il suo sottomodulo test.b.

Ogni volta che si importa un sottomodulo in Python, il nome del sottomodulo viene automaticamente memorizzato nel pacchetto genitore. In questo modo, quando si esegue import collections.abc (o from collections.abc import Iterable o simile), il pacchetto collections ottiene automaticamente l'attributo abc.

Questo è ciò che sta accadendo qui. Quando si esegue:

from b import B 

il nome b viene automaticamente caricata test, perché b è un modulo del pacchetto test.

Anche se non si fa esplicitamente , ogni volta che si importa quel modulo, il nome viene inserito in test. Perché b è un sottomodulo di test e appartiene a test.


nodo laterale: il codice non funziona con Python 3, perché relative imports have been removed.Per rendere il vostro lavoro con il codice Python 3, si dovrebbe scrivere:

from test.b import B 

Questa sintassi è perfettamente identica a from b import B, ma è molto più esplicito, e dovrebbe aiutare a capire cosa sta succedendo.


Riferimento: il Python reference documentation spiega anche questo comportamento, e include un esempio disponibile, molto simile a questa situazione (la differenza è solo che un'importazione assoluto viene utilizzato, al posto di un import relativa).

Quando un modulo viene caricato utilizzando qualsiasi meccanismo (per esempio importlib API, i import o import-from dichiarazioni, o incorporato __import__()) un legante è collocato nel namespace del modulo padre all'oggetto modulo. Ad esempio, se il pacchetto spam ha un sottomodulo foo, dopo l'importazione di spam.foo, spam avrà un attributo foo associato al sottomodulo.

Diciamo che si ha la seguente struttura di directory:

spam/ 
    __init__.py 
    foo.py 
    bar.py 

e spam/__init__.py ha le seguenti linee in esso:

from .foo import Foo 
from .bar import Bar 

quindi eseguire il seguente mette un nome vincolante per foo e bar nel spam modulo:

>>> import spam 
>>> spam.foo 
<module 'spam.foo' from '/tmp/imports/spam/foo.py'> 
>>> spam.bar 
<module 'spam.bar' from '/tmp/imports/spam/bar.py'> 

Date le regole di binding del nome familiare di Python, questo potrebbe sembrare sorprendente, ma in realtà è una caratteristica fondamentale del sistema di importazione. Il mantenimento invariante è che se hai sys.modules['spam'] e sys.modules['spam.foo'] (come faresti dopo l'importazione di cui sopra), quest'ultimo deve apparire come l'attributo foo del primo.

+0

Grazie per la risposta. Vedo che succede, ma non capisco perché. L'ho trovato in qualche codice prodotto funzionante e volevo capire perché funziona in questo modo prima di liberarmene. – DeTeReR

+0

@DeTeReR: cosa esattamente non capisci? –

+0

Ha senso che il modulo 'test.b' sia lì (proprio come' os.path' c'è quando si importa 'os' dato che il pacchetto' os' importa anche da 'os.path'). Questo spiegherebbe perché potresti fare riferimento a 'test.b.B' da' run.py'. Ma non è una spiegazione del perché il modulo '__init__' del pacchetto' test' abbia improvvisamente 'b' * come nome * definito. Non c'è nulla in "from b import B" che dovrebbe rendere disponibile localmente il nome "b". – poke

Problemi correlati