A1: Вещи, которые могут вам помочь:
Константы объекта кода
Из документации:
Если объект кода представляет функцию, первый элемент в co_consts является строкой документации функции или None, если он не определен.
Кроме того, если объект кода представляет класс, первый элемент co_consts
всегда является полным именем этого класса. Вы можете попробовать использовать эту информацию.
Следующее решение будет правильно работать в большинстве случаев, но вам придется пропустить объекты кода, которые Python создает для списков/наборов/слов и выражений генератора:
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))
отпечатки
<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>'
Флаги кода
Есть способ получить необходимую информацию от co_flags
. Ссылаясь на документацию, которую я связал выше:
Следующие флаговые биты определены для co_flags: бит 0x04 устанавливается, если функция использует синтаксис *arguments для приема произвольного количества позиционных аргументов; бит 0x08 устанавливается, если функция использует синтаксис **keywords для приема произвольных аргументов ключевого слова; бит 0x20 устанавливается, если функция является генератором.
Другие биты в co_flags зарезервированы для внутреннего использования.
Управление флагами осуществляется в 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;
}
}
...
}
Есть 2 флага кода (CO_NEWLOCALS
и CO_OPTIMIZED
), которые не будут установлены для классов. Вы можете использовать их для проверки типа (это не означает, что вы должны это делать — плохо документированные детали реализации могут измениться в будущем):
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))
Выход точно такой же.
Байт-код внешней функции
Также возможно получить тип объекта, проверив байт-код внешней функции.
Инструкции по поиску байт-кода для поиска блоков с LOAD_BUILD_CLASS
означают создание класса (LOAD_BUILD_CLASS
- Помещает встроенную функцию.__build_class__() в стек. Позже она вызывается CALL_FUNCTION для создания класса.)
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))
Результат тот же (снова).
А2: Конечно.
Исходный код
Чтобы получить исходный код для объектов кода, вы должны использовать inspect.getsource
< /a> или эквивалент:
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))
Например nested_sources(complex_func)
(см. ниже)
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 ())
должен предоставить исходный код для первых lambda
, decorator
, b
(включая @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
==============================
Объекты функций и типов
Если вам все еще нужен объект функции/класса, вы можете eval
/< a href="https://docs.python.org/3/library/functions.html#exec" rel="nofollow">exec
исходный код.
Пример
для lambda
функций:
In [39]: source = sources[0]
In [40]: eval(source, func.__globals__)
Out[40]: <function __main__.<lambda>>
для обычных функций
In [21]: source, local = sources[1], {}
In [22]: exec(source, func.__globals__, local)
In [23]: local.popitem()[1]
Out[23]: <function __main__.decorator>
для занятий
In [24]: source, local = sources[3], {}
In [25]: exec(source, func.__globals__, local)
In [26]: local.popitem()[1]
Out[26]: __main__.c
person
vaultah
schedule
16.09.2015