Mám dlhodobo spustený server Python a chcel by som mať možnosť aktualizovať službu bez reštartovania servera. Aký je najlepší spôsob, ako to urobiť?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
Modul, ktorý už bol importovaný, môžete znovu načítať pomocou zabudovanej funkcie reload
:
from importlib import reload # Python 3.4+ only.
import foo
while True:
# Do some things.
if is_changed(foo):
foo = reload(foo)
V Pythone 3 bola funkcia reload
presunutá do modulu imp
. Vo verzii 3.4 bol modul imp
zrušený v prospech modulu importlib
a modul reload
bol pridaný do tohto modulu. Ak sa zameriavate na verziu 3 alebo novšiu, pri volaní modulu reload
sa buď odvolajte na príslušný modul, alebo ho importujte.
Myslím si, že práve toto je to, čo chcete. Webové servery, ako napríklad vývojový server Django'to používajú, aby ste mohli vidieť účinky zmien vášho kódu bez toho, aby ste museli reštartovať samotný proces servera.
Citujem z dokumentácie:
Kód modulov Python je prekompilovaný a
kód na úrovni modulu sa znovu vykoná, definuje novú sadu objektov, ktoré sú viazané na mená v module slovníku. Funkcia init rozširujúcich modulov sa nevolá a druhýkrát. Rovnako ako pri všetkých ostatných objektoch v jazyku Python sú staré objekty len regenerované po tom, čo sa na ne odpočítajú referencie klesnú na nulu. Názvy v module mennom priestore sú aktualizované tak, aby ukazovali na všetky nové alebo zmenené objekty. Ďalšie odkazy na staré objekty (ako napr. názvy mimo modulu) nie sú preklopené tak, aby odkazovali na nové objekty a musia byť aktualizované v každom mennom priestore kde sa vyskytujú, ak je to žiaduce.
Ako ste si všimli vo svojej otázke, budete musieť prestaviť objekty Foo
, ak sa trieda Foo
nachádza v module foo
.
Odstránenie modulu môže byť obzvlášť zložité, ak nejde o čistý Python.
Tu je niekoľko informácií z: Ako skutočne odstrániť importovaný modul?
Môžete použiť funkciu sys.getrefcount() na zistenie skutočného počtu referencií.
>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3
Čísla väčšie ako 3 znamenajú, že bude ťažké zbaviť sa modulu. Domáci "prázdny" (neobsahujúci nič) modul by mal byť po jeho skončení vyzbieraný do koša
>>> del sys.modules["empty"]
>>> del empty
keďže tretí odkaz je artefakt funkcie getrefcount().
reload(module)
, ale iba ak je úplne samostatný. Ak má niečo iné odkaz na modul (alebo akýkoľvek objekt patriaci modulu), potom sa vám budú objavovať nenápadné a kuriózne chyby spôsobené tým, že starý kód bude visieť dlhšie, ako ste očakávali, a veci ako isinstance
nebudú fungovať v rôznych verziách toho istého kódu.
Ak máte jednosmerné závislosti, musíte tiež znovu načítať všetky moduly, ktoré závisia od načítaného modulu, aby ste sa zbavili všetkých odkazov na starý kód. A potom rekurzívne načítajte moduly, ktoré závisia od načítaných modulov.
Ak máte kruhové závislosti, čo je veľmi časté, napríklad keď sa zaoberáte opätovným načítaním balíka, musíte naraz načítať všetky moduly v skupine. Nemôžete to urobiť pomocou funkcie reload()
, pretože tá znovu importuje každý modul skôr, ako sa obnovia jeho závislosti, čo umožní, aby sa staré odkazy vkradli do nových modulov.
Jediný spôsob, ako to v tomto prípade urobiť, je nabúrať sys.modules
, čo však nie je podporované. Museli by ste prejsť a vymazať každú položku sys.modules
, ktorú chcete, aby sa pri ďalšom importe znovu načítala, a tiež vymazať položky, ktorých hodnoty sú None
, aby ste vyriešili implementačný problém súvisiaci s cachovaním neúspešných relatívnych importov. Nie je to veľmi príjemné, ale pokiaľ máte plne samostatnú sadu závislostí, ktorá nenecháva odkazy mimo svojej kódovej základne, je to použiteľné.
Najlepšie je asi reštartovať server :-)