lo necesitaba también a mí mismo. Esta es mi solución. El camino rápido no cubre la mayoría de los casos es probable que esté interesado.
def iterGlobalsUsedInFunc(f, fast=False, loadsOnly=True):
if hasattr(f, "func_code"): code = f.func_code
else: code = f
if fast:
# co_names is the list of all names which are used.
# These are mostly the globals. These are also attrib names, so these are more...
for name in code.co_names:
yield name
else:
# Use the disassembly. Note that this will still not
# find dynamic lookups to `globals()`
# (which is anyway not possible to detect always).
import dis
ops = ["LOAD_GLOBAL"]
if not loadsOnly:
ops += ["STORE_GLOBAL", "DELETE_GLOBAL"]
ops = map(dis.opmap.__getitem__, ops)
i = 0
while i < len(code.co_code):
op = ord(code.co_code[i])
i += 1
if op >= dis.HAVE_ARGUMENT:
oparg = ord(code.co_code[i]) + ord(code.co_code[i+1])*256
i += 2
else:
oparg = None
if op in ops:
name = code.co_names[oparg]
yield name
# iterate through sub code objects
import types
for subcode in code.co_consts:
if isinstance(subcode, types.CodeType):
for g in iterGlobalsUsedInFunc(subcode, fast=fast, loadsOnly=loadsOnly):
yield g
Una versión actualizada podría ser here.
Mi caso de uso:
tengo algún módulo (songdb
) que tiene algunos objetos de bases de datos globales y quería cargar con pereza una vez llamé a una función que utiliza la variable de base de datos global. Podría haber decorado manualmente tales funciones con un cargador lento o podría detectar automáticamente qué funciones lo necesitan con mi función iterGlobalsUsedInFunc
.
Esto es básicamente el código (full code; fue realmente extendieron para las clases de ahora), donde init
decora automáticamente tales funciones:
DBs = {
"songDb": "songs.db",
"songHashDb": "songHashs.db",
"songSearchIndexDb": "songSearchIndex.db",
}
for db in DBs.keys(): globals()[db] = None
def usedDbsInFunc(f):
dbs = []
for name in utils.iterGlobalsUsedInFunc(f, loadsOnly=True):
if name in DBs:
dbs += [name]
return dbs
def init():
import types
for fname in globals().keys():
f = globals()[fname]
if not isinstance(f, types.FunctionType): continue
dbs = usedDbsInFunc(f)
if not dbs: continue
globals()[fname] = lazyInitDb(*dbs)(f)
def initDb(db):
if not globals()[db]:
globals()[db] = DB(DBs[db])
def lazyInitDb(*dbs):
def decorator(f):
def decorated(*args, **kwargs):
for db in dbs:
initDb(db)
return f(*args, **kwargs)
return decorated
return decorator
Otra solución habría sido utilizar un proxy de objeto que carga con pereza la base de datos. Lo he usado en otro lugar de este proyecto, así que también he implementado dicho proxy de objeto; si está interesado, consulte aquí: utils.py: ObjectProxy
.
También utiliza 'inspeccionar', para la mayoría de las definiciones de" uso ". Si te refieres a algo más por "uso", sé específico. – delnan
El propósito es eliminar todas las cosas extra en globales. Hay una biblioteca principal que se importa a través de *, por lo que hay demasiadas variables globales para informar de todas ellas. Pregunta actualizada para realmente usar 'diversión'. –