Python: reload komponen Y diimpor dengan 'from X import Y'?


96

Dengan Python, setelah saya mengimpor modul X dalam sesi penerjemah menggunakan import X, dan modul berubah di luar, saya dapat memuat ulang modul dengan reload(X). Perubahan tersebut kemudian tersedia di sesi penerjemah saya.

Saya bertanya-tanya apakah ini juga mungkin ketika saya mengimpor komponen Y dari modul X menggunakan from X import Y.

Pernyataan reload Ytersebut tidak berfungsi, karena Y bukanlah modul itu sendiri, tetapi hanya komponen (dalam hal ini kelas) di dalam modul.

Apakah mungkin untuk memuat ulang komponen individu dari sebuah modul tanpa meninggalkan sesi interpreter (atau mengimpor seluruh modul)?

EDIT:

Untuk klarifikasi, pertanyaannya adalah tentang mengimpor kelas atau fungsi Y dari modul X dan memuat ulang saat ada perubahan, bukan modul Y dari paket X.


Saya yakin ada kontradiksi dalam pertanyaan ini: " ... possible ... import a component Y from module X" vs " question is ... importing a class or function X from a module Y". Saya menambahkan pengeditan pada efek itu.
Catskul

tampaknya jawaban yang ditandai tidak benar-benar menjawab pertanyaan, saya yakin jawaban saya menjawab pertanyaan itu. Bisakah Anda memperbarui / berkomentar?
Catskul

Jawaban:


51

Jika Y adalah modul (dan X sebuah paket) reload(Y)akan baik-baik saja - jika tidak, Anda akan melihat mengapa panduan gaya Python yang baik (seperti majikan saya) mengatakan untuk tidak pernah mengimpor apa pun kecuali modul (ini adalah salah satu dari banyak alasan bagus - namun orang-orang masih tetap mengimpor fungsi dan kelas secara langsung, tidak peduli seberapa banyak saya menjelaskan bahwa itu bukan ide yang baik ;-).


1
Saya mengerti maksud Anda. Maukah Anda menjelaskan lebih lanjut tentang alasan bagus lainnya mengapa itu bukan ide yang bagus?
cschol

6
@cschol: Zen of Python, bait terakhir ( import thisdari prompt interaktif untuk melihat Zen of Python); dan semua alasan mengapa namespace adalah ide bagus membunyikan klakson (petunjuk visual lokal langsung bahwa nama sedang dicari, kemudahan mengejek / menyuntikkan dalam pengujian, kemampuan untuk memuat ulang, kemampuan modul untuk berubah secara fleksibel dengan mendefinisikan ulang beberapa entri, dapat diprediksi dan dikontrol perilaku serialisasi dan pemulihan data Anda [[misalnya dengan membuat acar dan membongkar]], dan seterusnya, dan seterusnya - komentar SO hampir tidak cukup untuk melakukan keadilan terhadap argumen yang panjang dan kaya ini !!! -)
Alex Martelli

4
perhatikan bahwa di Python 3, reload tidak lagi di namespace default tetapi telah dipindahkan ke importlibpaket. importlib.reload(Y) docs.python.org/3.4/library/… lihat juga stackoverflow.com/questions/961162/…
terbang

4
@ThorSummoner, sama sekali tidak - itu berarti "selalu mengimpor MODUL", jadi "dari my.package import mymodule" baik-baik saja dan memang lebih disukai - cukup, jangan pernah mengimpor kelas, fungsi, dll - selalu, hanya, modul yang pernah ada .
Alex Martelli

3
Suara negatif. Mengapa? Ini bukanlah jawaban yang benar. Jawaban yang benar diberikan oleh Catskul pada 30 Juli '12 pukul 15:04.
meh

105

Menjawab

Dari pengujian saya, jawaban yang ditandai, yang menyarankan sederhana reload(X), tidak berhasil.

Dari apa yang bisa saya katakan, jawaban yang benar adalah:

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

Uji

Tes saya adalah sebagai berikut (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

1
Wow. Saya menemukan ini sangat berguna. Terima kasih! Saya menggunakan ini sebagai satu liner sekarang: import X; muat ulang (X); dari X import Y
otterb

1
Ini adalah jawaban yang lebih baik daripada jawaban yang diterima. Ini adil untuk memperingatkan orang, tetapi kasus penggunaan setiap orang berbeda. Dalam beberapa kasus, sangat membantu untuk memuat ulang kelas, misalnya saat Anda menggunakan konsol python dan ingin memuat perubahan ke kode Anda tanpa kehilangan sesi Anda.
nicb

1
Ini sepertinya tidak selalu berhasil. Saya memiliki modul Fooyang memiliki __init__.pyyang mengambil submodul ... Saya akan memposting jawaban sebagai contoh balasan.
Jason S

Python 3 satu liner sekarang: import importlib; import X; importlib.reload (X); dari X import Y
Wayne

12
from modulename import func

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

Ini adalah cara terbaik karena Anda mungkin tidak ingat persis bagaimana cara mengimpornya
portforwardpodcast

Itulah satu-satunya solusi yang berfungsi untuk pertanyaan asli, dan memiliki suara yang terlalu sedikit!
CharlesB

1
di python 3 tambahkan: dari importlib import reload
mirek

6

Pertama, Anda tidak boleh menggunakan isi ulang sama sekali, jika Anda bisa menghindarinya. Tapi mari kita asumsikan Anda memiliki alasan Anda (yaitu debugging di dalam IDLE).

Memuat ulang pustaka tidak akan mengembalikan nama ke ruang nama modul. Untuk melakukan ini, cukup tetapkan kembali variabel:

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

Anda dapat melakukan ini dengan beberapa cara lain. Anda dapat mengotomatiskan proses dengan mencari melalui namespace lokal, dan menetapkan ulang apa pun yang berasal dari modul tersebut, tetapi saya pikir kami cukup jahat.


4

Jika Anda ingin melakukan ini:

from mymodule import myobject

Lakukan ini sebagai gantinya:

import mymodule
myobject=mymodule.myobject

Anda sekarang dapat menggunakan myobject dengan cara yang sama seperti yang Anda rencanakan (tanpa referensi mymodule tidak terbaca yang melelahkan di mana-mana).

Jika Anda bekerja secara interaktif dan ingin memuat ulang myobject dari mymodule, Anda sekarang dapat menggunakan:

reload(mymodule)
myobject=mymodule.myobject

2

dengan asumsi Anda menggunakan from X import Y, Anda memiliki dua opsi:

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

atau

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

beberapa pertimbangan:

A. jika cakupan impor tidak selebar modul (misalnya, impor dalam fungsi) - Anda harus menggunakan versi kedua.

B. jika Y diimpor ke X dari modul lain (Z) - Anda harus memuat ulang Z, lalu memuat ulang X dan kemudian memuat ulang modul Anda, bahkan memuat ulang semua modul Anda (e, g: using [ reload(mod) for mod in sys.modules.values() if type(mod) == type(sys) ]) mungkin memuat ulang X sebelum memuat ulang Z - dan kemudian tidak menyegarkan nilai Y.


1
  1. reload()modul X,
  2. reload()modul mengimpor Ydari X.

Perhatikan bahwa memuat ulang tidak akan mengubah objek yang sudah dibuat yang terikat di ruang nama lain (meskipun Anda mengikuti panduan gaya dari Alex).


1

Jika Anda bekerja di lingkungan jupyter, dan Anda sudah from module import functiondapat menggunakan fungsi ajaib, autoreloaddengan

%load_ext autoreload
%autoreload
from module import function

Pengenalan autoreloaddi IPython diberikan di sini .


0

Hanya untuk menindaklanjuti AlexMartelli ini dan Catskul ini jawaban, ada beberapa kasus benar-benar sederhana tapi jahat yang muncul untuk mencampuradukkan reload, setidaknya dalam Python 2.

Misalkan saya memiliki pohon sumber berikut:

- foo
  - __init__.py
  - bar.py

dengan konten berikut:

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)

Ini berfungsi dengan baik tanpa menggunakan reload:

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

Tetapi coba muat ulang dan itu tidak berpengaruh atau merusak hal-hal:

>>> 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

Satu-satunya cara saya bisa memastikan barsubmodul dimuat ulang adalah dengan reload(foo.bar); satu-satunya cara saya mengakses Quuxkelas yang dimuat ulang adalah dengan menjangkau dan mengambilnya dari sub modul yang dimuat ulang; tetapi foomodul itu sendiri tetap memegang Quuxobjek kelas asli , mungkin karena ia menggunakan from bar import Bar, Quux(daripada import bardiikuti oleh Quux = bar.Quux); Selain itu, Quuxkelas menjadi tidak selaras dengan dirinya sendiri, yang sungguh aneh.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.