Yanıtlar:
Bu modülün yorumladığı şekliyle genel nesnelerin bir listesidir import *
. Alt çizgi ile başlayan her şeyi gizleme varsayılanını geçersiz kılar.
import *
(örn tk
. Gibi ) içe aktarmanız önerilir . Bu durumda, __all__
modülün kodunda alt çizgi ile başlayan veya isimler varsa iyi bir ipucu .
tk
Bugün (veya 2012'de bile) yayınlandıysa, önerilen uygulamanın kullanmak olacağından emin değilim from tk import *
. Bence uygulama kasıtlı tasarım değil atalet nedeniyle kabul edildi.
Bağlantılı, ancak burada açıkça belirtilmemiş, tam olarak ne zaman __all__
kullanılır. Modülde kullanıldığında bir modülde hangi sembollerin dışa aktarılacağını tanımlayan dizelerin bir listesidir from <module> import *
.
Örneğin, aşağıdaki kod foo.py
açıkça sembolleri bar
ve dışa aktarır baz
:
__all__ = ['bar', 'baz']
waz = 5
bar = 10
def baz(): return 'baz'
Bu semboller daha sonra şu şekilde içe aktarılabilir:
from foo import *
print(bar)
print(baz)
# The following will trigger an exception, as "waz" is not exported by the module
print(waz)
Eğer __all__
yukarıdaki dışarı yorum varsayılan davranışı olarak, bu kod, sonra tamamlanma çalıştırır import *
verilen ad alanından, bir alt çizgi ile başlar olmayan tüm sembolleri içe aktarmaktır.
Referans: https://docs.python.org/tutorial/modules.html#importing-from-a-package
NOT: yalnızca davranışı __all__
etkiler from <module> import *
. Bahsi geçmeyen üyeler __all__
modül dışından hala erişilebilir durumdadırlar from <module> import <member>
.
print(baz())
?
print(baz)
böyle bir şey yazdırır <function baz at 0x7f32bc363c10>
oysa print(baz())
baskılarbaz
__All__ 'i Python'da açıkla?
Değişkenleri
__all__
farklı__init__.py
dosyalarda görmeye devam ediyorum .Bu ne yapar?
__all__
?Bir modülden anlamsal olarak "ortak" adları bildirir. __all__
İçinde bir ad varsa , kullanıcıların onu kullanması beklenir ve değişmeyeceği beklentisine sahip olabilirler.
Ayrıca programatik etkileri olacaktır:
import *
__all__
bir modülde, örneğin module.py
:
__all__ = ['foo', 'Bar']
import *
modülden geldiğinizde , yalnızca içindeki isimlerin __all__
içe aktarıldığı anlamına gelir :
from module import * # imports foo and Bar
Belgeler ve kod otomatik tamamlama araçları, __all__
bir modülde hangi adların gösterileceğini belirlemek için de (aslında gerekir) denetleyebilir .
__init__.py
bir dizini Python paketi yaparGönderen docs :
__init__.py
Dosyaları 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__.py
boş bir dosya olabilir, ancak paket için başlatma kodunu yürütebilir veya__all__
değişkeni ayarlayabilir .
Böylece bir paket için __init__.py
ilan edebilir .__all__
Bir paket genellikle birbirini içe aktarabilen, ancak mutlaka bir __init__.py
dosyayla birbirine bağlanan modüllerden oluşur . Bu dosya dizini gerçek bir Python paketi yapan şeydir. Örneğin, bir pakette aşağıdaki dosyaların bulunduğunu varsayalım:
package
├── __init__.py
├── module_1.py
└── module_2.py
Bu dosyaları Python ile oluşturalım, böylece takip edebilirsiniz - aşağıdakileri bir Python 3 kabuğuna yapıştırabilirsiniz:
from pathlib import Path
package = Path('package')
package.mkdir()
(package / '__init__.py').write_text("""
from .module_1 import *
from .module_2 import *
""")
package_module_1 = package / 'module_1.py'
package_module_1.write_text("""
__all__ = ['foo']
imp_detail1 = imp_detail2 = imp_detail3 = None
def foo(): pass
""")
package_module_2 = package / 'module_2.py'
package_module_2.write_text("""
__all__ = ['Bar']
imp_detail1 = imp_detail2 = imp_detail3 = None
class Bar: pass
""")
Ve şimdi paketinizi içe aktarırken başka birinin kullanabileceği tam bir API sundunuz, şöyle:
import package
package.foo()
package.Bar()
Ve paket, package
ad alanınızı doldururken modüllerinizi oluştururken kullandığınız diğer tüm uygulama ayrıntılarına sahip olmayacaktır .
__all__
içinde __init__.py
Daha fazla işten sonra, modüllerin çok büyük olduğuna (binlerce satır gibi?) Ve ayrılmaya karar verdiniz. Böylece aşağıdakileri yaparsınız:
package
├── __init__.py
├── module_1
│ ├── foo_implementation.py
│ └── __init__.py
└── module_2
├── Bar_implementation.py
└── __init__.py
Öncelikle modüllerle aynı adlara sahip alt paket dizinlerini yapın:
subpackage_1 = package / 'module_1'
subpackage_1.mkdir()
subpackage_2 = package / 'module_2'
subpackage_2.mkdir()
Uygulamaları taşıma:
package_module_1.rename(subpackage_1 / 'foo_implementation.py')
package_module_2.rename(subpackage_2 / 'Bar_implementation.py')
her biri için __init__.py
bildirilen alt paketler için s oluşturun __all__
:
(subpackage_1 / '__init__.py').write_text("""
from .foo_implementation import *
__all__ = ['foo']
""")
(subpackage_2 / '__init__.py').write_text("""
from .Bar_implementation import *
__all__ = ['Bar']
""")
Ve şimdi hala paket düzeyinde API sağladınız:
>>> import package
>>> package.foo()
>>> package.Bar()
<package.module_2.Bar_implementation.Bar object at 0x7f0c2349d210>
Ayrıca API'nize, alt paketin modül düzeyi yerine alt paket düzeyinde yönetebileceğiniz şeyler ekleyebilirsiniz. API'ya yeni bir ad eklemek isterseniz __init__.py
, örneğin modül_2'de güncellemeniz yeterlidir:
from .Bar_implementation import *
from .Baz_implementation import *
__all__ = ['Bar', 'Baz']
Baz
Üst düzey API'da yayınlamaya hazır değilseniz, üst düzeyinizde __init__.py
sahip olabilirsiniz:
from .module_1 import * # also constrained by __all__'s
from .module_2 import * # in the __init__.py's
__all__ = ['foo', 'Bar'] # further constraining the names advertised
ve kullanıcılarınız kullanılabilirliğinin farkındaysa Baz
kullanabilirler:
import package
package.Baz()
ama eğer bilmiyorlarsa, diğer araçlar ( pydoc gibi ) onları bilgilendirmez.
Daha sonra Baz
, prime time için hazır olduğunda bunu değiştirebilirsiniz :
from .module_1 import *
from .module_2 import *
__all__ = ['foo', 'Bar', 'Baz']
_
karşı __all__
:Varsayılan olarak, Python ile başlamayan tüm adları dışa aktarır _
. Kesinlikle olabilir Bu mekanizma güveniyor. Python standart kütüphanesinde Bazı paketler, aslında, do bu itimat, ancak, örneğin, bunların ithalatı takma, onlar da yapmak ctypes/__init__.py
:
import os as _os, sys as _sys
_
Konvansiyonu kullanmak daha zarif olabilir, çünkü isimleri tekrar adlandırmanın artıklığını ortadan kaldırır. Ancak, ithalat için fazlalık ekler (eğer çok fazla varsa) ve bunu tutarlı bir şekilde yapmayı unutmak kolaydır - ve istediğiniz son şey, sadece bir uygulama detayı olmayı amaçladığınız bir şeyi süresiz olarak desteklemektir. çünkü _
bir işlevi adlandırırken an önekini unuttun .
__all__
Kodlarımı kullanabilecek diğerlerinin ne kullanmaları ve kullanmamaları gerektiğini bilmesi için kişisel olarak modüller için geliştirme yaşam döngümün başlarında yazıyorum .
Standart kitaplıktaki çoğu paket de kullanır __all__
.
__all__
mantıklı olduğundaAşağıdaki durumlarda _
yerine önek kuralına bağlı kalmak mantıklıdır __all__
:
export
dekoratörKullanmanın dezavantajı, __all__
iki kez dışa aktarılan işlevlerin ve sınıfların adlarını yazmanız gerektiğidir - ve bilgiler tanımlardan ayrı tutulur. Biz olabilir bu sorunu çözmek için bir dekoratör kullanın.
David Beazley'in ambalaj konusundaki konuşmasından böyle bir ihracat dekoratörü fikrimi aldım. Bu uygulama CPython'un geleneksel ithalatçısında iyi çalışıyor gibi görünüyor. Özel bir ithalat kancanız veya sisteminiz varsa, bunu garanti etmiyorum, ancak kabul ederseniz, geri çekilmesi oldukça önemsizdir - sadece isimleri manuel olarak__all__
Örneğin, bir yardımcı program kitaplığında dekoratörü tanımlarsınız:
import sys
def export(fn):
mod = sys.modules[fn.__module__]
if hasattr(mod, '__all__'):
mod.__all__.append(fn.__name__)
else:
mod.__all__ = [fn.__name__]
return fn
Bir tanımlarsınız nerede ve ardından __all__
, bunu yapmak:
$ cat > main.py
from lib import export
__all__ = [] # optional - we create a list if __all__ is not there.
@export
def foo(): pass
@export
def bar():
'bar'
def main():
print('main')
if __name__ == '__main__':
main()
Ve bu, ana olarak çalıştırılsın veya başka bir işlev tarafından içe aktarılsın.
$ cat > run.py
import main
main.main()
$ python run.py
main
Ve ile API sağlama da import *
çalışacaktır:
$ cat > run.py
from main import *
foo()
bar()
main() # expected to error here, not exported
$ python run.py
Traceback (most recent call last):
File "run.py", line 4, in <module>
main() # expected to error here, not exported
NameError: name 'main' is not defined
@export
.
__init__.py
ve kullanımını anlamasına yardımcı olmak için gördüğüm en yararlı cevap oldu__all__
__all__
olarak doğru olmasını sağlamak zorunda kalmadan, çıkartmak istediğim sembollerinde çok fazla hamle oluşturulmuş dosyalar olduğu.
__all__
- ama sonra dengesiz bir API olduğunu söyleyebilirim ... Bu bazı kapsamlı kabul testleri için bir şey olurdu.
module_1
ve module_2
; o açık içerecek şekilde OK del module_1
in __init__.py
? Bunun değerli olduğunu düşünmek yanlış mıyım?
Bunu kesin olarak eklemek istiyorum:
Diğer tüm cevaplar modüllerle ilgilidir . Orijinal soru explicitely sözü __all__
de __init__.py
dosyalar, bu nedenle bu piton hakkındadır paketleri .
Genellikle, __all__
yalnızca devreye girer from xxx import *
ve varyant import
deyimi kullanılır. Bu, paketler ve modüller için geçerlidir.
Modüllerin davranışı diğer cevaplarda açıklanmaktadır. Paketler için kesin davranış burada ayrıntılı olarak açıklanmaktadır.
Kısacası, __all__
paket seviyesinde , paket içindeki modüller ile ilgilenmesi dışında ( modül içindeki adları belirtmenin aksine) , modüllerle yaklaşık olarak aynı şeyi yapar . Bu nedenle __all__
, kullandığımızda geçerli ad alanına yüklenecek ve alınacak tüm modülleri belirtir from package import *
.
En büyük fark, bir paketin bildirimini atladığınız zaman , ifadenin hiçbir şey içe aktarmayacağıdır (belgelerde açıklanan istisnalar dışında, yukarıdaki bağlantıya bakın).__all__
__init__.py
from package import *
Öte yandan, __all__
bir modülü atlarsanız , "yıldızlı içe aktarma", modülde tanımlanan tüm adları (alt çizgi ile başlayarak değil) içe aktarır.
from package import *
tanımlanmamış __init__.py
olsa bile, tanımlanan her şeyi içe aktarır all
. Önemli fark, onsuz __all__
paketin dizininde tanımlanan modülleri otomatik olarak içe aktarmayacağıdır.
Ayrıca, pydoc'un göstereceği şeyi değiştirir:
module1.py
a = "A"
b = "B"
c = "C"
module2.py
__all__ = ['a', 'b']
a = "A"
b = "B"
c = "C"
$ pydoc module1
Modül modülü1 ile ilgili yardım: ADI module1 DOSYA module1.py VERİ a = 'A' b = 'B' c = 'C'
$ pydoc module2
Modül modülü2 ile ilgili yardım: ADI modül2 DOSYA module2.py VERİ __all__ = ['a', 'b'] a = 'A' b = 'B'
__all__
Tüm modüllerimde ve dahili detayların altını çizdiğimi beyan ederim, bunlar daha önce canlı tercüman oturumlarında hiç kullanmadığınız şeyleri kullanırken gerçekten yardımcı olur.
__all__
özelleştirir *
içindefrom <module> import *
__all__
özelleştirir *
içindefrom <package> import *
Bir modül bir olduğunu .py
ithal edilecek anlamına dosyası.
Bir paket bir bir dizindir __init__.py
dosyası. Bir paket genellikle modüller içerir.
""" cheese.py - an example module """
__all__ = ['swiss', 'cheddar']
swiss = 4.99
cheddar = 3.99
gouda = 10.99
__all__
insanların bir modülün "genel" özelliklerini bilmelerini sağlar . [ @AaronHall ] Ayrıca, pydoc onları tanır. [ @Longpoke ]
Yerel ad alanına nasıl swiss
ve nasıl cheddar
getirildiklerini görün , ancak gouda
şunları yapmayın :
>>> from cheese import *
>>> swiss, cheddar
(4.99, 3.99)
>>> gouda
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'gouda' is not defined
Olmadan __all__
, herhangi bir sembol (alt çizgi ile başlamayan) mevcut olurdu.
*
bundan etkilenmez__all__
>>> import cheese
>>> cheese.swiss, cheese.cheddar, cheese.gouda
(4.99, 3.99, 10.99)
>>> from cheese import swiss, cheddar, gouda
>>> swiss, cheddar, gouda
(4.99, 3.99, 10.99)
>>> import cheese as ch
>>> ch.swiss, ch.cheddar, ch.gouda
(4.99, 3.99, 10.99)
In __init__.py
a dosyaya paketinde __all__
kamu modülleri veya diğer nesnelerin adları ile dizeleri listesidir. Bu özellikler joker karakterle içe aktarma için kullanılabilir. Modüllerde olduğu gibi , paketten joker karakter içe aktarılırken __all__
özelleştirir *
. [ @MartinStettner ]
İşte Python MySQL Bağlayıcısı'ndan bir alıntı __init__.py
:
__all__ = [
'MySQLConnection', 'Connect', 'custom_error_exception',
# Some useful constants
'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
'HAVE_CEXT',
# Error handling
'Error', 'Warning',
...etc...
]
Varsayılan durum, bir paket için yıldız içermeyen__all__
karmaşıktır, çünkü bariz davranış pahalı olacaktır: paketteki tüm modülleri aramak için dosya sistemini kullanmak. Bunun yerine, dokümanları okuduğumda yalnızca içinde tanımlanan nesneler __init__.py
içe aktarılır:
Eğer
__all__
tanımlı değil, deyimfrom sound.effects import *
yok değil paketinden tüm submodules içesound.effects
Geçerli isim haline; yalnızca paketinsound.effects
içe aktarıldığından (büyük olasılıkla herhangi bir başlatma kodunu çalıştırdığından__init__.py
) emin olur ve ardından pakette tanımlanmış olan adları alır. Buna, tarafından tanımlanmış (ve açıkça yüklenen alt modüller) adlar dahildir__init__.py
. Ayrıca, önceki içe aktarma ifadeleri tarafından açıkça yüklenmiş olan paketin alt modüllerini de içerir.
Joker karakter ithalatından ... okuyucuları ve birçok otomatik aracı [karıştırdıkları] için kaçınılmalıdır.
[ PEP 8 , @ToolmakerSteve]
from <package> import *
olmadan __all__
içinde __init__.py
olduğunu modüllerin herhangi ithal değil .
__init__.py
davrandığını söylemek istedim . Ancak bunun doğru olduğundan veya özellikle alt çizgi ön ekli nesnelerin hariç tutulduğundan emin değilim. Ayrıca, MODÜLLER ve PAKETLER bölümlerini daha net bir şekilde ayırdım. Senin düşüncelerin?
Gönderen (An Resmi olmayan) Python Referans Wiki :
Bir modül tarafından tanımlanan genel adlar, modülün ad alanı adlı bir değişken olup olmadığına bakılarak belirlenir
__all__
; tanımlanmışsa, bu modül tarafından tanımlanan veya içe aktarılan isimler olan bir dizi dizisi olmalıdır. Verilen adların__all__
tümü herkese açık olarak kabul edilir ve bulunması gerekir.__all__
Tanımlı değilse , genel adlar kümesi, modülün ad alanında bulunan ve alt çizgi karakteriyle ("_") başlamayan tüm adları içerir.__all__
herkese açık API'yı içermelidir. API'nın bir parçası olmayan öğelerin (modül içinde içe aktarılan ve kullanılan kütüphane modülleri gibi) yanlışlıkla dışa aktarılmasından kaçınılması amaçlanmıştır.
__all__
bir Python modülünün genel API'sını belgelemek için kullanılır. İsteğe bağlı olmasına rağmen __all__
kullanılmalıdır.
İşte Python dil referansından ilgili alıntı :
Bir modül tarafından tanımlanan genel adlar, modülün ad alanı adlı bir değişken olup olmadığına bakılarak belirlenir
__all__
; tanımlanmışsa, bu modül tarafından tanımlanan veya içe aktarılan isimler olan bir dizi dizisi olmalıdır. Verilen adların__all__
tümü herkese açık olarak kabul edilir ve bulunması gerekir.__all__
Tanımlı değilse , genel adlar kümesi, modülün ad alanında bulunan ve alt çizgi karakteriyle ('_') başlamayan tüm adları içerir.__all__
herkese açık API'yı içermelidir. API'nın bir parçası olmayan öğelerin (modül içinde içe aktarılan ve kullanılan kütüphane modülleri gibi) yanlışlıkla dışa aktarılmasından kaçınılması amaçlanmıştır.
PEP 8 benzer ifadeler kullanır, ancak içe aktarılan adların yokken genel API'nin bir parçası olmadığını da açıkça belirtir __all__
:
İçgözlemi daha iyi desteklemek için modüller, bu
__all__
özelliği kullanarak genel API'larındaki adları açıkça bildirmelidir .__all__
Boş bir listeye ayarlamak , modülün genel API'sı olmadığını gösterir.[...]
İçe aktarılan adlar her zaman bir uygulama ayrıntısı olarak düşünülmelidir. Diğer modüller, içerilen modülün API'sinin açık bir şekilde belgelenmiş bir parçası
os.path
veya__init__
alt modüllerden işlevsellik gösteren bir paket modülü olmadıkça, bu tür içe aktarılan adlara dolaylı erişime güvenmemelidir .
Ayrıca, diğer yanıtlarda belirtildiği gibi, paketler için joker karakterle içe aktarmayı__all__
etkinleştirmek için kullanılır :
İmport ifadesi aşağıdaki kuralı kullanır: bir paketin
__init__.py
kodu adlı bir liste tanımlarsa , karşılaşıldığında__all__
alınması gereken modül adlarının listesi olarak alınırfrom package import *
.
__all__
from <module> import *
ifadeleri etkiler .
Bu örneği düşünün:
foo
├── bar.py
└── __init__.py
İçinde foo/__init__.py
:
Biz define yoksa (Örtülü) __all__
, daha sonra from foo import *
sadece tanımlanmış adlar ithal edecek foo/__init__.py
.
(Açık) Tanımlarsak __all__ = []
, from foo import *
hiçbir şey içe aktarmaz.
Biz tanımlarsanız (Explicit) __all__ = [ <name1>, ... ]
, daha sonra from foo import *
sadece bu isimleri ithal edecek.
Örtük durumda, python ile başlayan adları içe aktarmaz _
. Ancak, bu tür adları kullanarak içe aktarmayı zorlayabilirsiniz __all__
.
Python belgesini burada görüntüleyebilirsiniz .
__all__
nasıl from foo import *
çalıştığını etkiler .
Bir modül gövdesi içindeki (ancak bir işlev veya sınıf gövdesinde olmayan *
) bir from
deyimde yıldız işareti ( ) kullanabilir :
from foo import *
*
Tüm modülün ilişkilendirdiği istekleri foo
(alt çizgi olanlar başından hariç) ithal modülünde küresel değişkenler olarak bağlı. Bir foo
özniteliğe sahip olduğunda , özniteliğin __all__
değeri, bu tür from
ifadelerle bağlanan adların listesidir .
Eğer foo
bir olan paket ve __init__.py
tanımlar adında bir liste __all__
, zaman ithal edilmelidir alt modülü isimlerinin listesi olarak alınır from foo import *
karşılaşılır. Eğer __all__
tanımlı değil, deyim from foo import *
ne olursa olsun isimleri ithalatı paketinde tanımlanmıştır. Buna, tarafından tanımlanmış (ve açıkça yüklenen alt modüller) adlar dahildir __init__.py
.
Bunun __all__
bir liste olması gerekmediğini unutmayın . Belgelere uyarınca import
açıklamada , tanımlanan eğer __all__
bir olmalıdır dizeleri dizisi modülü tarafından tanımlanmış veya ithal isimlerdir. Bu nedenle, bazı bellek ve CPU döngülerini kaydetmek için bir demet kullanabilirsiniz . Modülün tek bir ortak ad tanımlaması durumunda virgül kullanmayı unutmayın:
__all__ = ('some_name',)
Ayrıca bkz. “İçe aktarma *” neden kötü?
Bu burada PEP8'de tanımlanmıştır :
Global Değişken İsimleri
(Bu değişkenlerin yalnızca bir modül içinde kullanılması amaçlandığını umalım.) Kurallar, işlevler ile hemen hemen aynıdır.
Aracılığıyla kullanılmak üzere tasarlanmış modüller, küreselleri dışa aktarmayı önlemek
from M import *
için__all__
mekanizmayı kullanmalı veya bu küreselleri alt çizgiyle önekleme eski kuralını kullanmalıdır (bu küresellerin "halka açık değil" olduğunu belirtmek isteyebilirsiniz).
PEP8, ana Python dağıtımında standart kütüphaneyi içeren Python kodu için kodlama kuralları sağlar. Bunu ne kadar çok takip ederseniz, orijinal niyetinize o kadar yakın olursunuz.
__all__
varsa belirtilmeyen nesneler__all__
tam olarak gizli değildir; isimlerini biliyorsanız normal şekilde mükemmel şekilde görülebilir ve erişilebilir. Sadece, zaten tavsiye edilmeyen bir "ithal *" durumunda, ayrımın herhangi bir ağırlık taşıması söz konusudur.