__Future__ import absolute_import gerçekte ne yapar?


164

Ben var cevap Ben okuma dayalı anlaşılan düşünce Python mutlak ithalat, ilgili soru Python 2.5 changelog ve beraberindeki PEP . Ancak, Python 2.5'i yükledikten ve doğru bir şekilde kullanmanın bir örneğini oluşturmaya çalıştıktan sonra from __future__ import absolute_import, şeylerin o kadar net olmadığını fark ettim.

Yukarıda bağlantılı değişiklik günlüğünden, bu ifade mutlak ithalat değişikliği hakkındaki anlayışımı doğru bir şekilde özetledi:

Diyelim ki böyle bir paket dizininiz var:

pkg/
pkg/__init__.py
pkg/main.py
pkg/string.py

Bu pkg, pkg.mainve pkg.stringalt modüllerini içeren adlı bir paketi tanımlar .

Main.py modülündeki kodu düşünün. İfadeyi yürütürse ne olur import string? Python 2.4 ve öncesinde, göreceli bir içe aktarma gerçekleştirmek için paketin dizinine bakar, pkg / string.py dosyasını bulur, bu dosyanın içeriğini pkg.stringmodül olarak içe aktarır "string"ve pkg.mainmodül modülün ad alanındaki ada bağlanır .

Bu yüzden tam dizin yapısını oluşturdum:

$ ls -R
.:
pkg/

./pkg:
__init__.py  main.py  string.py

__init__.pyve string.pyboş. main.pyaşağıdaki kodu içerir:

import string
print string.ascii_uppercase

Beklendiği gibi, bunu Python 2.5 ile çalıştırmak aşağıdakilerle başarısız olur AttributeError:

$ python2.5 pkg/main.py
Traceback (most recent call last):
  File "pkg/main.py", line 2, in <module>
    print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'

Bununla birlikte, 2.5 değişiklik günlüğünde, bunu buluyoruz (vurgu eklendi):

Python 2.5'te, yönerge importkullanarak davranışlarını mutlak içe aktarmalara geçirebilirsiniz from __future__ import absolute_import. Bu mutlak içe aktarma davranışı, gelecekteki bir sürümde varsayılan olabilir (muhtemelen Python 2.7). Mutlak içe aktarmalar varsayılan olduğunda, import stringher zaman standart kitaplığın sürümünü bulur.

Bu nedenle pkg/main2.py, main.pygelecekteki ek ithalat yönergesiyle özdeş ancak aynı şekilde oluşturdum . Şimdi şöyle görünüyor:

from __future__ import absolute_import
import string
print string.ascii_uppercase

Python 2.5 ile bu Koşu Ancak ... Bir başarısız olur AttributeError:

$ python2.5 pkg/main2.py
Traceback (most recent call last):
  File "pkg/main2.py", line 3, in <module>
    print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'

Bu , mutlak ithalatın etkin olduğu import stringher zaman std-lib sürümünü bulan ifadeyle oldukça çelişir . Dahası, mutlak ithalatın "yeni varsayılan" davranış olarak planlandığı uyarısına rağmen, aynı sorunu hem Python 2.7'yi kullanarak, hem de __future__direktifle veya direktifsiz olarak vurdum :

$ python2.7 pkg/main.py
Traceback (most recent call last):
  File "pkg/main.py", line 2, in <module>
    print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'

$ python2.7 pkg/main2.py
Traceback (most recent call last):
  File "pkg/main2.py", line 3, in <module>
    print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'

Python 3.5 ile birlikte veya birlikte ( printifadenin her iki dosyada da değiştiği varsayılarak ):

$ python3.5 pkg/main.py
Traceback (most recent call last):
  File "pkg/main.py", line 2, in <module>
    print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'

$ python3.5 pkg/main2.py
Traceback (most recent call last):
  File "pkg/main2.py", line 3, in <module>
    print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'

Bunun diğer varyasyonlarını test ettim. Yerine string.pyadlı bir dizin -, boş bir modül oluşturduk stringsadece boş içeren __init__.py- ve bunun yerine ithalatı verilmesine main.py, ben cd'için d pkgve repl doğrudan ithalat çalıştırın. Bu varyasyonların hiçbiri (ya da bunların bir kombinasyonu) yukarıdaki sonuçları değiştirmedi. Bunu __future__direktif ve mutlak ithalatlar hakkında okuduğum şeyle uzlaştıramıyorum .

Bana öyle geliyor ki , aşağıdakiler tarafından kolayca açıklanabilir (bu Python 2 belgelerinden gelir, ancak bu ifade Python 3 için aynı belgelerde değişmeden kalır):

sys.path

(...)

Bu listenin ilk öğesi olan program başlangıcında başlatıldığı gibi path[0], Python yorumlayıcısını çağırmak için kullanılan komut dosyasını içeren dizindir. Kod dizini kullanılamıyorsa (örneğin, yorumlayıcı etkileşimli olarak çağrılırsa veya kod standart girdiden okunursa), Python'u önce geçerli dizindeki modülleri aramaya yönlendirenpath[0] boş dizedir .

O zaman neyi özlüyorum? İfade neden __future__söylediği gibi görünmüyor ve bu çelişkinin belgelerin bu iki bölümü ile tarif edilen ve gerçek davranış arasındaki çözümü nedir?


Yanıtlar:


104

Değişiklik günlüğü yavaşça ifade edilir. from __future__ import absolute_importbir şeyin standart kitaplığın parçası olup olmadığı umurunda değildir ve import stringsize her zaman mutlak ithalatın olduğu standart kitaplık modülünü vermeyecektir.

from __future__ import absolute_importyani siz import stringPython her zaman üst düzey bir stringmodül arayacaktır current_package.string. Ancak, Python'un hangi dosyanın stringmodül olduğuna karar vermek için kullandığı mantığı etkilemez . Ne zaman yaparsın

python pkg/script.py

pkg/script.pyPython için bir paketin parçası gibi görünmüyor. Normal yordamları izleyerek, pkgdizin yola eklenir ve dizindeki tüm .pydosyalar pkgüst düzey modüllere benzer. göreli bir içe aktarma gerçekleştirdiği için değil, üst düzey modül olarak göründüğü için import stringbulur . Bunun standart kütüphane modülü olmaması gerçeği ortaya çıkmıyor.pkg/string.pypkg/string.pystringstring

Dosyayı pkgpaketin bir parçası olarak çalıştırmak için şunları yapabilirsiniz:

python -m pkg.script

Bu durumda, pkgdizin yola eklenmez. Ancak, geçerli dizin yola eklenir.

pkg/script.pyPython'un pkgbir dosya olarak çalıştırıldığında bile paketin bir parçası olarak işlemesi için bazı kazanlar da ekleyebilirsiniz :

if __name__ == '__main__' and __package__ is None:
    __package__ = 'pkg'

Ancak, bu durum etkilenmeyecektir sys.path. pkgDizini yoldan kaldırmak için ek işlem yapmanız gerekir ve pkgüst dizini yolda değilse, bunu da yola yapıştırmanız gerekir.


2
Tamam, demek istediğim, anladım. Mesajımın belgelediği davranış tam olarak bu. Bununla birlikte, iki soru: (1.) "Bu tam olarak doğru değil" ise, dokümanlar neden kategorik olduğunu söylüyor? ve, (2.) Peki, import stringyanlışlıkla en azından yivsiz bir şekilde gölgelendirirseniz nasılsınız sys.modules. Önlenmesi from __future__ import absolute_importamaçlanan bu değil mi? Bu ne işe yarıyor? (PS, ben downvoter değilim.)
İki Bit Simyacı

14
Evet, o bendim ('yararlı değil' için aşağı oy, 'yanlış' için değildi). Alt bölümden OP'nin nasıl sys.pathçalıştığını anladığı açıktır ve asıl soru hiç çözülmemiştir. Yani, aslında ne yapar from __future__ import absolute_import?
wim

5
@ İki BitAlchemist: 1) Değişiklik günlüğü gevşek bir şekilde ifade edilmiştir ve normatif değildir. 2) Gölgelemeyi bırakıyorsun. Hatta tarayarak sys.modulessize standart kütüphane almazsınız stringkendi üst düzey modülü ile gölgeli eğer modülü. from __future__ import absolute_importüst düzey modüllerin üst düzey modülleri gölgelemesini durdurmak anlamına gelmez; paket dahili modüllerin üst düzey modülleri gölgelemesini durdurması gerekiyordu. Dosyayı pkgpaketin bir parçası olarak çalıştırırsanız, paketin iç dosyaları üst düzey olarak görünmeyi durdurur.
user2357112 Monica

@ Two-BitAlchemist: Yanıt revize edildi. Bu sürüm daha yararlı mı?
user2357112 Monica

1
@storen: Alma pkgarama yolunda olması gereken bir paket olduğunu varsayarsak python -m pkg.main. -mdosya yoluna değil, modül adına ihtiyaç duyar.
user2357112 Monica

44

Mutlak ve göreli içe aktarmalar arasındaki fark, yalnızca bir paketten bir modülü içe aktardığınızda ve bu modül bu paketten başka bir alt modülü içe aktardığınızda devreye girer. Farkı gör:

$ mkdir pkg
$ touch pkg/__init__.py
$ touch pkg/string.py
$ echo 'import string;print(string.ascii_uppercase)' > pkg/main1.py
$ python2
Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pkg.main1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pkg/main1.py", line 1, in <module>
    import string;print(string.ascii_uppercase)
AttributeError: 'module' object has no attribute 'ascii_uppercase'
>>> 
$ echo 'from __future__ import absolute_import;import string;print(string.ascii_uppercase)' > pkg/main2.py
$ python2
Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pkg.main2
ABCDEFGHIJKLMNOPQRSTUVWXYZ
>>> 

Özellikle:

$ python2 pkg/main2.py
Traceback (most recent call last):
  File "pkg/main2.py", line 1, in <module>
    from __future__ import absolute_import;import string;print(string.ascii_uppercase)
AttributeError: 'module' object has no attribute 'ascii_uppercase'
$ python2
Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pkg.main2
ABCDEFGHIJKLMNOPQRSTUVWXYZ
>>> 
$ python2 -m pkg.main2
ABCDEFGHIJKLMNOPQRSTUVWXYZ

Not python2 pkg/main2.pysonra başlatılması farklı bir davranışa sahip python2ve daha sonra ithal pkg.main2(kullanarak eşdeğerdir hangi -manahtarı).

Bir paketin alt modülünü çalıştırmak isterseniz, her zaman -myorumlayıcının sys.pathlisteyi zincirlemesini engelleyen ve alt modülün anlambilimini doğru şekilde işleyen anahtarı kullanın .

Ayrıca, hata durumunda daha fazla semantik ve daha iyi hata mesajları sağladıkları için paket alt modülleri için açık göreli ithalatları kullanmayı tercih ederim.


Yani aslında sadece "geçerli dizin" sorunu kaçınmak dar bir durumda çalışır? Bu, PEP 328 ve 2.5 değişim günlüğü tarafından açıklanandan çok daha zayıf bir uygulama gibi görünüyor. Belgelerin yanlış olduğuna inanıyor musunuz?
İki Bit Simyacı

@ İki BitAlchemist Aslında neyi sen yapıyorsun "dar durum" dur. Yürütülecek yalnızca tek bir python dosyası başlatırsınız, ancak bu yüzlerce içe aktarmayı tetikleyebilir. Bir paketin alt modülleri çalıştırılmamalıdır, hepsi bu.
Bakuriu

neden python2 pkg/main2.pyfarklı bir davranış python2 başlatıp pkg.main2 içeri aktarılıyor?
storen

1
@storen Bunun nedeni, göreli içe aktarma davranışlarının değişmesidir. Eğer başlattığınızda pkg/main2.pypiton (versiyon 2) yok değil tedavi pkgbir paket olarak. Kullanırken python2 -m pkg.main2veya ithal o do dikkate almak pkgbir pakettir.
Bakuriu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.