Python: tải lại thành phần Y được nhập với 'từ X nhập Y'?


91

Trong Python, sau khi tôi đã nhập mô-đun X trong phiên thông dịch bằng cách sử dụng import Xvà mô-đun thay đổi ở bên ngoài, tôi có thể tải lại mô-đun bằng reload(X). Các thay đổi sau đó sẽ có sẵn trong phiên thông dịch viên của tôi.

Tôi đang tự hỏi liệu điều này có khả thi không khi tôi nhập một thành phần Y từ mô-đun X bằng cách sử dụng from X import Y.

Câu lệnh reload Ykhông hoạt động, vì bản thân Y không phải là một mô-đun, mà chỉ là một thành phần (trong trường hợp này là một lớp) bên trong một mô-đun.

Có thể hoàn toàn tải lại các thành phần riêng lẻ của một mô-đun mà không cần rời khỏi phiên thông dịch (hoặc nhập toàn bộ mô-đun) không?

BIÊN TẬP:

Để làm rõ, câu hỏi là về việc nhập một lớp hoặc chức năng Y từ một mô-đun X và tải lại trên một thay đổi, không phải mô-đun Y từ một gói X.


Tôi tin rằng có sự mâu thuẫn trong câu hỏi này: " ... possible ... import a component Y from module X" vs " question is ... importing a class or function X from a module Y". Tôi đang thêm một chỉnh sửa cho hiệu ứng đó.
Catskul

Có vẻ như câu trả lời được đánh dấu không thực sự trả lời câu hỏi, tôi tin là của tôi. Bạn có thể cập nhật / bình luận?
Catskul

Câu trả lời:


49

Nếu Y là một mô-đun (và X là một gói) reload(Y)thì sẽ ổn - nếu không, bạn sẽ thấy tại sao các hướng dẫn phong cách Python tốt (chẳng hạn như chủ nhân của tôi) nói rằng không bao giờ nhập bất kỳ thứ gì ngoại trừ một mô-đun (đây là một trong nhiều lý do tuyệt vời - vậy mà mọi người vẫn tiếp tục nhập trực tiếp các hàm và lớp, bất kể tôi giải thích thế nào rằng đó không phải là một ý kiến ​​hay ;-).


1
Tôi thấy điểm của bạn. Bạn có muốn giải thích thêm về bất kỳ lý do chính đáng nào khác khiến nó không phải là một ý kiến ​​hay không?
cschol

6
@cschol: Zen của Python, câu cuối cùng ( import thistừ lời nhắc tương tác để xem Zen của Python); và tất cả các lý do tại sao không gian tên là một ý tưởng tuyệt vời (manh mối trực quan cục bộ ngay lập tức cho thấy tên đang được tra cứu, dễ chế nhạo / tiêm vào trong các thử nghiệm, khả năng tải lại, khả năng thay đổi linh hoạt của mô-đun bằng cách xác định lại một số mục nhập, có thể dự đoán và kiểm soát được hành vi tuần tự hóa và khôi phục dữ liệu của bạn [[ví dụ: bằng cách chọn và giải nén]], vân vân và vân vân - một nhận xét SO không đủ dài để thực hiện công lý cho lập luận dài và phong phú này !!! -)
Alex Martelli

4
lưu ý rằng trong Python 3, tải lại không còn trong không gian tên mặc định nữa mà đã được chuyển đến importlibgói. importlib.reload(Y) docs.python.org/3.4/library/… xem thêm stackoverflow.com/questions/961162/…
bay

4
@ThorSummoner, hoàn toàn không - nó có nghĩa là "luôn luôn nhập MODULES", vì vậy "từ my.package nhập mymodule" là hoàn toàn tốt và thực sự được ưu tiên - chỉ là, không bao giờ nhập các lớp, hàm, v.v. - luôn luôn, chỉ, từng mô-đun .
Alex Martelli

2
Phản đối. Tại sao? Đây không phải là câu trả lời đúng. Câu trả lời đúng được đưa ra bởi Catskul vào ngày 30 tháng 7 '12 lúc 15:04.
meh

102

Câu trả lời

Từ các bài kiểm tra của tôi, câu trả lời được đánh dấu, gợi ý một câu đơn giản reload(X), không hoạt động.

Từ những gì tôi có thể cho câu trả lời chính xác là:

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

Kiểm tra

Bài kiểm tra của tôi như sau (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
Chà. Tôi thấy điều này thực sự tiện dụng. Cảm ơn! Bây giờ tôi sử dụng cái này làm lớp lót: import X; tải lại (X); từ X nhập Y
otterb

1
Đây là một câu trả lời tốt hơn câu trả lời được chấp nhận. Thật công bằng khi cảnh báo mọi người, nhưng trường hợp sử dụng của mọi người là khác nhau. Trong một số trường hợp, việc tải lại một lớp thực sự hữu ích, ví dụ: khi bạn đang sử dụng bảng điều khiển python và muốn tải các thay đổi đối với mã của bạn mà không làm mất phiên của bạn.
nicb

Điều này dường như không hoạt động luôn luôn. Tôi có một mô-đun Footrong đó có một __init__.pymà lấy một viên submodule ... Tôi sẽ đăng một câu trả lời như một phản ví dụ.
Jason S

Python 3 một lớp lót bây giờ: import importlib; import X; importlib.reload (X); từ X nhập Y
Wayne

12
from modulename import func

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

Đây là cách tốt nhất imo vì có thể bạn không nhớ chính xác nó như thế nào được nhập khẩu
portforwardpodcast

Đó là giải pháp hiệu quả duy nhất cho câu hỏi ban đầu và có quá ít phiếu bầu!
CharlesB

1
trong python 3 thêm: từ tải lại nhập
importlib

7

Trước hết, bạn hoàn toàn không nên sử dụng tải lại, nếu bạn có thể tránh nó. Nhưng giả sử bạn có lý do của mình (tức là gỡ lỗi bên trong IDLE).

Tải lại thư viện sẽ không lấy lại tên vào không gian tên của mô-đun. Để thực hiện việc này, chỉ cần gán lại các biế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

Bạn có thể làm điều này theo một số cách khác. Bạn có thể tự động hóa quy trình bằng cách tìm kiếm trong không gian tên cục bộ và chỉ định lại bất kỳ thứ gì thuộc về mô-đun được đề cập, nhưng tôi nghĩ chúng ta đã đủ xấu.


4

Nếu bạn muốn làm điều này:

from mymodule import myobject

Làm điều này thay thế:

import mymodule
myobject=mymodule.myobject

Bây giờ bạn có thể sử dụng myobject giống như cách bạn đang lập kế hoạch (không có tham chiếu mymodule khó đọc ở khắp mọi nơi).

Nếu bạn đang làm việc tương tác và muốn tải lại myobject từ mymodule, bạn có thể sử dụng:

reload(mymodule)
myobject=mymodule.myobject

2

giả sử bạn đã sử dụng from X import Y, bạn có hai tùy chọn:

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

hoặc là

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

một số cân nhắc:

A. nếu phạm vi nhập không phải toàn mô-đun (e, g: nhập trong một hàm) - bạn phải sử dụng phiên bản thứ hai.

B. nếu Y được nhập vào X từ một mô-đun khác (Z) - bạn phải tải lại Z, hơn tải lại X và hơn tải lại mô-đun của bạn, thậm chí tải lại tất cả các mô-đun của bạn (e, g: using [ reload(mod) for mod in sys.modules.values() if type(mod) == type(sys) ]) có thể tải lại X trước khi tải lại Z - và hơn không làm mới giá trị của Y.


1
  1. reload()mô-đun X,
  2. reload()nhập mô-đun Ytừ X.

Lưu ý rằng việc tải lại sẽ không thay đổi các đối tượng đã được tạo bị ràng buộc trong các không gian tên khác (ngay cả khi bạn làm theo hướng dẫn kiểu từ Alex).


1

Nếu bạn đang làm việc trong một môi trường lộn xộn và bạn đã có from module import functionthể sử dụng chức năng ma thuật, autoreloadbằng cách

%load_ext autoreload
%autoreload
from module import function

Phần giới thiệu của autoreloadIPython được đưa ra ở đây .


0

Chỉ cần theo dõi của AlexMartelliCatskul của câu trả lời, có một số trường hợp thực sự đơn giản nhưng khó chịu xuất hiện lại gây bất ngờ reload, ít nhất là trong Python 2.

Giả sử tôi có cây nguồn sau:

- foo
  - __init__.py
  - bar.py

với nội dung sau:

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)

Điều này hoạt động tốt mà không cần sử dụng reload:

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

Nhưng hãy thử tải lại và nó không có tác dụng hoặc làm hỏng mọi thứ:

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

Cách duy nhất tôi có thể đảm bảo barmô-đun con được tải lại là reload(foo.bar); cách duy nhất để tôi truy cập vào Quuxlớp được tải lại là truy cập và lấy nó từ mô-đun con được tải lại; nhưng foobản thân mô-đun vẫn giữ Quuxđối tượng lớp ban đầu , có lẽ là vì nó sử dụng from bar import Bar, Quux(thay vì import bartheo sau Quux = bar.Quux); hơn nữa Quuxlớp học không đồng bộ với chính nó, điều này thật kỳ lạ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.