una resueltos programacion poo polimorfismo otra orientada objetos metodo llamar importar herencia ejercicios como clases clase python python-3.x nested bytecode code-inspection

python - resueltos - ¿Es posible acceder a funciones y clases internas a través de objetos de código?



poo python 3 (1)

A1: Cosas que pueden ayudarte son -

Constantes del objeto de código

De la documentación :

Si un objeto de código representa una función, el primer elemento en co_consts es la cadena de documentación de la función, o None si no está definido.

Además, si un objeto de código representa una clase, el primer elemento de co_consts es siempre el nombre calificado de esa clase. Puedes intentar usar esta información.

La siguiente solución funcionará correctamente en la mayoría de los casos, pero deberá omitir los objetos de código que Python crea para las comprensiones de la lista / conjunto / dict y las expresiones del generador:

from inspect import iscode for x in func.__code__.co_consts: if iscode(x): # Skip <setcomp>, <dictcomp>, <listcomp> or <genexp> if x.co_name.startswith(''<'') and x.co_name != ''<lambda>'': continue firstconst = x.co_consts[0] # Compute the qualified name for the current code object # Note that we don''t know its "type" yet qualname = ''{func_name}.<locals>.{code_name}''.format( func_name=func.__name__, code_name=x.co_name) if firstconst is None or firstconst != qualname: print(x, ''represents a function {!r}''.format(x.co_name)) else: print(x, ''represents a class {!r}''.format(x.co_name))

huellas dactilares

<code object a at 0x7fd149d1a9c0, file "<ipython-input>", line 2> represents a class ''a'' <code object a at 0x7fd149d1ab70, file "<ipython-input>", line 5> represents a function ''a'' <code object <lambda> at 0x7fd149d1aae0, file "<ipython-input>", line 6> represents a function ''<lambda>''

Banderas de código

Hay una forma de obtener la información requerida de co_flags . Citando la documentación que he vinculado anteriormente:

Los siguientes bits de marca se definen para co_flags : el bit 0x04 se establece si la función usa la sintaxis * arguments para aceptar un número arbitrario de argumentos posicionales; el bit 0x08 se establece si la función usa ** la sintaxis de las palabras clave para aceptar argumentos arbitrarios de palabra clave; bit 0x20 se establece si la función es un generador.

Otros bits en co_flags están reservados para uso interno.

Los indicadores se manipulan en compute_code_flags ( Python / compile.c ):

static int compute_code_flags(struct compiler *c) { PySTEntryObject *ste = c->u->u_ste; ... if (ste->ste_type == FunctionBlock) { flags |= CO_NEWLOCALS | CO_OPTIMIZED; if (ste->ste_nested) flags |= CO_NESTED; if (ste->ste_generator) flags |= CO_GENERATOR; if (ste->ste_varargs) flags |= CO_VARARGS; if (ste->ste_varkeywords) flags |= CO_VARKEYWORDS; } /* (Only) inherit compilerflags in PyCF_MASK */ flags |= (c->c_flags->cf_flags & PyCF_MASK); n = PyDict_Size(c->u->u_freevars); ... if (n == 0) { n = PyDict_Size(c->u->u_cellvars); ... if (n == 0) { flags |= CO_NOFREE; } } ... }

Hay 2 indicadores de código ( CO_NEWLOCALS y CO_OPTIMIZED ) que no se establecerán para las clases. Puede usarlos para verificar el tipo (no significa que deba hacerlo; los detalles de implementación mal documentados pueden cambiar en el futuro):

from inspect import iscode for x in complex_func.__code__.co_consts: if iscode(x): # Skip <setcomp>, <dictcomp>, <listcomp> or <genexp> if x.co_name.startswith(''<'') and x.co_name != ''<lambda>'': continue flags = x.co_flags # CO_OPTIMIZED = 0x0001, CO_NEWLOCALS = 0x0002 if flags & 0x0001 and flags & 0x0002: print(x, ''represents a function {!r}''.format(x.co_name)) else: print(x, ''represents a class {!r}''.format(x.co_name))

La salida es exactamente la misma.

Bytecode de la función externa

También es posible obtener el tipo de objeto inspeccionando el bytecode de la función externa.

Buscar instrucciones de bytecode para encontrar bloques con LOAD_BUILD_CLASS , significa la creación de una clase ( LOAD_BUILD_CLASS - Impulsa builtins .__ build_class __ () en la pila. Posteriormente CALL_FUNCTION lo llama para construir una clase ) .

from dis import Bytecode from inspect import iscode from itertools import groupby def _group(i): if i.starts_line is not None: _group.starts = i return _group.starts bytecode = Bytecode(func) for _, iset in groupby(bytecode, _group): iset = list(iset) try: code = next(arg.argval for arg in iset if iscode(arg.argval)) # Skip <setcomp>, <dictcomp>, <listcomp> or <genexp> if code.co_name.startswith(''<'') and code.co_name != ''<lambda>'': raise TypeError except (StopIteration, TypeError): continue else: if any(x.opname == ''LOAD_BUILD_CLASS'' for x in iset): print(code, ''represents a function {!r}''.format(code.co_name)) else: print(code, ''represents a class {!r}''.format(code.co_name))

La salida es la misma (de nuevo).

A2: Claro.

Código fuente

Para obtener el código fuente de los objetos de código, debe usar inspect.getsource o equivalente:

from inspect import iscode, ismethod, getsource from textwrap import dedent def nested_sources(ob): if ismethod(ob): ob = ob.__func__ try: code = ob.__code__ except AttributeError: raise TypeError(''Can/'t inspect {!r}''.format(ob)) from None for c in code.co_consts: if not iscode(c): continue name = c.co_name # Skip <setcomp>, <dictcomp>, <listcomp> or <genexp> if not name.startswith(''<'') or name == ''<lambda>'': yield dedent(getsource(c))

Por ejemplo, nested_sources(complex_func) (ver a continuación)

def complex_func(): lambda x: 42 def decorator(cls): return lambda: cls() @decorator class b(): def method(): pass class c(int, metaclass=abc.ABCMeta): def method(): pass {x for x in ()} {x: x for x in ()} [x for x in ()] (x for x in ())

debe proporcionar el código fuente para la primera lambda , decorator , b (incluido @decorator ) c :

In [41]: nested_sources(complex_func) Out[41]: <generator object nested_sources at 0x7fd380781d58> In [42]: for source in _: ....: print(source, end=''='' * 30 + ''/n'') ....: lambda x: 42 ============================== def decorator(cls): return lambda: cls() ============================== @decorator class b(): def method(): pass ============================== class c(int, metaclass=abc.ABCMeta): def method(): pass ==============================

Función y tipo objetos

Si aún necesita un objeto de función / clase, puede eval / exec el código fuente.

Ejemplo

  • para funciones lambda :

    In [39]: source = sources[0] In [40]: eval(source, func.__globals__) Out[40]: <function __main__.<lambda>>

  • para funciones regulares

    In [21]: source, local = sources[1], {} In [22]: exec(source, func.__globals__, local) In [23]: local.popitem()[1] Out[23]: <function __main__.decorator>

  • para clases

    In [24]: source, local = sources[3], {} In [25]: exec(source, func.__globals__, local) In [26]: local.popitem()[1] Out[26]: __main__.c

Digamos que hay una función func

def func(): class a: def method(self): return ''method'' def a(): return ''function'' lambda x: ''lambda''

que necesito examinar.

Como parte del examen, quiero "recuperar" el código fuente u objetos de todas las clases y funciones anidadas (si las hay). Sin embargo, me doy cuenta de que aún no existen y no hay una forma directa / limpia de acceder a ellos sin ejecutar func o definirlos fuera (antes) del func . Desafortunadamente, lo máximo que puedo hacer es importar un módulo que contenga func para obtener el objeto de función func .

Descubrí que las funciones tienen el atributo __code__ contiene el objeto de code , que tiene el atributo co_consts así que escribí esto:

In [11]: [x for x in func.__code__.co_consts if iscode(x) and x.co_name == ''a''] Out[11]: [<code object a at 0x7fe246aa9810, file "<ipython-input-6-31c52097eb5f>", line 2>, <code object a at 0x7fe246aa9030, file "<ipython-input-6-31c52097eb5f>", line 4>]

Esos objetos de code se ven muy similares y no creo que contengan datos necesarios para ayudarme a distinguir entre los tipos de objetos que representan (por ejemplo, type y function ).

P1: ¿Estoy en lo cierto?

P2: ¿Hay alguna forma de acceder a clases / funciones (ordinarias y lambdas) definidas dentro del cuerpo de la función?