Python: bir alt paketi veya alt modülü içe aktarma


94

Zaten düz paketler kullandığım için iç içe paketlerle karşılaştığım sorunu beklemiyordum. Burada…

Dizin düzeni

dir
 |
 +-- test.py
 |
 +-- package
      |
      +-- __init__.py
      |
      +-- subpackage
           |
           +-- __init__.py
           |
           +-- module.py

İçeriği init .py

Hem package/__init__.pyve package/subpackage/__init__.pyboş.

İçeriği module.py

# file `package/subpackage/module.py`
attribute1 = "value 1"
attribute2 = "value 2"
attribute3 = "value 3"
# and as many more as you want...

İçeriği test.py(3 versiyon)

Versiyon 1

# file test.py
from package.subpackage.module import *
print attribute1 # OK

Bu, bir şeyleri içe aktarmanın kötü ve güvenli olmayan yolu (hepsini toplu olarak içe aktar), ancak işe yarıyor.

Versiyon 2

# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
from module import attribute1

Öğeye göre içe aktarmanın daha güvenli bir yolu, ancak başarısız oluyor, Python bunu istemiyor: "Modül adında modül yok" mesajıyla başarısız oluyor. Ancak …

# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
print module # Surprise here

… Diyor <module 'package.subpackage.module' from '...'>. Yani bu bir modül, ama bu bir modül değil / -P 8-O ... uh

Sürüm 3

# file test.py v3
from package.subpackage.module import attribute1
print attribute1 # OK

Bu çalışıyor. Yani ya her zaman aşırı öldürme önekini kullanmak zorunda kalıyorsunuz ya da 1. sürümde olduğu gibi güvenli olmayan yolu kullanmak zorunda kalıyorsunuz ve Python tarafından güvenli ve kullanışlı yolu kullanmak için izin verilmiyor mu? Güvenli olan ve gereksiz uzun öneklerden kaçınmanın daha iyi yolu, Python'un reddettiği tek yol mu? Bu, sevdiği import *için mi yoksa aşırı uzun önekleri sevdiği için mi?

Zor sözler için özür dilerim, ama bu iki gün bu aptalca davranışları aşmaya çalışıyorum. Bir yerde tamamen yanılmadıysam, bu bana Python'un paket ve alt paket modelinde bir şeylerin gerçekten bozuk olduğunu hissettirecek.

Notlar

  • sys.pathKüresel yan etkilerden kaçınmak için ya da *.pthdosyalara güvenmek istemiyorum , bunlar sys.pathaynı küresel etkilerle oynamanın başka bir yolu . Çözümün temiz olması için yalnızca yerel olması gerekir. Her iki Python da alt paketi işleyebilir, ya da değil, ancak yerel şeyleri işleyebilmek için global konfigürasyonla oynamaya gerek kalmamalıdır.
  • Ayrıca ithalatı kullanmayı denedim package/subpackage/__init__.py, ama hiçbir şeyi çözmedi, aynı şeyi yapıyor ve şikayetçi subpackagebir modül değil, print subpackagebunun bir modül olduğunu söylüyor (yine garip davranış).

Tamamen yanılıyor olabilirim (tercih ettiğim seçenek), ama bu Python hakkında beni çok hayal kırıklığına uğratıyor.

Denediğim üçünün dışında bilinen başka bir yol var mı? Bilmediğim bir şey mi?

(iç çekmek)

-----% <----- düzenle ----->% -----

Şimdiye kadarki sonuç (insanların yorumlarından sonra)

Python'da gerçek alt paket gibisi yoktur, çünkü tüm paket referansları yalnızca küresel bir sözlüğe gider, bu da yerel bir sözlük olmadığı anlamına gelir, bu da yerel paket referansını yönetmenin bir yolu olmadığı anlamına gelir.

Tam önek veya kısa önek veya takma ad kullanmanız gerekir. De olduğu gibi:

Tam önek sürümü

from package.subpackage.module import attribute1
# An repeat it again an again
# But after that, you can simply:
use_of (attribute1)

Kısa önek versiyonu (ancak tekrarlanan önek)

from package.subpackage import module
# Short but then you have to do:
use_of (module.attribute1)
# and repeat the prefix at every use place

Ya da yukarıdakinin bir varyasyonu.

from package.subpackage import module as m
use_of (m.attribute1)
# `m` is a shorter prefix, but you could as well
# define a more meaningful name after the context

Faktörlü versiyon

Birden çok varlığı aynı anda toplu olarak içe aktarmayı düşünmüyorsanız şunları yapabilirsiniz:

from package.subpackage.module import attribute1, attribute2
# and etc.

İlk favori zevkimde değil (ithal edilen her varlık için bir ithalat beyannamesi olmasını tercih ederim), ancak kişisel olarak tercih edeceğim biri olabilir.

Güncelleme (2012-09-14):

Son olarak, düzen hakkında bir yorum dışında pratikte iyi görünüyor. Yukarıdakilerin yerine kullandım:

from package.subpackage.module import (

    attribute1, 
    attribute2,
    attribute3,
    ...)  # and etc.

"From. İmport modülünü" "/package/subpackage/__init__.py" içine yazdığınızda işler nasıl gidiyor?
Markus Unterwaditzer 01

"Faktörlü sürümünüz" yapmak istediğiniz şey için tam olarak doğru görünüyor. Öznitelik1 ve öznitelik2 için ayrı bir içe aktarma satırı yaparsanız ("tercih ettiğiniz" gibi), kasıtlı olarak kendinize daha fazla iş vermiş olursunuz. Bunu yapmak için hiçbir sebep yok.
BrenBarn

Üzgünüm ama ne istediğini anlamadım. Sorunuzu daha net bir şekilde yeniden ifade edebilir misiniz? Tam olarak ne yapmak istersiniz? Demek istediğim, işe yaramayan ne yazmak istersiniz ve nasıl çalışmasını beklersiniz? Okuduklarıma göre, içe aktarmanın anlambiliminin Java'nın veya C'ninki gibi olması gerektiğini düşünüyorum. Son __all__olarak, yıldız içe aktarıldığında dışa aktarılması gereken adların bir listesini içeren bir değişken ekleyerek bir modülü "yıldız içe aktarmayı" güvenli hale getirebilirsiniz . düzenleme: Tamam, BrenBarn cevabını okuyorum Ne demek istediğini anladım.
Bakuriu

Yanıtlar:


69

importModüllerin nasıl arandığını yanlış anlıyorsunuz. Bir import deyimi kullandığınızda, her zaman gerçek modül yolunu (ve / veya sys.modules) arar ; önceki içe aktarmalar nedeniyle var olan yerel ad alanındaki modül nesnelerini kullanmaz . Ne zaman yaparsan:

import package.subpackage.module
from package.subpackage import module
from module import attribute1

İkinci satır, çağrılan package.subpackageve moduleo paketten alınan bir paketi arar . Bu çizginin üçüncü çizgiye etkisi yoktur. Üçüncü satır sadece çağrılan bir modülü arar moduleve bulamaz. moduleYukarıdaki satırdan aldığınız nesneyi "yeniden kullanmaz" .

Başka bir deyişle from someModule import ..., "daha önce içe aktardığım someModule adlı modülden ..." anlamına gelmez, "sys.path'de bulduğunuz someModule adlı modülden ..." anlamına gelir. Bir modül yolunu, ona götüren paketleri içe aktararak "artımlı" olarak oluşturmanın bir yolu yoktur. İçe aktarırken her zaman tüm modül adına başvurmanız gerekir.

Ne başarmaya çalıştığınız belli değil. Yalnızca belirli nesne özniteliğini1 içe aktarmak istiyorsanız, sadece yapın from package.subpackage.module import attribute1ve onunla bitirin. package.subpackage.moduleOndan istediğiniz adı içeri aktardıktan sonra uzun süre endişelenmenize gerek yok.

Eğer varsa do başka isimler daha sonra erişmek için modülün erişmesini istiyorum, sonra yapabileceğiniz from package.subpackage import modulesize daha sonra yapabilirsiniz gördüğümüz gibi, ve module.attribute1ve benzeri şekilde mümkün olduğu kadar çok.

Her ikisini de istiyorsanız - yani, attribute1doğrudan erişilebilir ve erişilebilir olmak istiyorsanız module, yukarıdakilerin ikisini birden yapın:

from package.subpackage import module
from package.subpackage.module import attribute1
attribute1 # works
module.someOtherAttribute # also works

package.subpackageİki kez bile yazmayı sevmiyorsanız, özellik1'e manuel olarak yerel bir referans oluşturabilirsiniz:

from package.subpackage import module
attribute1 = module.attribute1
attribute1 # works
module.someOtherAttribute #also works

Yorumlarınız, Ignacio Vazquez-Abrams'tan gelenlerle aynı yönde gidiyor (mesajını yorumladım). Sonunda ekleyeceksiniz, kullanmak hakkında module.attribute1düşündüğüm bir şey var, ama her yerde bir önek gerekliliğinden kaçınmanın bir yolu olacağını düşündüm. Bu yüzden ya her yerde bir önek kullanmalıyım ya da adı tekrar eden yerel bir takma ad oluşturmalıyım. Beklediğim tarz değil, ama bir yolu yoksa (sonuçta, yeniden adlandırma beyanlarına benzer bir şey gerektiren Ada'ya alışkınım).
Hibou57

@ Hibou57: "Versiyon 2" nizde neyi başarmaya çalıştığınız hala bana net değil. Mümkün olmayan ne yapmak istiyorsun? Paket / modül / öznitelik adının herhangi bir bölümünü asla yeniden yazmak, ancak yine de hem modülü hem de özniteliğini içe aktarmak mı istiyorsunuz?
BrenBarn

Yerel bir nesne referansına sahip olabileceğiniz gibi, yerel bir paket referansına sahip olmak istedim. Görünüşe göre sonunda gerçekten yerel modül referansı var, ancak bunlardan içe aktaramazsınız. Bu, komik bir tada sahip yerel ve küresel bir karışım (bazı şeyler yerel olabilir, bazıları küresel olmalı, hoşuma gitmiyor, ama şimdi nasıl çalıştığını daha iyi anladığım sürece iyiyim). Bu arada mesajınız için teşekkürler.
Hibou57

1
Hala nasıl çalıştığını anladığından emin değilim. Ya da 2012'de yaptığınız her halükarda.
Hejazzman

1
6 aylık bir aradan sonra Python'a her döndüğümde, buraya geliyorum. Keşke bu sayfayı her ziyaret ettiğimde oy ekleyebilseydim! Şu cümleyle devasa bir poster yapacağım: "Bir modül yolunu ona götüren paketleri içe aktararak" aşamalı olarak "oluşturmanın bir yolu yoktur."
PatrickT

10

# 2'nin başarısız olmasının nedeni, sys.modules['module']mevcut olmaması (içe aktarma rutininin kendi kapsamı vardır ve moduleyerel adı göremez ) ve modulediskte modül veya paket bulunmamasıdır . İçe aktarılan birden çok adı virgülle ayırabileceğinizi unutmayın.

from package.subpackage.module import attribute1, attribute2, attribute3

Ayrıca:

from package.subpackage import module
print module.attribute1

sys.modules['name']Şimdiye kadar bilmediğim referansınız , korktuğum şeyin bu olduğunu düşünmemi sağladı (ve BrenBarn onaylıyor): Python'da gerçek alt paketler gibisi yoktur. sys.modules, adından da anlaşılacağı gibi, küreseldir ve modüllere yapılan tüm atıflar buna dayanıyorsa, o zaman bir modüle yerel referans gibisi yoktur (Python 3.x ile gelebilir mi?).
Hibou57

Orada "referans" kullanımınız belirsizdir; import# 2'deki ilk , package.subpackage.modulebağlanılacak yerel bir referans oluşturur module.
Ignacio Vazquez-Abrams

Evet, ama bu bir "modül" ;-) alanından içe
aktaramıyorum

0

Yapmaya çalıştığınız tek şey global ad alanınızda nitelik1'i almaksa, sürüm 3 gayet iyi görünüyor. Neden aşırı önek?

2. sürümde, yerine

from module import attribute1

yapabilirsin

attribute1 = module.attribute1

attribute1 = module.attribute1sadece adı katma değeri olmadan tekrarlamaktır. İşe yaradığını biliyorum, ancak bu tarzı sevmiyorum (bu, cevabınızı beğenmediğim anlamına gelmez).
Hibou57

2
Sanırım, burada yorum yapan tüm insanlar gibi, ne yapmak istediğinizi anlamıyorum. Verdiğiniz tüm örneklerde, ad alanınızdaki bir alt paketten bir sembolle sonuçlanmak istediğinize benziyor. Çalışmayan örneğiniz (örnek 2), bunu bir paketten bir alt modülü içe aktararak ve ardından bu alt modülden bir simge içe aktararak yapmak istiyor. Bunu neden bir yerine iki adımda yapmak istediğini bilmiyorum. Belki ideal çözümünüzün ne olacağını ve nedenini daha fazla açıklayın.
Thomas Vander Stichele
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.