Python: 'from X import Y' ile içe aktarılan Y bileşenini yeniden yüklensin mi?


96

Python'da, kullanarak bir yorumlayıcı oturumuna X modülünü içe import Xaktardığımda ve modül dışarıdan değiştiğinde, modülü ile yeniden yükleyebilirim reload(X). Değişiklikler daha sonra tercüman oturumumda kullanılabilir hale gelir.

Bunu kullanarak X modülünden bir Y bileşenini içe aktardığımda bunun da mümkün olup olmadığını merak ediyorum from X import Y.

reload YY bir modülün kendisi olmadığından, sadece bir modülün içindeki bir bileşen (bu durumda bir sınıf) olduğu için ifade çalışmaz.

Yorumlayıcı oturumundan çıkmadan (veya tüm modülü içe aktarmadan) bir modülün tek tek bileşenlerini yeniden yüklemek mümkün müdür?

DÜZENLE:

Açıklık getirmek gerekirse, soru, X modülünden bir Y sınıfını veya işlevini içe aktarmak ve bir X paketinden bir Y modülünü değil, bir değişikliği yeniden yüklemekle ilgilidir.


Bu soruda bir çelişki olduğuna inanıyorum: " ... possible ... import a component Y from module X" vs " question is ... importing a class or function X from a module Y". Bu etkiye bir düzenleme ekliyorum.
Catskul

Görünüşe göre işaretli cevap aslında soruya cevap vermiyor, sanırım benimki cevaplıyor. Güncelleyebilir misin / yorum yapabilir misin?
Catskul

Yanıtlar:


53

Y bir modül ise (ve X bir paket) iyi reload(Y)olacaktır - aksi takdirde, iyi Python stil kılavuzlarının (işverenim gibi) neden bir modül dışında hiçbir şeyi içe aktarmamayı söylediğini göreceksiniz (bu, birçok harika nedenden biridir) - yine de insanlar, bunun iyi bir fikir olmadığını ne kadar açıklasam da, fonksiyonları ve sınıfları doğrudan aktarmaya devam ediyor ;-).


1
Senin değinmek istediğin noktayı anlıyorum. Bunun neden iyi bir fikir olmadığına dair diğer iyi sebeplerden herhangi birini detaylandırmak ister misiniz?
cschol

7
@cschol: Python Zen, son ayet (Python Zen'i import thisgörmek için etkileşimli komut isteminden); ve ad alanlarının harika bir fikir olmasının tüm nedenleri (adın arandığına dair anında yerel görsel ipuçları, testlerde alay / enjeksiyon kolaylığı, yeniden yükleme yeteneği, bir modülün bazı girişleri yeniden tanımlayarak esnek bir şekilde değiştirebilme yeteneği, öngörülebilir ve kontrol edilebilir) verilerinizin serileştirilmesi ve kurtarılmasıyla ilgili davranış [[örn. dekapaj ve çözme yoluyla]] ve benzeri - bir SO yorumu bu zengin, uzun tartışmanın hakkını verecek kadar uzun değildir !!! -)
Alex Martelli

4
Python 3'te yeniden yüklemenin artık varsayılan ad alanında olmadığını, importlibpakete taşındığını unutmayın . importlib.reload(Y) docs.python.org/3.4/library/... ayrıca bkz stackoverflow.com/questions/961162/...
uçar

4
@ThorSummoner, kesinlikle hayır - bu "her zaman MODÜLLERİ içe aktar" anlamına gelir, bu nedenle "my.package içeriğinden içe aktarım modülü" kesinlikle iyidir ve gerçekten tercih edilir - sadece, sınıfları, işlevleri vb. Asla içe aktarmayın - her zaman, yalnızca, her zaman modülleri .
Alex Martelli

3
Olumsuz oy. Neden? Bu doğru cevap değil. Doğru cevap, 30 Temmuz '12'de 15: 04'te Catskul tarafından verildi.
meh

108

Cevap

Testlerimden basit bir öneren işaretli cevap reload(X)işe yaramıyor.

Doğru cevabı söyleyebileceğim kadarıyla:

from importlib import reload # python 2.7 does not require this
import X
reload( X )
from X import Y

Ölçek

Testim şuydu (Python 2.6.5 + bpython 0.9.5.2)

X.py:

def Y():
    print "Test 1"

bpython:

>>> from X import Y
>>> print Y()
Test 1
>>> # Edit X.py to say "Test 2"
>>> print Y()
Test 1
>>> reload( X )  # doesn't work because X not imported yet
Traceback (most recent call last):
  File "<input>", line 1, in <module>
NameError: name 'X' is not defined
>>> import X
>>> print Y()
Test 1
>>> print X.Y()
Test 1
>>> reload( X ) # No effect on previous "from" statements
>>> print Y()
Test 1
>>> print X.Y() # first one that indicates refresh
Test 2
>>> from X import Y
>>> print Y()
Test 2 
>>> # Finally get what we were after

2
Vay. Bunu gerçekten kullanışlı buldum. Teşekkürler! Şimdi bunu bir satır olarak kullanıyorum: X'i içe aktar; yeniden yükle (X); X ithalatından Y
otterb

2
Bu, kabul edilenden daha iyi bir cevap. İnsanları uyarmak adil, ancak herkesin kullanım durumu farklı. Bazı durumlarda, örneğin python konsolunu kullanırken ve oturumunuzu kaybetmeden kodunuzdaki değişiklikleri yüklemek istediğinizde, bir sınıfı yeniden yüklemek gerçekten yararlıdır.
nicb

1
Bu her zaman işe yaramıyor gibi görünüyor. Bir alt modülü Foogetiren __init__.pybir modülüm var ... Bir yanıtı karşı örnek olarak göndereceğim.
Jason S

Python 3 şimdi tek satırlık: importlib'i içe aktar; X'i içe aktar; importlib.reload (X); X içe aktarma Y'den
Wayne

14
from modulename import func

import importlib, sys
importlib.reload(sys.modules['modulename'])
from modulename import func

1
Bu da ithal edildi tam olarak nasıl hatırlamıyorum çünkü iyi yolu imo olduğunu
portforwardpodcast

1
Orijinal soru için çalışan tek çözüm bu ve çok az oyu var!
CharlesB

Bu işe yaradı, teşekkürler.
Thomas

Python2'de de aynısını yapın, ancak importlib'i içe aktarmadan - sadece python2'de bağımsız bir komut olan reload () 'u çağırın.
mirek

6

Öncelikle, önleyebiliyorsanız, yeniden yüklemeyi hiç kullanmamalısınız. Ama nedenleriniz olduğunu varsayalım (yani, IDLE içinde hata ayıklama).

Kitaplığı yeniden yüklemek, adları modülün ad alanına geri getirmez. Bunu yapmak için, sadece değişkenleri yeniden atayın:

f = open('zoo.py', 'w')
f.write("snakes = ['viper','anaconda']\n")
f.close()

from zoo import snakes
print snakes

f = open('zoo.py', 'w')
f.write("snakes = ['black-adder','boa constrictor']\n")
f.close()

import zoo
reload(zoo)
snakes = zoo.snakes # the variable 'snakes' is now reloaded

print snakes

Bunu birkaç farklı şekilde yapabilirsiniz. Yerel ad alanını arayarak ve söz konusu modüldeki herhangi bir şeyi yeniden atayarak süreci otomatikleştirebilirsiniz, ancak bence yeterince kötü davranıyoruz.


4

Bunu yapmak istiyorsanız:

from mymodule import myobject

Bunun yerine şunu yapın:

import mymodule
myobject=mymodule.myobject

Artık myobject'i planladığınız gibi kullanabilirsiniz (her yerde yorucu okunamayan mymodül referansları olmadan).

Etkileşimli olarak çalışıyorsanız ve nesnemi mymodule'den yeniden yüklemek istiyorsanız, şimdi şunları kullanabilirsiniz:

reload(mymodule)
myobject=mymodule.myobject

2

Kullandığınızı varsayarsak from X import Y, iki seçeneğiniz vardır:

reload(sys.modules['X'])
reload(sys.modules[__name__]) # or explicitly name your module

veya

Y=reload(sys.modules['X']).Y

birkaç önemli nokta:

A. içe aktarma kapsamı modül çapında değilse (e, g: bir işlevde içe aktarma) - ikinci sürümü kullanmalısınız.

B. Y başka bir modülden (Z) X'e aktarılırsa - X'i yeniden yüklemek yerine Z'yi yeniden yüklemelisiniz ve modülünüzü yeniden yüklemek yerine, tüm modüllerinizi yeniden yüklemek (e, g: kullanarak [ reload(mod) for mod in sys.modules.values() if type(mod) == type(sys) ]) bile Z'yi yeniden yüklemeden önce X'i yeniden yükleyebilir - ve daha sonra Y'nin değerini yenilemeyin.


1
  1. reload()modül X,
  2. reload()ithal modülü Ydan X.

Yeniden yüklemenin, diğer ad alanlarına bağlı önceden oluşturulmuş nesneleri değiştirmeyeceğini unutmayın (Alex'ten stil kılavuzunu takip etseniz bile).


1

Jüpiter bir ortamda çalışıyorsanız ve zaten sahipseniz from module import functionsihirli işlevi şu şekilde kullanabilirsiniz autoreload:

%load_ext autoreload
%autoreload
from module import function

autoreloadIPython'daki giriş burada verilmiştir .


0

Sadece AlexMartelli'nin ve Catskul'un cevaplarını takip etmek için reload, en azından Python 2'de kafa karıştırıcı görünen gerçekten basit ama kötü vakalar var .

Aşağıdaki kaynak ağacına sahip olduğumu varsayalım:

- foo
  - __init__.py
  - bar.py

aşağıdaki içeriğe sahip:

init.py:

from bar import Bar, Quux

bar.py:

print "Loading bar"

class Bar(object):
  @property
  def x(self):
     return 42

class Quux(Bar):
  object_count = 0
  def __init__(self):
     self.count = self.object_count
     self.__class__.object_count += 1
  @property
  def x(self):
     return super(Quux,self).x + 1
  def __repr__(self):
     return 'Quux[%d, x=%d]' % (self.count, self.x)

Bu, kullanmadan gayet iyi çalışıyor reload:

>>> from foo import Quux
Loading bar
>>> Quux()
Quux[0, x=43]
>>> Quux()
Quux[1, x=43]
>>> Quux()
Quux[2, x=43]

Ancak yeniden yüklemeyi deneyin ve bunun ya bir etkisi olmaz ya da bazı şeyleri bozar:

>>> import foo
Loading bar
>>> from foo import Quux
>>> Quux()
Quux[0, x=43]
>>> Quux()
Quux[1, x=43]
>>> reload(foo)
<module 'foo' from 'foo\__init__.pyc'>
>>> Quux()
Quux[2, x=43]
>>> from foo import Quux
>>> Quux()
Quux[3, x=43]
>>> reload(foo.bar)
Loading bar
<module 'foo.bar' from 'foo\bar.pyc'>
>>> Quux()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "foo\bar.py", line 17, in __repr__
    return 'Quux[%d, x=%d]' % (self.count, self.x)
  File "foo\bar.py", line 15, in x
    return super(Quux,self).x + 1
TypeError: super(type, obj): obj must be an instance or subtype of type
>>> Quux().count
5
>>> Quux().count
6
>>> Quux = foo.bar.Quux
>>> Quux()
Quux[0, x=43]
>>> foo.Quux()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "foo\bar.py", line 17, in __repr__
    return 'Quux[%d, x=%d]' % (self.count, self.x)
  File "foo\bar.py", line 15, in x
    return super(Quux,self).x + 1
TypeError: super(type, obj): obj must be an instance or subtype of type
>>> foo.Quux().count
8

Alt barmodülün yeniden yüklendiğinden emin olabilmemin tek yolu reload(foo.bar); Yeniden yüklenen Quuxsınıfa erişmemin tek yolu , onu yeniden yüklenen alt modülden almak ve almaktır; ancak foomodülün kendisi orijinal Quuxsınıf nesnesini tutmaya devam etti , muhtemelen kullandığı için from bar import Bar, Quux( import baronu takip etmek yerine Quux = bar.Quux); dahası Quuxsınıf kendisiyle uyumsuz hale geldi, bu çok tuhaf.

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.