Bir klasöre tüm modüller nasıl yüklenir?


269

Birisi bana bütün bir modül dizini almanın iyi bir yolunu sağlayabilir mi?
Ben böyle bir yapı var:

/Foo
    bar.py
    spam.py
    eggs.py

Sadece ekleyerek __init__.pyve yaparak bir pakete dönüştürmeyi denedim from Foo import *ama umduğum şekilde çalışmadı.


2
İnit .py yolu oldukça doğrudur. Çalışmayan kodu gönderebilir misiniz?
— balpha

9
"İşe yaramadı" tanımlayabilir misiniz? Ne oldu? Hangi hata mesajını aldınız?
— S.Lott

Bu Pythonic veya tavsiye edilir mi? "Açık, örtük olmaktan iyidir."
— yangmillstheory

Açık gerçekten daha iyidir. Ancak, her yeni modül eklediğinizde bu can sıkıcı 'bulunamadı' mesajlarına gerçekten değinmek ister misiniz? Ben birçok küçük modül fonksiyonları içeren bir paket dizininiz varsa, o zaman bu konuda gitmek için en güzel yol olduğunu düşünüyorum. Benim varsayımlarım: 1. modül oldukça basit 2. Sen kod düzen için paketi kullanın
— Ido_f

Yanıtlar:


400

.pyGeçerli klasördeki tüm python ( ) dosyalarını listele ve __all__değişken olarak__init__.py

from os.path import dirname, basename, isfile, join
import glob
modules = glob.glob(join(dirname(__file__), "*.py"))
__all__ = [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]

57
Temelde bu yüzden başka bir yapılandırma ile bir dizine python dosyaları bırakabilirsiniz ve başka bir yerde çalışan bir komut dosyası tarafından yürütülmesini.
— Evan Fosmark

5
@NiallDouglas bu cevap OP'nin sorduğu belirli bir soru için, bir zip dosyası yoktu ve pyc dosyaları kolayca dahil edilebilir ve .pyd veya .so libs vb. De unutuyorsun
— Anurag Uniyal

35
Ekleyeceğim tek şey if not os.path.basename(f).startswith('_')ya da en azından if not f.endswith('__init__.py')liste kavrayışının sonuna
— Pykler

10
Daha sağlam hale getirmek için ayrıca emin os.path.isfile(f)olduğunu True. Bu gibi kırık semboller ve dizinleri filtre somedir.py/(köşe-case, itiraf, ama yine de ...)
— MestreLion

12
Ekle from . import *ayarında sonra __all__size yardımcı modüllerle kullanarak kullanılabilir olmasını istiyorsanız .(örn olarak module.submodule1, module.submodule2vs.).
— ostrokach

133

__all__Değişkeni __init__.pyiçerenlere ekleyin :

__all__ = ["bar", "spam", "eggs"]

Ayrıca bkz. Http://docs.python.org/tutorial/modules.html


163
Evet, evet, ama dinamik olmasını sağlamanın bir yolu var mı?
— Evan Fosmark

27
os.listdir()Bazı filtreleme, .pyuzatma ve sıyırma kombinasyonu __all__.

Burada benim için örnek kod olarak çalışmıyor github.com/namgivu/python-import-all/blob/master/error_app.py . Belki orada bir şey özledim?
— Nam G VU

1
Kendimi öğrendim - bu modüllerde tanımlanan değişkeni / nesneyi kullanmak için tam referans yolunu kullanmalıyız, örneğin moduleName.varNameref. stackoverflow.com/a/710603/248616
— Nam G VU

1
@NamGVU: Bu kod Cevabıma ilgili soruya paketin ad içine tüm kamu alt modüllerin isimlerini ithal edecek.
— martineau

54

2017'de güncelleme: muhtemelen importlibbunun yerine kullanmak istersiniz .

Bir ekleyerek Foo dizinini bir paket yapın __init__.py. Bu __init__.pyekte:

import bar
import eggs
import spam

Dinamik olmasını istediğinizden (iyi bir fikir olabilir veya olmayabilir), tüm py dosyalarını list dir ile listeleyip aşağıdaki gibi bir şeyle içe aktarın:

import os
for module in os.listdir(os.path.dirname(__file__)):
    if module == '__init__.py' or module[-3:] != '.py':
        continue
    __import__(module[:-3], locals(), globals())
del module

Ardından, kodunuzdan bunu yapın:

import Foo

Artık modüllere

Foo.bar
Foo.eggs
Foo.spam

vs. from Foo import *, isim çakışmaları ve kodun analizini zorlaştırmak gibi çeşitli nedenlerle iyi bir fikir değildir.


1
Kötü değil, ancak .pyc ve .pyo dosyalarını da içe aktarabileceğinizi unutmayın.
— Evan Fosmark

7
tbh, ben __import__hackish buluyorum , bence isimleri eklemek __all__ve daha sonra from . import *komut dosyasının altına koymak daha iyi olurdu
— freeforall tousez

1
Bunun glob versiyonundan daha güzel olduğunu düşünüyorum.
— lpapp

8
__import__Genel kullanımlar için, bu tarafından kullanılmaz interpreter, kullanmak importlib.import_module()yerine.
— Andrew_1510

2
İlk örnek çok yardımcı oldu, teşekkürler! Python 3.6.4 altında, Python'un içe aktarılmasından önce from . import eggsvb. Yapmak zorunda kaldım __init__.py. Yalnızca ile import eggsalıyorum ModuleNotFoundError: No module named 'eggs'çalışırken import Foode main.pyyukarıdaki dizinde.
— Nick

41

Mihail'in cevabını genişleterek, hackish olmayan yolun (dosya yollarını doğrudan işlememe gibi) aşağıdaki olduğuna inanıyorum:

  1. __init__.pyaltında boş bir dosya oluşturFoo/
  2. gerçekleştirmek
import pkgutil
import sys


def load_all_modules_from_dir(dirname):
    for importer, package_name, _ in pkgutil.iter_modules([dirname]):
        full_package_name = '%s.%s' % (dirname, package_name)
        if full_package_name not in sys.modules:
            module = importer.find_module(package_name
                        ).load_module(full_package_name)
            print module


load_all_modules_from_dir('Foo')

Alacaksınız:

<module 'Foo.bar' from '/home/.../Foo/bar.pyc'>
<module 'Foo.spam' from '/home/.../Foo/spam.pyc'>

Doğru cevaba giden yol budur - ZIP arşivlerini işler, ancak init veya import yazmaz . Aşağıdaki otomatiğe bakın.
— Niall Douglas

1
Başka bir şey: yukarıdaki örnek, modülün zaten yüklü olup olmadığını görmek için sys.modules'ü kontrol etmez. Bu olmadan yukarıdaki modül ikinci kez yükleyecektir kontrol edin :)
— Niall Douglas

3
Kodunuzla load_all_modules_from_dir ('Foo / bar') komutunu çalıştırdığımda "RuntimeWarning: Ana modül 'Foo / bar' bulunamadı mutlak içe aktarma" - bunu bastırmak için, full_package_name = 'ayarlamalıyım.'. dirname.split (os.path.sep) + package_name]) ve ayrıca Foo.bar dosyasını içe aktarın
— Alex Dupuy

Bu RuntimeWarningmesajlar da hiç full_package_name kullanmayan önlenebilir: importer.find_module(package_name).load_module(package_name).
— Artfunkel

RuntimeWarningHatalar da ebeveyn (AKA dizinadı) ithal ederek (bir tartışmalı çirkin şekilde) önlenebilir. Bunu yapmanın bir yolu - if dirname not in sys.modules: pkgutil.find_loader(dirname).load_module(dirname). Tabii ki, bu sadece dirnametek bileşenli göreli bir yol ise işe yarar ; eğik çizgi yok. Şahsen, @ Artfunkel'in bunun yerine base_name temel paketini kullanma yaklaşımını tercih ediyorum.
— dannysauer

31

Python, bir dizinin altındaki tüm dosyaları dahil et:

Ellerini tutması gereken işe yaramayan yeni başlayanlar için.

  1. Bir klasör / home / el / foo yapın ve main.py/ home / el / foo altında bir dosya oluşturun. Bu kodu buraya yerleştirin:

    from hellokitty import *
    spam.spamfunc()
    ham.hamfunc()
    
  2. Bir dizin oluşturun /home/el/foo/hellokitty

  3. __init__.pyAltında bir dosya oluşturun /home/el/foo/hellokittyve bu kodu buraya yerleştirin:

    __all__ = ["spam", "ham"]
  4. İki python dosyası oluşturun: spam.pyve ham.pyaltı/home/el/foo/hellokitty

  5. Spam.py içinde bir işlev tanımlayın:

    def spamfunc():
      print("Spammity spam")
    
  6. Ham.py içinde bir işlev tanımlayın:

    def hamfunc():
      print("Upgrade from baloney")
    
  7. Çalıştır:

    el@apollo:/home/el/foo$ python main.py 
    spammity spam
    Upgrade from baloney
    

1
Sadece yeni başlayanlar için değil, aynı zamanda net cevapları seven Python geliştiricileri de deneyimledi. Teşekkürler.
— Michael Scheper

Ancak import *kullanımı kötü Python kodlama uygulaması olarak kabul edilir. Bunu onsuz nasıl yapıyorsun?
— Rachael Blake

16

Bu sorundan kendim bıktım, bu yüzden düzeltmek için automodinit adlı bir paket yazdım. Http://pypi.python.org/pypi/automodinit/ adresinden edinebilirsiniz .

Kullanımı şöyle:

  1. automodinitPaketi setup.pybağımlılıklarınıza dahil edin .
  2. Tüm __init__.py dosyalarını şu şekilde değiştirin:
__all__ = ["Yeniden yazacağım"]
# Yukarıdaki satırı veya bu satırı değiştirmeyin!
içe aktarma otomatiği
automodinit.automodinit (__ name__, __file__, globaller ())
del automodinit
# İstediğiniz başka bir şey buradan sonra gidebilir, değiştirilmez.

Bu kadar! Şu andan itibaren bir modül içe aktarıldığında __all__ modüldeki .py [co] dosyaları listesine ayarlanacak ve bu dosyaların her biri siz yazmışsınız gibi içe aktarılacaktır:

for x in __all__: import x

Bu nedenle, "M içe aktarımından *" etkisi tam olarak "içe aktarma M" ile eşleşir.

automodinit ZIP arşivlerinin içinden koşmaktan mutlu olduğu için ZIP güvenlidir.

Niall


pip, automodinit'i indiremez, çünkü bunun için pypi'ye yüklenmiş bir şey yoktur.
— kanzure

Github ile ilgili hata raporu için teşekkürler. Bunu v0.13'te düzelttim. Niall
— Niall Douglas

11

Oldukça eski bir automodinitgönderiyi güncellediğimi biliyorum ve kullanmayı denedim , ancak kurulum işleminin python3 için bozulduğunu öğrendim. Bu nedenle, Luca'nın cevabına dayanarak, bu konuya .zip ile çalışmayabilir - daha basit bir cevap buldum, bu yüzden burada paylaşmam gerektiğini düşündüm:

__init__.pymodül içinde yourpackage:

#!/usr/bin/env python
import os, pkgutil
__all__ = list(module for _, module, _ in pkgutil.iter_modules([os.path.dirname(__file__)]))

ve aşağıdaki başka bir paket içinde yourpackage:

from yourpackage import *

Daha sonra paketin içine yerleştirilen tüm modüller yüklenir ve yeni bir modül yazarsanız, otomatik olarak da içe aktarılır. Tabii ki, bu tür şeyleri dikkatle kullanın, büyük güçlerle büyük sorumluluklar gelir.


6
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
for imp, module, ispackage in pkgutil.walk_packages(path=__path__, prefix=__name__+'.'):
  __import__(module)

5

Ayrıca bu sorunla karşılaştım ve bu benim çözümüm oldu:

import os

def loadImports(path):
    files = os.listdir(path)
    imps = []

    for i in range(len(files)):
        name = files[i].split('.')
        if len(name) > 1:
            if name[1] == 'py' and name[0] != '__init__':
               name = name[0]
               imps.append(name)

    file = open(path+'__init__.py','w')

    toWrite = '__all__ = '+str(imps)

    file.write(toWrite)
    file.close()

Bu işlev, klasördeki her modülü tutan __init__.pybir __all__değişken içeren bir dosya (sağlanan klasörde) oluşturur .

Örneğin, adında bir klasör var Test :

Foo.py
Bar.py

Komut dosyasında modüllerin içe aktarılmasını istiyorum:

loadImports('Test/')
from Test import *

Bu, her şeyi içeri aktarır Testve içindeki __init__.pydosya Testşimdi şunları içerir:

__all__ = ['Foo','Bar']

mükemmel, tam olarak ne arıyorum
— uma mahesh

4

Anurag'ın birkaç düzeltmeyle örneği:

import os, glob

modules = glob.glob(os.path.join(os.path.dirname(__file__), "*.py"))
__all__ = [os.path.basename(f)[:-3] for f in modules if not f.endswith("__init__.py")]

4

Önerilen iyileştirmelerle Anurag Uniyal cevabı !

#!/usr/bin/python
# -*- encoding: utf-8 -*-

import os
import glob

all_list = list()
for f in glob.glob(os.path.dirname(__file__)+"/*.py"):
    if os.path.isfile(f) and not os.path.basename(f).startswith('_'):
        all_list.append(os.path.basename(f)[:-3])

__all__ = all_list  

3

Senin o Bkz __init__.pytanımlayıp __all__. Modülleri - paketler doc diyor

__init__.pyDosyaları Python tedavi paket içeren dizinleri yapmaları gerekmektedir; bu, dize gibi ortak bir ada sahip dizinlerin, daha sonra modül arama yolunda gerçekleşen geçerli modülleri yanlışlıkla gizlemesini önlemek için yapılır. En basit durumda, __init__.pyboş bir dosya olabilir, ancak paket için başlatma kodunu yürütebilir veya __all__daha sonra açıklanan değişkeni ayarlayabilir .

...

Tek çözüm, paket yazarının paketin açık bir dizinini sağlamasıdır. İmport deyimi aşağıdaki kuralı kullanır: bir paketin __init__.pykodu adlı bir liste tanımlarsa __all__, paket içe aktarma * ile karşılaşıldığında alınması gereken modül adlarının listesi olarak alınır. Paketin yeni bir sürümü çıktığında bu listeyi güncel tutmak paket yazarına bağlıdır. Paket yazarları, paketlerinden * içe aktarma için bir kullanım görmüyorlarsa desteklememeye de karar verebilirler. Örneğin, dosya sounds/effects/__init__.pyaşağıdaki kodu içerebilir:

__all__ = ["echo", "surround", "reverse"]

Bu from sound.effects import *, ses paketinin adlandırılmış üç alt modülünü içe aktaracağı anlamına gelir .


3

Bu şimdiye kadar bulduğum en iyi yol:

from os.path import dirname, join, isdir, abspath, basename
from glob import glob
pwd = dirname(__file__)
for x in glob(join(pwd, '*.py')):
    if not x.startswith('__'):
        __import__(basename(x)[:-3], globals(), locals())

2

Eklemeniz importlibgereken tek şey şu:

from importlib import import_module
from pathlib import Path

__all__ = [
    import_module(f".{f.stem}", __package__)
    for f in Path(__file__).parent.glob("*.py")
    if "__" not in f.stem
]
del import_module, Path

Meşru mypy sorununa Bu potansiyel müşteriler: error: Type of __all__ must be "Sequence[str]", not "List[Module]". __all__Bu import_moduletemelli yaklaşım kullanılıyorsa tanımlama gerekli değildir .
— Acumenus

1

Standart kütüphaneden pkgutil modülüne bakın. Dizinde bir __init__.pydosya olduğu sürece tam olarak istediğinizi yapmanıza izin verecektir . __init__.pyDosya boş olabilir.


1

Bunun için bir modül oluşturdum, bu da __init__.py(veya başka bir yardımcı dosyaya) güvenmiyor ve sadece aşağıdaki iki satırı yazmamı sağlıyor:

import importdir
importdir.do("Foo", globals())

Tekrar kullanmaktan veya katkıda bulunmaktan çekinmeyin: http://gitlab.com/aurelien-lourot/importdir


0

Sadece importlib ile içe aktarın ve paketteki geri çekilişe__all__ ( addeylem isteğe bağlıdır) ekleyin __init__.py.

/Foo
    bar.py
    spam.py
    eggs.py
    __init__.py

# __init__.py
import os
import importlib
pyfile_extes = ['py', ]
__all__ = [importlib.import_module('.%s' % filename, __package__) for filename in [os.path.splitext(i)[0] for i in os.listdir(os.path.dirname(__file__)) if os.path.splitext(i)[1] in pyfile_extes] if not filename.startswith('__')]
del os, importlib, pyfile_extes

Pyfile_extes nerede tanımlanır?
— Jeppe

özlediğim için üzgünüm, düzeltildi. Bu genellikle almak istediğiniz python dosyasının uzantısıdırpy
— Cheney

0

Ne zaman from . import *yeterince iyi değil, bu üzerinde bir gelişmedir ted tarafından cevap . Özellikle, __all__bu yaklaşımda kullanımı gerekli değildir.

"""Import all modules that exist in the current directory."""
# Ref https://stackoverflow.com/a/60861023/
from importlib import import_module
from pathlib import Path

for f in Path(__file__).parent.glob("*.py"):
    module_name = f.stem
    if (not module_name.startswith("_")) and (module_name not in globals()):
        import_module(f".{module_name}", __package__)
    del f, module_name
del import_module, Path

module_name not in globals()Döngüsel içe aktarma riskini taşıyabileceğinden, zaten içe aktarılmışsa modülün yeniden içe aktarılmasını önleme amaçlı olduğunu unutmayın .

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.