Hoe ontlaad ik (herlaad) een Python-module?

Ik heb een langlopende Python-server en wil graag een service upgraden zonder de server opnieuw te starten. Wat is de beste manier om dit te doen?

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

1, Autoriteit 100%

U kunt een module opnieuw laden wanneer deze al is geïmporteerd door gebruik te maken van de reloadingebouwde functie (alleen Python 3.4+) :

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

In Python 3, reloadwerd verplaatst naar de impmodule. In 3.4 werd impverouderd ten gunste van importliben reloadwerd toegevoegd aan de laatste. Bij het richten van 3 of later, verwijs ofwel de juiste module bij het bellen van reloadof importeer deze.

Ik denk dat dit is wat je wilt. Webservers zoals Django’s Development Server gebruiken dit, zodat u de effecten van uw codewijzigingen kunt zien zonder het serverproces zelf opnieuw te starten.

Offerte van de Documenten:

Code van Python Modules wordt opnieuw gecompileerd en
de module-niveau-code is opnieuw uitgevoerd,
Een nieuwe reeks objecten definiëren die
zijn gebonden aan namen in de module
woordenboek. De init-functie van
uitbreidingsmodules heet niet a
tweede keer. Zoals bij alle andere objecten
in Python zijn de oude objecten alleen
teruggewonnen na hun referentietellingen
naar nul dalen. De namen in de module
naamruimte worden bijgewerkt om te verwijzen naar een
nieuwe of gewijzigde objecten. Ander
verwijzingen naar de oude objecten (zoals
namen buiten de module) zijn niet
rebound om te verwijzen naar de nieuwe objecten
en moet in elke naamruimte worden bijgewerkt
waar ze voorkomen als dat gewenst is.

Zoals je in je vraag hebt opgemerkt, moet je foo-objecten reconstrueren als de klasse foozich in de module foobevindt.


Antwoord 2, autoriteit 28%

In Python 3.0–3.3 zou je het volgende gebruiken: imp.reload(module)

De BDFLheeft beantwoorddedeze vraag.

Echter, impis in 3.4 afgeschaft ten gunste van importlib(bedankt @Stefan!).

Ik denkdaarom dat u nu importlib.reload(module), hoewel ik het niet zeker weet.


Antwoord 3, autoriteit 12%

Het kan bijzonder moeilijk zijn om een module te verwijderen als het geen pure Python is.

Hier is wat informatie van: Hoe verwijder ik een geïmporteerde module echt?

U kunt sys.getrefcount() gebruiken om het werkelijke aantal . te achterhalen
referenties.

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

Getallen groter dan 3 geven aan dat
het zal moeilijk zijn om zich te ontdoen van de
module. De inlandse “lege”
(die niets bevat) module zou moeten zijn
afval verzameld na

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

omdat de derde referentie een artefact is
van de functie getrefcount().


Antwoord 4, autoriteit 9%

reload(module), maar alleen als het volledig op zichzelf staat. Als iets anders een verwijzing heeft naar de module (of een object dat bij de module hoort), dan krijg je subtiele en merkwaardige fouten die worden veroorzaakt doordat de oude code langer blijft hangen dan je had verwacht, en dingen als isinstancewerkt niet in verschillende versies van dezelfde code.

Als je afhankelijkheden in één richting hebt, moet je ook alle modules die afhankelijk zijn van de opnieuw geladen module opnieuw laden om alle verwijzingen naar de oude code te verwijderen. En herlaad vervolgens modules die afhankelijk zijn van de opnieuw geladen modules, recursief.

Als je circulaire afhankelijkheden hebt, wat bijvoorbeeld heel gebruikelijk is als je een pakket opnieuw moet laden, moet je alle modules in de groep in één keer ontladen. Je kunt dit niet doen met reload()omdat het elke module opnieuw zal importeren voordat de afhankelijkheden zijn vernieuwd, waardoor oude verwijzingen in nieuwe modules kunnen kruipen.

De enige manier om dit in dit geval te doen, is door sys.moduleste hacken, wat een beetje niet wordt ondersteund. U moet elk sys.modules-item doorlopen en verwijderen dat u bij de volgende import opnieuw wilt laden, en ook items verwijderen waarvan de waarden Nonezijn om een implementatie af te handelen probleem met het cachen van mislukte relatieve importen. Het is niet erg leuk, maar zolang je een volledig op zichzelf staande set afhankelijkheden hebt die geen referenties buiten de codebase achterlaat, is het werkbaar.

Het is waarschijnlijk het beste om de server opnieuw op te starten. 🙂


Antwoord 5, autoriteit 8%

Gebruik voor Python 2de ingebouwde functie reload:

reload(module)

Voor Python 2enPython 3.23.3gebruik reloadvan module imp:

import imp
imp.reload(module)

Voor Python ≥3.4, impis afgeschaftten gunste van importlib, dus gebruik dit:

import importlib
importlib.reload(module)

of:

from importlib import reload
reload(module)

TL;DR:

Python ≥ 3.4: importlib.reload(module)
Python 3.2 – 3.3: imp.reload(module)
Python 2: reload(module)


6, Autoriteit 7%

if 'myModule' in sys.modules:  
    del sys.modules["myModule"]

7, Autoriteit 3%

De volgende code Hiermee kunt u Python 2/3-compatibiliteit:

try:
    reload
except NameError:
    # Python 3
    from imp import reload

Hiermee kunt u het gebruiken als reload()in beide versies die de dingen eenvoudiger maken.


8, Autoriteit 2%

Het geaccepteerde antwoord verwerkt niet van X-import Y-zaak. Deze code behandelt het en de standaard invoercase ook:

def importOrReload(module_name, *names):
    import sys
    if module_name in sys.modules:
        reload(sys.modules[module_name])
    else:
        __import__(module_name, fromlist=names)
    for name in names:
        globals()[name] = getattr(sys.modules[module_name], name)
# use instead of: from dfly_parser import parseMessages
importOrReload("dfly_parser", "parseMessages")

In de herlaadbehuizing halen we de namen van topniveau opnieuw toe aan de waarden die zijn opgeslagen in de nieuw reloaded-module, die deze bijwerkt.


9, Autoriteit 2%

Dit is de moderne manier om een ​​module opnieuw te laden:

from importlib import reload

Als u de versies van Python ouder dan 3,5 wilt ondersteunen, probeer dan dit:

from sys import version_info
if version_info[0] < 3:
    pass # Python 2 has built in reload
elif version_info[0] == 3 and version_info[1] <= 4:
    from imp import reload # Python 3.0 - 3.4 
else:
    from importlib import reload # Python 3.5+

Om het te gebruiken, voert u reload(MODULE), vervanging van MODULEmet de module die u opnieuw wilt laden.

Bijvoorbeeld, reload(math)wordt de mathmodule opnieuw laden.


10

Voor degenen zoals ik die alle modules willen verwijderen (wanneer ze worden uitgevoerd in de Python-interpreter onder Emacs):

  for mod in sys.modules.values():
      reload(mod)

Meer informatie vindt u in Python-modules opnieuw laden.


Antwoord 11

Enthought Traits heeft een module die hier redelijk goed voor werkt. https://traits.readthedocs.org/en/ 4.3.0/_modules/traits/util/refresh.html

Het zal elke module die is gewijzigd opnieuw laden en andere modules en instanced-objecten bijwerken die deze gebruiken. Het werkt meestal niet met __very_private__-methoden, en kan stikken in klasse-overerving, maar het bespaart me waanzinnig veel tijd om de hosttoepassing opnieuw te moeten starten bij het schrijven van PyQt-guis, of dingen die worden uitgevoerd binnen programma’s zoals Maya of Nuke. Het werkt misschien niet 20-30% van de tijd, maar het is nog steeds ongelooflijk nuttig.

Het pakket van Enthought laadt bestanden niet opnieuw op het moment dat ze veranderen – je moet het expliciet noemen – maar dat zou niet zo moeilijk te implementeren moeten zijn als je het echt nodig hebt


Antwoord 12

Degenen die python 3 gebruiken en herladen vanuit importlib.

Als je problemen hebt alsof het lijkt alsof de module niet herlaadt… Dat komt omdat het wat tijd nodig heeft om pyc opnieuw te compileren (tot 60 sec). Ik schrijf deze hint alleen maar om te weten of je dit soort hebt meegemaakt van het probleem.


Antwoord 13

01-02-2018

  1. module foomoet vooraf succesvol zijn geïmporteerd.
  2. from importlib import reload, reload(foo)

31.5. importlib — De implementatie van import — Python 3.6.4 documentatie


Antwoord 14

Andere optie. Zie dat Python-standaard importlib.reloadde bibliotheek die als argument is doorgegeven, gewoon opnieuw importeert. Het zal nietde bibliotheken herladen die uw lib importeert. Als je veel bestanden hebt gewijzigd en een enigszins complex pakket hebt om te importeren, moet je een diepe herlaadbeurtdoen.

Als je IPythonof Jupyteris geïnstalleerd, kunt u een functie gebruiken om alle bibliotheken diep te herladen:

from IPython.lib.deepreload import reload as dreload
dreload(foo)

Als je Jupyter niet hebt, installeer het dan met dit commando in je shell:

pip3 install jupyter

Antwoord 15

Bewerken (Antwoord V2)

De oplossing van vroeger is goed om alleen de reset-informatie te krijgen, maar het zal niet alle verwijzingen veranderen (meer dan reloadmaar minder dan vereist). Om alle referenties ook daadwerkelijk in te stellen, moest ik naar de garbage collector gaan en de referenties daar herschrijven. Nu werkt het als een tierelier!

Houd er rekening mee dat dit nietwerkt als de GC is uitgeschakeld of als gegevens worden herladen die niet door de GC worden gecontroleerd. Als je niet wilt rotzooien met de GC, is het oorspronkelijke antwoord misschien genoeg voor jou.

Nieuwe code:

import importlib
import inspect
import gc
from enum import EnumMeta
from weakref import ref
_readonly_attrs = {'__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
               '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__',
               '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__',
               '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__',
               '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__',
               '__subclasshook__', '__weakref__', '__members__', '__mro__', '__itemsize__', '__isabstractmethod__',
               '__basicsize__', '__base__'}
def reset_module(module, inner_modules_also=True):
    """
    This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
    module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
    to be the reloaded-module's
    :param module: The module to reload (module reference, not the name)
    :param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
    """
    # For the case when the module is actually a package
    if inner_modules_also:
        submods = {submod for _, submod in inspect.getmembers(module)
                   if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
        for submod in submods:
            reset_module(submod, True)
    # First, log all the references before reloading (because some references may be changed by the reload operation).
    module_tree = _get_tree_references_to_reset_recursively(module, module.__name__)
    new_module = importlib.reload(module)
    _reset_item_recursively(module, module_tree, new_module)
def _update_referrers(item, new_item):
    refs = gc.get_referrers(item)
    weak_ref_item = ref(item)
    for coll in refs:
        if type(coll) == dict:
            enumerator = coll.keys()
        elif type(coll) == list:
            enumerator = range(len(coll))
        else:
            continue
        for key in enumerator:
            if weak_ref_item() is None:
                # No refs are left in the GC
                return
            if coll[key] is weak_ref_item():
                coll[key] = new_item
def _get_tree_references_to_reset_recursively(item, module_name, grayed_out_item_ids = None):
    if grayed_out_item_ids is None:
        grayed_out_item_ids = set()
    item_tree = dict()
    attr_names = set(dir(item)) - _readonly_attrs
    for sub_item_name in attr_names:
        sub_item = getattr(item, sub_item_name)
        item_tree[sub_item_name] = [sub_item, None]
        try:
            # Will work for classes and functions defined in that module.
            mod_name = sub_item.__module__
        except AttributeError:
            mod_name = None
        # If this item was defined within this module, deep-reset
        if (mod_name is None) or (mod_name != module_name) or (id(sub_item) in grayed_out_item_ids) \
                or isinstance(sub_item, EnumMeta):
            continue
        grayed_out_item_ids.add(id(sub_item))
        item_tree[sub_item_name][1] = \
            _get_tree_references_to_reset_recursively(sub_item, module_name, grayed_out_item_ids)
    return item_tree
def _reset_item_recursively(item, item_subtree, new_item):
    # Set children first so we don't lose the current references.
    if item_subtree is not None:
        for sub_item_name, (sub_item, sub_item_tree) in item_subtree.items():
            try:
                new_sub_item = getattr(new_item, sub_item_name)
            except AttributeError:
                # The item doesn't exist in the reloaded module. Ignore.
                continue
            try:
                # Set the item
                _reset_item_recursively(sub_item, sub_item_tree, new_sub_item)
            except Exception as ex:
                pass
    _update_referrers(item, new_item)

Origineel antwoord

Zoals geschreven in het antwoord van @ BOBINE, als er al een verwijzing naar die module in een andere module is (vooral als deze is geïmporteerd met de astrefwoord als import numpy as np), Die exemplaar wordt niet overschreven.

Dit bleek me behoorlijk problematisch bij het toepassen van tests die een “clean-leate” -status van de configuratiemodules vereisten, dus ik heb een functie geschreven met de naam reset_moduledie importlib‘s reloadFunctie opnieuw op en overschrijft recursief alle kenmerken van de aangegeven module. Het is getest met Python-versie 3.6.

import importlib
import inspect
from enum import EnumMeta
_readonly_attrs = {'__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
               '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__',
               '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__',
               '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__',
               '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__',
               '__subclasshook__', '__weakref__', '__members__', '__mro__', '__itemsize__', '__isabstractmethod__',
               '__basicsize__', '__base__'}
def reset_module(module, inner_modules_also=True):
    """
    This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
    module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
    to be the reloaded-module's
    :param module: The module to reload (module reference, not the name)
    :param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
    """
    new_module = importlib.reload(module)
    reset_items = set()
    # For the case when the module is actually a package
    if inner_modules_also:
        submods = {submod for _, submod in inspect.getmembers(module)
                   if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
        for submod in submods:
            reset_module(submod, True)
    _reset_item_recursively(module, new_module, module.__name__, reset_items)
def _reset_item_recursively(item, new_item, module_name, reset_items=None):
    if reset_items is None:
        reset_items = set()
    attr_names = set(dir(item)) - _readonly_attrs
    for sitem_name in attr_names:
        sitem = getattr(item, sitem_name)
        new_sitem = getattr(new_item, sitem_name)
        try:
            # Set the item
            setattr(item, sitem_name, new_sitem)
            try:
                # Will work for classes and functions defined in that module.
                mod_name = sitem.__module__
            except AttributeError:
                mod_name = None
            # If this item was defined within this module, deep-reset
            if (mod_name is None) or (mod_name != module_name) or (id(sitem) in reset_items) \
                    or isinstance(sitem, EnumMeta):  # Deal with enums
                continue
            reset_items.add(id(sitem))
            _reset_item_recursively(sitem, new_sitem, module_name, reset_items)
        except Exception as ex:
            raise Exception(sitem_name) from ex

Opmerking: Gebruik met zorg! Gebruik deze op niet-perifere modules (modules die bijvoorbeeld extern gebruikte klassen definiëren, kunnen leiden tot interne problemen in Python (zoals Pickling / Un-Pickling-problemen).


16

Voor mij voor het geval van Abaqus is het de manier waarop het werkt.
Stel je voor dat je bestand class_verticsedges.py

is

sys.path.append('D:\...\My Pythons')
if 'Class_VerticesEdges' in sys.modules:  
    del sys.modules['Class_VerticesEdges']
    print 'old module Class_VerticesEdges deleted'
from Class_VerticesEdges import *
reload(sys.modules['Class_VerticesEdges'])

17

Als u de volgende fout tegenkomt, kan dit antwoord u helpen om een ​​oplossing te krijgen:

TraceBack (meest recente oproep Laatste):
 Bestand "ffff", lijn 1, in
NameError: naam 'YJYY' is niet gedefinieerd 

of

TraceBack (meest recente oproep Laatste):
 Bestand "ffff", lijn 1, in
 Bestand "/USR/LOCAL/LIB/PYTHON3.7/IMPORTLIB/__INIT__.PY", LINE 140, IN RELOAD
  Verhoog TypeError ("Reload () Argument moet een module zijn")
TypeError: Reload () Argument moet een module zijn 

Als u een importeert, zoals de onderstaande, moet u mogelijk de sys.modulesgebruiken om de module te krijgen die u opnieuw wilt laden:

 import importlib
  import sys
  from YYYY.XXX.ZZZ import CCCC
  import AAA.BBB.CC
  def reload(full_name)
    if full_name in sys.modules:
      importlib.reload(sys.modules[full_name])
  reload('YYYY.XXX.ZZZ') # this is fine in both cases
  reload('AAA.BBB.CC')  
  importlib.reload(YYYY.XXX.ZZZ) # in my case: this fails
  importlib.reload(AAA.BBB.CC)   #             and this is ok

Het belangrijkste probleem is dat de importlib.reloadalleen module accepteert en geen string.


Antwoord 18

Voor het verwijderen van modules uit sys.modules moeten ook ‘Geen’typen worden verwijderd.

Methode 1:

import sys
import json  ##  your module
for mod in [ m for m in sys.modules if m.lstrip('_').startswith('json') or sys.modules[m] == None ]: del sys.modules[mod]
print( json.dumps( [1] ) )  ##  test if functionality has been removed

Methode 2, waarbij gebruik wordt gemaakt van boekingen, om alleafhankelijkheden te verwijderen:

import sys
before_import = [mod for mod in sys.modules]
import json  ##  your module
after_import = [mod for mod in sys.modules if mod not in before_import]
for mod in [m for m in sys.modules if m in after_import or sys.modules[m] == None]: del sys.modules[mod]
print( json.dumps( [2] ) )  ##  test if functionality has been removed

Optioneel, om er zeker van te zijn dat alle inzendingen eruit zijn, als je dat wilt:

import gc
gc.collect()

Antwoord 19

Een andere manier zou kunnen zijn om de module in een functie te importeren. Op deze manier wordt de module, wanneer de functie is voltooid, verzameld.


Antwoord 20

Ik had veel problemen met het herladen van iets in Sublime Text, maar uiteindelijk kon ik dit hulpprogramma schrijven om modules op Sublime Text te herladen op basis van de code die sublime_plugin.pygebruikt om modules te herladen.

Hieronder accepteert u dat u modules herlaadt vanaf paden met spaties op hun naam, en later na het herladen kunt u gewoon importeren zoals u gewoonlijk doet.

def reload_module(full_module_name):
    """
        Assuming the folder `full_module_name` is a folder inside some
        folder on the python sys.path, for example, sys.path as `C:/`, and
        you are inside the folder `C:/Path With Spaces` on the file 
        `C:/Path With Spaces/main.py` and want to re-import some files on
        the folder `C:/Path With Spaces/tests`
        @param full_module_name   the relative full path to the module file
                                  you want to reload from a folder on the
                                  python `sys.path`
    """
    import imp
    import sys
    import importlib
    if full_module_name in sys.modules:
        module_object = sys.modules[full_module_name]
        module_object = imp.reload( module_object )
    else:
        importlib.import_module( full_module_name )
def run_tests():
    print( "\n\n" )
    reload_module( "Path With Spaces.tests.semantic_linefeed_unit_tests" )
    reload_module( "Path With Spaces.tests.semantic_linefeed_manual_tests" )
    from .tests import semantic_linefeed_unit_tests
    from .tests import semantic_linefeed_manual_tests
    semantic_linefeed_unit_tests.run_unit_tests()
    semantic_linefeed_manual_tests.run_manual_tests()
if __name__ == "__main__":
    run_tests()

Als je het voor de eerste keer uitvoert, zou dit de module moeten laden, maar als je later de methode/functie run_tests()opnieuw kunt gebruiken, zal het de testbestanden opnieuw laden. Met Sublime Text (Python 3.3.6) gebeurt dit vaak omdat de interpreter ervan nooit sluit (tenzij je Sublime Text opnieuw start, dwz de Python3.3-interpreter).

Other episodes