Kā izkraut (pārlādēt) moduli?

Man ir ilgstoši darbojošs Python serveris, un es vēlētos, lai varētu atjaunināt pakalpojumu, nepārstartējot serveri. Kāds ir labākais veids, kā to izdarīt?

if foo.py has changed:
    unimport foo  <-- How do I do this?
    import foo
    myfoo = foo.Foo()
Risinājums

Ja modulis jau ir importēts, to var pārlādēt, izmantojot iebūvēto funkciju reload:

from importlib import reload  # Python 3.4+ only.
import foo

while True:
    # Do some things.
    if is_changed(foo):
        foo = reload(foo)

Python 3 versijā reload tika pārcelta uz imp moduli. Versijā 3.4 imp tika atcelts par labu importlib, un reload tika pievienots pēdējam. Ja lietojat 3 vai jaunāku versiju, vai nu atsaucieties uz attiecīgo moduli, izsaucot reload, vai importējiet to.

Es domāju, ka tas ir tas, ko jūs vēlaties. Tādi tīmekļa serveri kā Django izstrādes serveris to izmanto, lai jūs varētu redzēt sava koda izmaiņu ietekmi, nepārstartējot pašu servera procesu.

Citējot no dokumentiem:

Python moduļu kods tiek pārkompilēts un pārveidots.

moduļa līmeņa kods tiek atkārtoti izpildīts, definējot jaunu objektu kopumu, kas tiek piesaistīti nosaukumiem moduļa vārdnīcā. init funkcija paplašinājuma moduļi netiek saukti par otro reizi. Tāpat kā visiem citiem objektiem Python sistēmā vecie objekti ir tikai atgūstami pēc tam, kad to atsauču skaits samazinās līdz nullei. Moduļa nosaukumi vārdu telpā tiek atjaunināti, lai norādītu uz jebkuru jauniem vai mainītiem objektiem. Citas atsauces uz vecajiem objektiem (piemēram, uz nosaukumi, kas ir ārpus moduļa) netiek pārsauktas uz jaunajiem objektiem, lai atsauktos uz jaunajiem objektiem un ir jāatjaunina katrā vārdu telpā. kur tās parādās, ja tas ir vēlams.

Kā jūs atzīmējāt savā jautājumā, jums būs jāpārveido Foo objekti, ja Foo klase atrodas foo modulī.

Komentāri (17)

Moduļa dzēšana var būt īpaši sarežģīta, ja tas nav tīrs Python.

Šeit ir sniegta informācija no: How do I really delete an imported module?.

Jūs varat izmantot sys.getrefcount(), lai noskaidrotu faktisko skaitu. atsauces.

>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3

Skaitļi, kas lielāki par 3, norāda, ka būs grūti atbrīvoties no moduļa. Mājas "tukšs" (nesatur neko) modulim vajadzētu būt pēc tam, kad

>>> del sys.modules["empty"]
>>> del empty

jo trešā atsauce ir artefakts no getrefcount() funkcijas.

Komentāri (2)

reload(modulis), bet tikai tad, ja tas ir pilnīgi autonoms. Ja uz šo moduli (vai kādu citu modulim piederošu objektu) ir atsauce uz ko citu, tad var rasties smalkas un interesantas kļūdas, ko izraisa vecais kods, kas ir saglabājies ilgāk, nekā gaidīts, un tādas lietas kā isinstance nedarbojas dažādās tā paša koda versijās.

Ja jums ir vienvirziena atkarības, jums jāpārlādē arī visi moduļi, kas ir atkarīgi no pārlādētā moduļa, lai atbrīvotos no visām atsaucēm uz veco kodu. Un pēc tam rekursīvi pārlādējiet moduļus, kas ir atkarīgi no pārlādētajiem moduļiem.

Ja ir apļveida atkarības, kas ir ļoti bieži sastopams, piemēram, kad tiek veikta pakotnes pārlādēšana, ir jāizlādē visi grupas moduļi vienā piegājienā. To nevar izdarīt ar reload(), jo katrs modulis tiks importēts atkārtoti, pirms tā atkarības ir atjaunotas, tādējādi ļaujot vecajām atsaucēm iefiltrēties jaunajos moduļos.

Vienīgais veids, kā to izdarīt šajā gadījumā, ir uzlauzt sys.modules, kas nav atbalstīts. Būtu jāiet cauri un jāizdzēš katrs sys.modules ieraksts, kuru vēlaties, lai nākamajā importā tiktu ielādēts no jauna, kā arī jāizdzēš ieraksti, kuru vērtības ir None, lai risinātu implementācijas problēmu, kas saistīta ar neizdevušos relatīvo importu kešēšanā. Tas nav ļoti jauki, bet, kamēr vien jums ir pilnībā pašpietiekams atkarību kopums, kas neatstāj atsauces ārpus savas kodbāzes, tas ir izpildāms.

Iespējams, vislabāk ir restartēt serveri. :-)

Komentāri (7)