¿Cómo descargo (recargo) un módulo?


797

Tengo un servidor Python de larga ejecución y me gustaría poder actualizar un servicio sin reiniciar el servidor. ¿Cuál es la mejor manera de hacer esto?

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

53
Sugerencia de memorando: "importar" no significa "cargar", significa "cargar si aún no está cargado y luego importar al espacio de nombres".
Kos

3
la pregunta no debe incluir 'descargar' ya que eso no es posible en python todavía; sin embargo, la recarga es un paradigma conocido como se responde a continuación
robertmoggach

Tuve el mismo problema al usar un módulo dinámico en la aplicación py2exe. Como py2exe siempre mantiene el bytecode en el directorio zip, la recarga no funcionaba. Pero encontré una solución que funciona usando el módulo import_file. Ahora mi aplicación está funcionando bien.
Pritam Pan

2
¿Qué sucede si desea "descargar" porque el código está tratando de eliminar un archivo .pyc?
darkgaze

Respuestas:


794

Puede volver a cargar un módulo cuando ya se ha importado utilizando la reloadfunción incorporada (solo Python 3.4+) :

from importlib import reload  
import foo

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

En Python 3, reloadse movió al impmódulo. En 3.4, impquedó en desuso a favor de importlib, y reloadse agregó a este último. Cuando apunte a 3 o posterior, haga referencia al módulo apropiado al llamar reloado impórtelo.

Creo que esto es lo que quieres. Los servidores web como el servidor de desarrollo de Django usan esto para que pueda ver los efectos de los cambios en su código sin reiniciar el proceso del servidor.

Para citar de los documentos:

El código de los módulos de Python se vuelve a compilar y el código de nivel de módulo se vuelve a ejecutar, definiendo un nuevo conjunto de objetos que están vinculados a nombres en el diccionario del módulo. La función init de los módulos de extensión no se llama por segunda vez. Al igual que con todos los demás objetos en Python, los objetos antiguos solo se recuperan después de que sus recuentos de referencia caen a cero. Los nombres en el espacio de nombres del módulo se actualizan para señalar cualquier objeto nuevo o modificado. Otras referencias a los objetos antiguos (como los nombres externos al módulo) no se rebotan para referirse a los nuevos objetos y deben actualizarse en cada espacio de nombres donde ocurran si así se desea.

Como señaló en su pregunta, tendrá que reconstruir Fooobjetos si la Fooclase reside en el foomódulo.


10
En realidad, éste se reinicia el servidor de Django dev cuando se cambia un archivo .. (se reinicia el servidor, no sólo vuelve a cargar el módulo)
Hasen

25
¿De dónde viene esta función "is_changed"? No veo documentación y no se ejecuta en mi entorno Python 3.1.3, ni se ejecuta en 2.6.4.
jedmao

55
sin cdleary, Django no puede simplemente usar reload: pyunit.sourceforge.net/notes/reloading.html
raylu

14
@BartoszKP si Xno es un módulo, puedeimport sys; reload(sys.modules[X.__module__])
drevicko

55
@jedmao @JamesDraper Estoy bastante seguro de que la is_changedfunción es solo una función arbitraria que tendrías que escribir; No es un incorporado. Por ejemplo, podría abrir el archivo correspondiente al módulo que está importando y diferenciarlo con una versión en caché para ver si ha cambiado.
James Mchugh

252

En Python 3.0–3.3 usarías: imp.reload(module)

El BDFL ha respondido esta pregunta.

Sin embargo, impquedó en desuso en 3.4, a favor deimportlib (¡gracias @Stefan! ).

Yo pienso , por lo tanto, tendría ahora utiliza importlib.reload(module), aunque no estoy seguro.


23
El novato sincero está agradecido de conocer los matices críticos entre Python 2 y 3.
Smandoli

24
¿es válido imp.reload (imp)?
Loïc Faure-Lacroix

2
@ LoïcFaure-Lacroix de la misma manera reload(__builtins__)es válido en 2.x
JBernardo

1
@Tarrasch: es el módulo de Python que desea recargar, como en el ejemplo de la pregunta.
Paul D. Waite

3
@ LoïcFaure-Lacroix sí, imp puede recargarse.
Devyn Collier Johnson

92

Puede ser especialmente difícil eliminar un módulo si no es Python puro.

Aquí hay información de: ¿Cómo elimino realmente un módulo importado?

Puede usar sys.getrefcount () para averiguar el número real de referencias.

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

Los números mayores que 3 indican que será difícil deshacerse del módulo. El módulo "vacío" (que no contiene nada) de cosecha propia debe recolectarse basura después de

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

como la tercera referencia es un artefacto de la función getrefcount ().


44
Acabo de descubrir que si el módulo es parte de un paquete, también debe eliminarlo allí:setattr(package, "empty", None)
u0b34a0f6ae

66
Esta es la solución correcta, especialmente si tiene un paquete con módulos anidados. reload()solo vuelve a cargar el módulo superior y todo lo que contenga no se volverá a cargar a menos que primero lo elimine de sys.modules.
Cerin

73

reload(module), pero solo si es completamente independiente. Si algo más tiene una referencia al módulo (o cualquier objeto que pertenezca al módulo), obtendrá errores sutiles y curiosos causados ​​por el código antiguo que duró más de lo esperado, y cosas como isinstanceno funcionar en diferentes versiones del mismo código

Si tiene dependencias unidireccionales, también debe volver a cargar todos los módulos que dependen del módulo recargado para deshacerse de todas las referencias al código anterior. Y luego vuelva a cargar los módulos que dependen de los módulos recargados, de forma recursiva.

Si tiene dependencias circulares, lo cual es muy común, por ejemplo, cuando se trata de recargar un paquete, debe descargar todos los módulos del grupo de una vez. No puede hacer esto reload()porque reimportará cada módulo antes de que se actualicen sus dependencias, lo que permitirá que las referencias antiguas se introduzcan en módulos nuevos.

La única forma de hacerlo en este caso es hackear sys.modules, lo que no es compatible. Tendría que revisar y eliminar cada sys.modulesentrada que desea que se vuelva a cargar en la próxima importación, y también eliminar las entradas cuyos valores son Nonepara tratar un problema de implementación relacionado con el almacenamiento en caché de importaciones relativas fallidas. No es terriblemente agradable, pero siempre que tenga un conjunto de dependencias totalmente autónomo que no deje referencias fuera de su base de código, es viable.

Probablemente sea mejor reiniciar el servidor. :-)


1
¿No se descarga específicamente para ese escenario?
Josh

@ Josh: no, es para volver a cargar un árbol de paquetes, e incluso entonces solo funciona mientras el paquete no tenga dependencias externas / circulares.
bobince

1
¿Puede elaborar la parte con Nonevalores porque me encuentro exactamente con este problema: estoy eliminando elementos de sys.modulesy después de volver a importar algunas dependencias importadas None?
schlamar

@shclamar: Consulte stackoverflow.com/questions/1958417/… (y los enlaces desde allí) para obtener más información. Para mí no está claro (incluso mirando el código import.c) cómo las Noneentradas lograron regresar a través del mecanismo de importación cuando se eliminaron las entradas 'reales', y parece que no puedo hacer que suceda en 2.7; en el futuro ciertamente ya no es un problema ya que las importaciones relativas implícitas se han ido. Mientras tanto, eliminar todas las entradas con Nonevalor parece solucionarlo.
bobince

1
@Eliethesaiyan: ¿te refieres a la reloadfunción? Está integrado, no tiene que importar ninguna biblioteca.
bobince

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

3
+1. Mi objetivo era realizar pruebas de nariz dentro de Python. Después de haber cargado un módulo y renombrado algunas funciones, los nombres antiguos se mantuvieron al llamar nose.run(), incluso despuésreload(my_module) %run my_module
Peter D

55
Si su módulo importa sus propios submódulos, es posible que también deba eliminarlos. Algo así como [del(sys.modules[mod] for mod in sys.modules.keys() if mod.startswith('myModule.')].
drevicko

No creo que eso descargue el módulo. En Python 3.8: import sys; import json; del sys.modules['json']; print(json.dumps([1]))y el módulo json todavía funciona, aunque ya no está en sys.modules.
Seperman

60

Para Python 2, use la función incorporada reload () :

reload(module)

Para Python 2 y 3.2–3.3 use reload from module imp :

import imp
imp.reload(module)

Pero imp está en desuso desde la versión 3.4 a favor de importlib , así que use:

import importlib
importlib.reload(module)

o

from importlib import reload
reload(module)

2
para manejar cualquiera de estos casos: from six import reload_module(es necesario pip install sixprimero, por supuesto)
Anentropic

@Anentropic: es un buen consejo recomendar el uso de seis paquetes, pero la sintaxis es from six.moves import reload_module( doc )
x0s

23

El siguiente código le permite la compatibilidad con Python 2/3:

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

Puede usarlo como reload()en ambas versiones, lo que simplifica las cosas.


18

La respuesta aceptada no maneja el caso de X import Y. Este código lo maneja y el caso de importación estándar también:

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

En el caso de recarga, reasignamos los nombres de nivel superior a los valores almacenados en el módulo recién recargado, que los actualiza.


Notado un problema, globals () se refiere al módulo en el que define esta función, por lo que si lo define en un módulo diferente al que lo llama, esto no funciona.
Joseph Garvin

Para interactiva, luego >>> from X import Yde recargar do>>> __import__('X', fromlist='Y')
Bob Stein

@ BobStein-VisiBone, ¿hay alguna manera de hacer que eso funcione cuando fromlist='*'?
Mike C

Buena pregunta, no sé @MikeC. Por cierto, estoy tendiendo a dejar de usar casi todo fromen las declaraciones de importación. Simplemente import <package>paquete y símbolo explícito en el código. Tenga en cuenta que esto no siempre es posible o deseable. (Aquí hay una excepción: de la futura función print_import.)
Bob Stein

Mike C: lo que funciona para mí esfoo = reload(foo); from foo import *
rampion

16

Esta es la forma moderna de recargar un módulo:

from importlib import reload

Si desea admitir versiones de Python anteriores a la 3.5, intente esto:

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+

Para usarlo, ejecute reload(MODULE), reemplazando MODULEcon el módulo que desea volver a cargar.

Por ejemplo, reload(math)volverá a cargar el mathmódulo.


44
O simplemente hazlo from importlib import reload. Entonces puedes hacer reload(MODULE_NAME). No hay necesidad de esta función.
pausa

Creo que modulereload(MODULE_NAME)se explica más que solo reload(MODULE_NAME)y tiene menos posibilidades de entrar en conflicto con otras funciones.
Richie Bendall

@RichieBendall Lo siento, pero esta respuesta es completamente incorrecta. La función reload () toma el objeto del módulo, no el nombre del módulo ... Lea los documentos: docs.python.org/3/library/importlib.html#importlib.reload Y estoy de acuerdo con @ pault: esto "como modulereload" es superfluo .
mbdevpl

He cambiado mi respuesta para reflejar tu opinión.
Richie Bendall el

13

Si usted es no en un servidor, pero el desarrollo y la necesidad de recargar con frecuencia un módulo, he aquí una buena propina.

Primero, asegúrese de estar utilizando el excelente shell de IPython , del proyecto Jupyter Notebook. Después de instalar Jupyter, puede comenzar con ipythono jupyter console, o incluso mejor jupyter qtconsole, lo que le dará una buena consola coloreada con finalización de código en cualquier sistema operativo.

Ahora en su shell, escriba:

%load_ext autoreload
%autoreload 2

Ahora, cada vez que ejecute su script, sus módulos se recargarán.

Más allá del 2, hay otras opciones de la magia de autocarga :

%autoreload
Reload all modules (except those excluded by %aimport) automatically now.

%autoreload 0
Disable automatic reloading.

%autoreload 1
Reload all modules imported with %aimport every time before executing the Python code typed.

%autoreload 2
Reload all modules (except those excluded by %aimport) every time before
executing the Python code typed.

7

Para aquellos como yo que desean descargar todos los módulos (cuando se ejecutan en el intérprete de Python en Emacs ):

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

Más información se encuentra en los módulos de recarga Python .


En realidad, eso no parece funcionar de manera confiable (en 2.6) porque no todo sys.modules.values()es un módulo. Por ejemplo: >>> type (sys.modules.values ​​() [1]) <class 'email.LazyImporter'> Entonces, si trato de ejecutar ese código, se cae (Sé que no es una solución práctica, solo señalando eso).
Francis Davey

Ni siquiera funciona en pitones anteriores, como está escrito. Tuve que excluir algunos nombres. Actualizaré la publicación cuando mueva ese código a mi nueva computadora.

2
Funciona bien en Python 2.7 después de algunas modificaciones:if mod and mod.__name__ != "__main__": imp.reload(mod)
Czarek Tomczak

2
Esto funciona bien para mí: import imp [reload (m) for m en sys.modules.values ​​() if my not not " " in m .__ name and not imp.is_builtin (m .__ name__)]
Patrick Wolf

5

Enthought Traits tiene un módulo que funciona bastante bien para esto. https://traits.readthedocs.org/en/4.3.0/_modules/traits/util/refresh.html

Recargará cualquier módulo que se haya cambiado y actualizará otros módulos y objetos instanciados que lo estén utilizando. No funciona la mayoría de las veces con __very_private__métodos, y puede ahogarse con la herencia de clase, pero me ahorra una gran cantidad de tiempo al tener que reiniciar la aplicación host al escribir guiones PyQt, o cosas que se ejecutan dentro de programas como Maya o Nuke. No funciona tal vez el 20-30% del tiempo, pero sigue siendo increíblemente útil.

El paquete de Enthought no recarga los archivos en el momento en que cambian, debe llamarlo explícitamente, pero eso no debería ser tan difícil de implementar si realmente lo necesita


5

Aquellos que usan python 3 y recargan desde importlib.

Si tiene problemas, parece que el módulo no se recarga ... Eso se debe a que necesita un poco de tiempo para volver a compilar pyc (hasta 60 segundos) .Escribo esta sugerencia solo para saber si ha experimentado este tipo de problema.



3

Otra opción. Vea que el valor predeterminado de Python importlib.reloadsolo volverá a importar la biblioteca pasada como argumento. No volverá a cargar las bibliotecas que importa su lib. Si cambió muchos archivos y tiene un paquete algo complejo para importar, debe realizar una recarga profunda .

Si tiene instalado IPython o Jupyter , puede usar una función para volver a cargar todas las bibliotecas:

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

Si no tiene Jupyter, instálelo con este comando en su shell:

pip3 install jupyter

Tanto este dpyload como reload () de Ipython de importlib se quejan reload() argument must be module. Estoy usando una importación de función personalizada y parece que no funciona. El uso de módulos integrados funciona. :-( es una pérdida de tiempo volver a cargar iPython por cada pequeño cambio que hice en mi código ...
m3nda

2

Editar (Respuesta V2)

La solución anterior es buena solo para obtener la información de reinicio, pero no cambiará todas las referencias (más de reloadpero menos de lo requerido). Para configurar realmente todas las referencias también, tuve que ir al recolector de basura y reescribir las referencias allí. ¡Ahora funciona como un encanto!

Tenga en cuenta que esto no funcionará si el GC está apagado, o si vuelve a cargar datos que no son monitoreados por el GC. Si no quiere meterse con el GC, la respuesta original podría ser suficiente para usted.

Nuevo código:

import importlib
import inspect
import gc
from weakref import ref


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)

Respuesta original

Como está escrito en la respuesta de @ bobince, si ya hay una referencia a ese módulo en otro módulo (especialmente si se importó con la aspalabra clave like import numpy as np), esa instancia no se sobrescribirá.

Esto resultó bastante problemático para mí al aplicar pruebas que requerían un estado "limpio" de los módulos de configuración, así que escribí una función llamada reset_moduleque usa importlibla reloadfunción y sobrescribe recursivamente todos los atributos del módulo declarado. Ha sido probado con Python versión 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

Nota: ¡ Usar con cuidado! El uso de estos en módulos no periféricos (módulos que definen clases usadas externamente, por ejemplo) podría conducir a problemas internos en Python (como problemas de decapado / no decapado).


1

para mí para el caso de Abaqus es la forma en que funciona. Imagine que su archivo es Class_VerticesEdges.py

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'])

Esta respuesta es una copia directa de aquí: ebanshi.cc/questions/1942/…
SiHa

0

Tuve muchos problemas para intentar volver a cargar algo dentro de Sublime Text, pero finalmente pude escribir esta utilidad para recargar módulos en Sublime Text en función del código sublime_plugin.pyutilizado para recargar módulos.

Lo siguiente acepta que recargue módulos desde rutas con espacios en sus nombres, luego, después de volver a cargar, puede importar como lo hace normalmente.

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

Si ejecuta por primera vez, esto debería cargar el módulo, pero si luego puede volver a utilizar el método / función run_tests(), volverá a cargar los archivos de prueba. Con Sublime Text ( Python 3.3.6) esto sucede mucho porque su intérprete nunca se cierra (a menos que reinicie Sublime Text, es decir, el Python3.3intérprete).


-1

Otra forma podría ser importar el módulo en una función. De esta manera, cuando la función se completa, el módulo obtiene basura recolectada.


44
El módulo nunca obtendrá basura recolectada porque al menos se mantiene una referencia global sys.modules.
Todos los trabajadores son esenciales
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.