“İçe aktarma *” neden kötü?


153

import *Python'da kullanmamanız önerilir .

Bir dahaki sefere yapmaktan kaçınabilmek için bunun nedenini herkes paylaşabilir mi?



2
komut dosyası kullanmanız veya yeniden yazmanız gereken kod yazmanızdan kaynaklanır. bazen kod standartlarını göz ardı etmek gerekir. "import *", bir şeylerin nereden geldiğini netleştiren bir adlandırma kuralınız varsa da iyi olabilir. örneğin "Cats import *; TabbyCat; MaineCoonCat; CalicoCat;"
gatoatigrado

3
import *Python 2 veya 3'te ilk olarak benim için çalışmıyor
joshreesjones

1
AMC

Yanıtlar:


223
  • Ad alanınıza çok fazla şey koyduğundan (önceki içe aktarmadan başka bir nesneyi gölgeleyebilir ve bunu bilmezsiniz).

  • Çünkü neyin ithal edildiğini tam olarak bilmiyorsunuz ve belirli bir şeyin hangi modülden alındığını kolayca bulamıyorsunuz (okunabilirlik).

  • Çünkü pyflakeskodunuzdaki hataları statik olarak saptamak gibi harika araçlar kullanamazsınız .


2
Evet, birisi * import kullandığında işimden gerçekten nefret ediyorum, çünkü o zaman sadece pyflakes çalıştırıp mutlu olamıyorum, ama bu ithalatı tamir etmek zorundayım. Yine de güzel, bu pyflakes ile bana yardımcı olur :-)
gruszczy

7
Somut bir örnek olarak, NumPy'nin birçok kullanıcısı yaptıkları zaman numpy.anygölgelendirerek ısırıldı veya "yararlı" bir araç onlar için yapıyor. anyfrom numpy import *
user2357112 Monica

1
IPpyhon için --pylab anahtarını aynı nedenlerle kullanmaktan kaçınmalı mıyım?
timgeb

6
Bunu okumadan önce hiç düşünmemiştim ediyorum bir risk vurgulayın ( "Önceki ithalat bazı diğer nesne gölge olabilir"): import *yapar düzeni içinde importbile normal ithalat sipariş umurumda değil standart kütüphane modülleri için ... önemli tablolar . İfade importsavaşının eski bir kazası hayatta kalan tek kişi olduğunda ifadelerinizi alfabetik olarak tanımlamak kadar masum bir şey senaryonuzu kırabilir. (Komut dosyanız şimdi çalışıyor ve asla değişmese bile, içe aktarılan modül güvendiğiniz yerin yerini alan yeni bir ad getirirse aniden başarısız olabilir.)
Kevin J. Chase

49

Göre Python Zen :

Açık, örtük olmaktan iyidir.

... bununla tartışamaz mısın?


29
Aslında, olabilir ile iddia. Python'da değişkenleri açıkça bildirmediğiniz göz önüne alındığında, bunlara atadıktan sonra sadece ortaya çıkmaktadırlar.
Konrad Rudolph

7
@gruszczy: değişkenleri bildirmek neye gereksizdir ? Atama? Hayır, bu iki ayrı kavram ve bir şeyi açıklamak çok farklı ve önemli bir bilgi veriyor. Her neyse, açıklık her zaman fazlalık ile bağlantılıdır, aynı madalyonun iki yüzüdür.
Konrad Rudolph

3
@kriss doğru, ama benim amacım bu değildi. Demek istediğim, bir değişkeni açıkça bildirememenin hatalara yol açmasıydı. "[Deklarasyon olmadan atama imkansız” diyorsunuz. Ama bu yanlış, bütün mesele Python'un maalesef tam olarak bunu mümkün kılması.
Konrad Rudolph

3
@kriss Derleyiciye bildirimle verilen bir başka bilgi, gerçekten de yeni bir değişken bildirmeyi planladığınız gerçeğidir. Bu , tip sistemi için çok önemli bir bilgi parçasıdır. Modern IDE'lerin yanlış yazma sorununu çözdüğünü söylüyorsunuz, ancak bu sadece yanlış ve aslında bu, statik olarak derlenmemiş dillerde önemli bir sorundur, bu yüzden Perl ekledi use strict(JavaScript var). Bir yana, elbette Python daktilo değil (aslında güçlü bir şekilde yazılmıştır). Neyse, hatta eğer sen haklıydın, bu yine Python Zen ters düşeceğini, bu cevap gösterdi.
Konrad Rudolph

3
@kriss Yanlış: aynı değişken ismini tekrar kullanmak sorun değil - aynı değişkeni tekrar kullanmak (yani aynı kapsamda aynı isim). Açık beyanlar tam olarak bu hatayı önleyecektir (ve basit bir yanlış yazmaya dayanan diğerleri, söylediğim gibi, aslında Perl benzeri daha büyük olmasına rağmen, son derece yaygın ve zaman alıcı bir sorundur. Diller). Ve bahsettiğim çelişki, Zen'in açıklık gereksinimi, ki burada vücuttan pencereden atıldı.
Konrad Rudolph

40

**locals()Fonksiyonlara geçmiyorsun , değil mi?

Python bir "include" deyimi yoksun, yana veself parametre açık olduğunu ve diğer modülleri okumadan ve her türlü olmaksızın - kapsam kuralları oldukça basittir, bu nesne nereden geldiğini bir değişken bir parmağını işaret ve anlatmak çok kolay genellikle var IDE (yine de içgözlemle sınırlı, dil çok dinamik olduğu için).

import *Sonları hepsi bu.

Ayrıca, böcekleri gizlemek için somut bir olasılığı vardır.

import os, sys, foo, sqlalchemy, mystuff
from bar import *

Şimdi, çubuk modülünde " os", " mystuff" vb. Niteliklerin herhangi biri varsa, açıkça içe aktarılanları geçersiz kılar ve muhtemelen çok farklı şeylere işaret ederler. Tanımlanması __all__örtük olarak ithal edilecek bu ne devletler - - barda akıllıca genellikle ancak nesneler okuma ve çubuk modülü ayrıştırma ve takip etmeden, nereden geldiğini hala iz zor olan ithalatı. Bir ağ, import *bir projenin sahipliğini aldığımda ilk düzelttiğim şey.

Beni yanlış anlama: eğer import *eksik olsaydı, sahip olmak için ağlardım. Ancak dikkatli kullanılmalıdır. İyi bir kullanım örneği, başka bir modül üzerinde bir cephe arayüzü sağlamaktır. Benzer şekilde, koşullu içe aktarma ifadelerinin veya işlev / sınıf ad alanları içinde içe aktarma işlemlerinin kullanılması biraz disiplin gerektirir.

Orta ila büyük projelerde veya birkaç katkısı olan küçük projelerde, daha önce birkaç tür hatayı yakalamak için statik analiz açısından en az hijyen gereklidir - en azından pyflakes veya daha iyi düzgün yapılandırılmış bir pylint çalıştırmak - olurlar.

Tabii ki bu python olduğundan - kuralları çiğnemekten ve keşfetmekten çekinmeyin - ancak on kat büyüyebilecek projelere karşı dikkatli olun, eğer kaynak kod disiplini eksikse sorun olacaktır.


6
Python 2.x yapar bir "include" deyimi var. Denir execfile(). Neyse ki, nadiren kullanılır ve 3.x'te gitti.
Sven Marnach

**vars()Aranan işlev başka bir dosyadaysa globalleri dahil etmeye ne dersiniz ? : P
Solomon Ucko

16

from ... import *Etkileşimli bir oturumda yapmanız uygundur .


Bir doctestdize içine ne dersin ? Does import *bu durumda bir "sandbox" içeride yorumlanır get? Teşekkürler.
PatrickT

16

Çünkü isim alanını kirletiyorsunuz. Kendiniz tanımladığınız işlevlerle çakışabilecek kendi ad alanınızdaki tüm işlevleri ve sınıfları içe aktaracaksınız.

Ayrıca, kalifiye bir adın bakım görevi için daha açık olduğunu düşünüyorum; kod satırında bir işlevin nereden geldiğini görürsünüz, böylece dokümanları daha kolay kontrol edebilirsiniz.

Foo modülünde:

def myFunc():
    print 1

Kodunuzda:

from foo import *

def doThis():
    myFunc() # Which myFunc is called?

def myFunc():
    print 2


9

Diyelim ki foo adlı bir modülde aşağıdaki kod var:

import ElementTree as etree

ve sonra kendi modülünüzde:

from lxml import etree
from foo import *

Şimdi, içinde lxml etree var gibi görünüyor , ancak gerçekten bunun yerine ElementTree var gibi hata ayıklaması zor bir modül var .


7

Bunların hepsi iyi cevaplar. Python'da yeni insanlara kod yazmayı öğretirken, uğraşmanın import *çok zor olduğunu da ekleyeceğim . Siz veya onlar kodu yazmasa bile, bu hala bir engel.

Çocuklara (yaklaşık 8 yaşında) Minecraft'ı manipüle etmeleri için Python'da program yapmayı öğretiyorum. Onlara ( Atom Editor ) çalışmak ve REPL güdümlü gelişimi ( bpython aracılığıyla ) öğretmek için yardımcı olacak bir kodlama ortamı sağlamayı seviyorum . Atom'da ipuçlarının / tamamlamanın bpython kadar etkili olduğunu düşünüyorum. Neyse ki, diğer bazı istatistiksel analiz araçlarının aksine, Atom tarafından aldanmayın import *.

Ancak, bu örneği ele alalım ... Bu paketleyicide , bu blok listesinifrom local_module import * içeren bir demet modülleri . İsim-alanı çarpışma riskini göz ardı edelim. Bunu yaparak , bu belirsiz blokların tüm listesini yapmak için neyin mevcut olduğunu bilmek için gitmeniz gereken bir şey yaparlar. Bunun yerine kullanmışlarsa , yazabilirsiniz ve daha sonra bir otomatik tamamlama listesi açılır. from mcpi.block import *from mcpi import blockwalls = block.Atom.io ekran görüntüsü


6

İnsanların buraya koyduğu geçerli noktaları anladı. Ancak, bazen, "yıldız içe aktarma" her zaman kötü bir uygulama olmayabilir bir argüman var:

  • Kodumu tüm sabitler adlı bir modüle gidecek şekilde yapılandırmak istediğimde const.py:
    • Eğer yaparsam import const, o zaman her sabit için, onu const.SOMETHINGmuhtemelen en uygun yol değil olarak ifade etmeliyim .
    • Eğer yaparsam from const import SOMETHING_A, SOMETHING_B ..., o zaman çok fazla açıktır ve yapılandırmanın amacını yener.
    • Böylece bu durumda hissediyorum, yapmak from const import *daha iyi bir seçim olabilir.

4

Bu ise çok kötü iki nedenden dolayı uygulama:

  1. Kod Okunabilirliği
  2. Değişkenleri / fonksiyonları vb. Geçersiz kılma riski

For noktası 1 : Biraz sert geçebilir bu bir örnek görmek:

from module1 import *
from module2 import *
from module3 import *

a = b + c - d

Burada kod görünce kimse modül hangi ilişkin fikir alacak b, cve daslında aittir.

Diğer taraftan, eğer bunu yaparsanız:

#                   v  v  will know that these are from module1
from module1 import b, c   # way 1
import module2             # way 2

a = b + c - module2.d
#            ^ will know it is from module2

Sizin için çok daha temiz ve ayrıca ekibinize katılan yeni kişinin daha iyi fikri olacak.

İçin noktası 2 : Let hem söylemek module1ve module2olarak değişken var b. Ben yaparken:

from module1 import *
from module2 import *

print b  # will print the value from module2

Burada gelen değer module1kaybolur. Eğer kod bile çalışmama nedenini hata ayıklama zor olacak bdeklare edilmediği module1ve ben kullanım için kodumu bekliyor kod yazdımmodule1.b

Farklı modüllerde aynı değişkenlere sahipseniz ve tüm modülü içe aktarmak istemiyorsanız, şunları da yapabilirsiniz:

from module1 import b as mod1b
from module2 import b as mod2b

2

Bir test olarak, sırasıyla "A 1" ve "B 1" yazdıran 2 fonksiyon A ve B ile bir test.py modülü oluşturdum. Test.py dosyasını aşağıdakilerle içe aktardıktan sonra:

import test

. . . 2 işlevini test.A () ve test.B () olarak çalıştırabilirim ve "test" ad alanında bir modül olarak görünür , bu yüzden test.py'yi düzenlerseniz yeniden yükleyebilirim:

import importlib
importlib.reload(test)

Ama eğer aşağıdakileri yaparsam:

from test import *

ad alanında "test" için bir referans yoktur, bu nedenle etkileşimli bir oturumda bir sorun olan bir düzenlemeden sonra (anlayabildiğim kadarıyla) yeniden yüklemenin bir yolu yoktur. Halbuki aşağıdakilerden herhangi biri:

import test
import test as tt

ad alanına modül adları olarak "test" veya "tt" (sırasıyla) ekler ve bu da yeniden yüklemeye izin verir.

Eğer yaparsam:

from test import *

"A" ve "B" adları, ad alanında işlev olarak görünür . Test.py dosyasını düzenler ve yukarıdaki komutu tekrarlarsam, işlevlerin değiştirilmiş sürümleri yeniden yüklenmez.

Ve aşağıdaki komut bir hata mesajı verir.

importlib.reload(test)    # Error - name 'test' is not defined

Birisi "modül içe aktarma *" ile yüklenen bir modülü nasıl yeniden yükleyeceğini biliyorsa, lütfen gönderin. Aksi takdirde, bu formu önlemek için başka bir neden olacaktır:

from module import *

2

Dokümanlarda önerildiği gibi, import *üretim kodunda (neredeyse) asla kullanmamalısınız .

İthal ederken *bir gelen modül kötü, bir gelen * ithal paketin daha da kötü. Varsayılan olarak, önceki ifadelerle yüklenmiş paketin alt modülleri de dahil olmak üzere from package import *paketin tanımladığı adları alır .__init__.pyimport

Ancak, bir paketin __init__.pykodu adlı bir liste tanımlarsa , karşılaşıldığında __all__alınması gereken alt modül adlarının listesi olarak alınır from package import *.

Bu örneği düşünün ( __all__içinde tanımlanmış bir varsayım yok sound/effects/__init__.py):

# anywhere in the code before import *
import sound.effects.echo
import sound.effects.surround

# in your module
from sound.effects import *

Son ifade echove deyimleri surroundgeçerli ad alanına alır (muhtemelen önceki tanımları geçersiz kılar) çünkü deyim yürütüldüğünde sound.effectspakette tanımlanırlar import.

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.