Python: โหลดคอมโพเนนต์ Y ที่นำเข้าด้วย 'from X import Y'?


96

ใน Python เมื่อฉันนำเข้าโมดูล X ในเซสชันล่ามโดยใช้import Xและโมดูลเปลี่ยนไปด้านนอกฉันสามารถโหลดโมดูลใหม่ด้วยreload(X). การเปลี่ยนแปลงจะพร้อมใช้งานในเซสชั่นล่ามของฉัน

ฉันสงสัยว่าสิ่งนี้เป็นไปได้หรือไม่เมื่อฉันนำเข้าส่วนประกอบ Y จากโมดูล X โดยใช้from X import Yไฟล์.

คำสั่งreload Yไม่ทำงานเนื่องจาก Y ไม่ใช่โมดูลเอง แต่เป็นเพียงส่วนประกอบ (ในกรณีนี้คือคลาส) ภายในโมดูล

เป็นไปได้หรือไม่ที่จะโหลดส่วนประกอบแต่ละส่วนของโมดูลโดยไม่ต้องออกจากเซสชันล่าม (หรือนำเข้าโมดูลทั้งหมด)

แก้ไข:

เพื่อความกระจ่างคำถามเกี่ยวกับการนำเข้าคลาสหรือฟังก์ชัน Yจากโมดูล Xและการโหลดการเปลี่ยนแปลงใหม่ไม่ใช่โมดูล Y จากแพ็คเกจ X


ฉันเชื่อว่ามีความขัดแย้งในคำถามนี้: " ... possible ... import a component Y from module X" vs " question is ... importing a class or function X from a module Y" ฉันกำลังเพิ่มการแก้ไขเอฟเฟกต์นั้น
Catskul

ดูเหมือนว่าคำตอบที่ทำเครื่องหมายไว้ไม่ได้ตอบคำถามจริงฉันเชื่อว่าฉันทำ คุณสามารถปรับปรุง / แสดงความคิดเห็น?
Catskul

คำตอบ:


51

ถ้า Y เป็นโมดูล (และ X เป็นแพ็คเกจ) reload(Y)ก็ใช้ได้ไม่เช่นนั้นคุณจะเห็นว่าทำไมคำแนะนำสไตล์ Python ที่ดี (เช่นนายจ้างของฉัน) บอกว่าจะไม่นำเข้าสิ่งใดเลยยกเว้นโมดูล (นี่เป็นหนึ่งในเหตุผลที่ดีหลายประการ - แต่ผู้คนยังคงนำเข้าฟังก์ชั่นและคลาสโดยตรงไม่ว่าฉันจะอธิบายมากแค่ไหนว่ามันไม่ใช่ความคิดที่ดีก็ตาม ;-)


1
ฉันเห็นประเด็นของคุณ คุณสนใจที่จะอธิบายรายละเอียดเกี่ยวกับเหตุผลที่ดีอื่น ๆ หรือไม่ว่าเหตุใดจึงไม่ใช่ความคิดที่ดี
cschol

6
@cschol: Zen of Python ข้อสุดท้าย ( import thisจากการโต้ตอบเพื่อดู Zen of Python); และเหตุผลทั้งหมดว่าทำไม namespaces มีความคิดบีบแตรดี (ทันทีเบาะแสภาพท้องถิ่นที่ชื่อที่ถูกมองขึ้นความสะดวกในการเยาะเย้ย / ฉีดในการทดสอบความสามารถในการโหลดความสามารถสำหรับโมดูลที่จะเปลี่ยนความยืดหยุ่นโดยกําหนดบางรายการสามารถคาดเดาได้และสามารถควบคุม พฤติกรรมในการทำให้เป็นอนุกรมและการกู้คืนข้อมูลของคุณ [[เช่นการดองและการไม่หยิบออก]] และอื่น ๆ - ความคิดเห็น SO แทบจะไม่ยาวพอที่จะให้ความยุติธรรมกับการโต้แย้งที่ยาวนานและยาวนานนี้ !!! -)
Alex Martelli

4
โปรดทราบว่าใน Python 3 การโหลดซ้ำไม่ได้อยู่ในเนมสเปซเริ่มต้นอีกต่อไป แต่ถูกย้ายไปยังimportlibแพ็กเกจ importlib.reload(Y) docs.python.org/3.4/library/…ดูด้วยที่stackoverflow.com/questions/961162/…
บิน

4
@ThorSummoner อย่างไม่ - มันหมายความว่า "มักจะนำเข้า Modules" ดังนั้น "จาก MyModule นำเข้า my.package" เป็นอย่างดีและแน่นอนแนะนำ - เพียงแค่ไม่เคยเรียนนำเข้าฟังก์ชั่น ฯลฯ - เสมอเพียงเคยโมดูล
Alex Martelli

3
โหวตลง ทำไม? นี่ไม่ใช่คำตอบที่ถูกต้อง Catskul ให้คำตอบที่ถูกต้องเมื่อ 30 ก.ค. 55 เวลา 15:04 น.
Meh

105

ตอบ

จากการทดสอบของฉันคำตอบที่ทำเครื่องหมายไว้ซึ่งแนะนำว่าง่ายreload(X)ใช้งานไม่ได้

จากสิ่งที่ฉันสามารถบอกได้คำตอบที่ถูกต้องคือ:

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

ทดสอบ

การทดสอบของฉันเป็นดังต่อไปนี้ (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
ว้าว. ฉันพบว่าสิ่งนี้มีประโยชน์จริงๆ ขอบคุณ! ฉันใช้สิ่งนี้เป็นหนึ่งซับตอนนี้: นำเข้า X; โหลดซ้ำ (X); จาก X import Y
otterb

1
นี่เป็นคำตอบที่ดีกว่าคำตอบที่ยอมรับ เป็นเรื่องยุติธรรมที่จะเตือนผู้คน แต่กรณีการใช้งานของทุกคนแตกต่างกัน ในบางกรณีการโหลดคลาสซ้ำจะช่วยได้มากเช่นเมื่อคุณใช้ไพ ธ อนคอนโซลและต้องการโหลดการเปลี่ยนแปลงโค้ดโดยไม่สูญเสียเซสชัน
nicb

1
ดูเหมือนจะไม่ได้ผลเสมอไป ฉันมีโมดูลFooซึ่งมี__init__.pyที่เรียก submodule ... ฉันจะโพสต์คำตอบเป็น counterexample
Jason S

Python 3 one ซับตอนนี้: import importlib; import X; importlib.reload (X); จาก X import Y
Wayne

12
from modulename import func

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

นี่เป็นวิธีที่ดีที่สุดเพราะคุณอาจจำไม่ได้ว่ามันถูกนำเข้าอย่างไร
portforwardpodcast

นั่นเป็นวิธีแก้ปัญหาเดียวที่ใช้ได้ผลสำหรับคำถามเดิมและมีคะแนนโหวตน้อยเกินไป!
CharlesB

1
ใน python 3 add: จาก importlib import reload
mirek

6

ก่อนอื่นคุณไม่ควรใช้การโหลดซ้ำเลยหากคุณสามารถหลีกเลี่ยงได้ แต่สมมติว่าคุณมีเหตุผลของคุณ (เช่นการดีบักภายใน IDLE)

การโหลดไลบรารีซ้ำจะไม่ทำให้ชื่อกลับเข้าไปในเนมสเปซของโมดูล ในการดำเนินการนี้เพียงกำหนดตัวแปรใหม่:

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

คุณสามารถทำได้อีกสองสามวิธี คุณสามารถทำให้กระบวนการเป็นอัตโนมัติโดยการค้นหาผ่านเนมสเปซในเครื่องและกำหนดอะไรก็ได้ที่มาจากโมดูลที่เป็นปัญหา แต่ฉันคิดว่าเราชั่วพอ


4

หากคุณต้องการทำสิ่งนี้:

from mymodule import myobject

ทำสิ่งนี้แทน:

import mymodule
myobject=mymodule.myobject

ตอนนี้คุณสามารถใช้ myobject ได้ในลักษณะเดียวกับที่คุณวางแผนไว้ (โดยไม่มีการอ้างอิง mymodule ที่อ่านไม่ได้น่าเบื่อทุกที่)

หากคุณกำลังทำงานแบบโต้ตอบและต้องการโหลด myobject จาก mymodule คุณสามารถใช้:

reload(mymodule)
myobject=mymodule.myobject

2

สมมติว่าคุณใช้from X import Yคุณมีสองทางเลือก:

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

หรือ

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

ข้อควรพิจารณาเล็กน้อย:

A. ถ้าขอบเขตการนำเข้าไม่กว้างทั้งโมดูล (e, g: import in a function) - คุณต้องใช้เวอร์ชันที่สอง

B. ถ้า Y ถูกนำเข้าสู่ X จากโมดูลอื่น (Z) - คุณต้องโหลด Z ซ้ำมากกว่าโหลด X และโหลดโมดูลของคุณซ้ำแม้จะโหลดโมดูลทั้งหมดของคุณใหม่ (e, g: using [ reload(mod) for mod in sys.modules.values() if type(mod) == type(sys) ]) อาจโหลด X ซ้ำก่อนที่จะโหลด Z - และมากกว่า ไม่รีเฟรชค่าของ Y.


1
  1. reload()โมดูลX,
  2. reload()นำเข้าโมดูลYจากX.

โปรดทราบว่าการโหลดซ้ำจะไม่เปลี่ยนวัตถุที่สร้างไว้แล้วซึ่งผูกไว้ในเนมสเปซอื่น (แม้ว่าคุณจะทำตามคำแนะนำสไตล์จาก Alex)


1

หากคุณกำลังทำงานในสภาพแวดล้อมที่มืดมิดและคุณfrom module import functionสามารถใช้ฟังก์ชันเวทย์มนตร์ได้แล้วautoreloadโดย

%load_ext autoreload
%autoreload
from module import function

การแนะนำของautoreloadใน IPython จะได้รับที่นี่


0

เพียงเพื่อติดตามAlexMartelli ของและCatskul ของคำตอบมีบางกรณีที่เรียบง่าย แต่จริงๆที่น่ารังเกียจที่ปรากฏฉิบหายreloadอย่างน้อยในหลาม 2

สมมติว่าฉันมีแผนผังแหล่งที่มาต่อไปนี้:

- foo
  - __init__.py
  - bar.py

โดยมีเนื้อหาดังต่อไปนี้:

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)

ใช้งานได้ดีโดยไม่ต้องใช้reload:

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

แต่ลองโหลดใหม่และมันก็ไม่มีผลหรือเสียหายอะไร:

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

วิธีเดียวที่ฉันจะให้แน่ใจว่าbarsubmodule ถูกโหลดใหม่คือการreload(foo.bar); วิธีเดียวที่ฉันเข้าถึงQuuxคลาสที่โหลดซ้ำคือการเข้าถึงและคว้ามันจากโมดูลย่อยที่โหลดซ้ำ แต่fooโมดูลตัวเองเก็บไว้ถือไว้เดิมQuuxวัตถุชั้นคงเพราะมันใช้from bar import Bar, Quux(แทนที่จะimport barตามมาด้วยQuux = bar.Quux); ยิ่งไปกว่านั้นQuuxชั้นเรียนก็ไม่ตรงกันซึ่งเป็นเรื่องแปลกประหลาด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.