Python 3.7 представляет модуль dataclasses
, который содержит декоратор @dataclass
. Этот декоратор может генерировать функции класса. Как я могу распечатать эти сгенерированные функции?
Как показать код, сгенерированный при использовании декоратора класса @dataclass?
Ответы (2)
Я сам задавал тот же вопрос. Часть проекта Dataclasses
должна иметь параметр verbose, но не .
Нашел это полезное видео. Судя по видео, dataclasses.py — это генератор кода. Так что это должно быть нашей первой идеей, как получить код.
Я пробовал этот код:
from dataclasses import dataclass
import inspect
import os
from uncompyle6 import PYTHON_VERSION, deparse_code2str
@dataclass
class C:
name: str
value: int = 34
inspect.getmembers(C) #('__init__', <function __init__(self, name: str, value: int = 34) -> None>)
co= C.__init__.__code__ # let's find out code for the __init__ method from code object
code = deparse_code2str(
code=co,
version=PYTHON_VERSION,
out=open(os.devnull, "w"))
print(code)
Будет печатать
self.name = name
self.value = value
Код фактически использует инспектор для понимания класса, а затем для декомпиляции методов с помощью декомпилятора Python.
Вот обнаруженные методы:
def __eq__(self, other):
if other.__class__ is self.__class__:
return (self.name, self.value) == (
other.name,
other.value,
)
else:
return NotImplemented
def __init__(self, name: str, value: int = 34) -> None:
self.name = name
self.value = value
def __repr__(self):
key = (id(self), _thread.get_ident())
if key in repr_running:
return "..."
else:
repr_running.add(key)
try:
result = user_function(self)
finally:
repr_running.discard(key)
return result
На самом деле существует проект, который выполняет обнаружение @dataclass. Я установил его, и он работал.
from dataclasses import dataclass
import inspect
import os
import dis
from DataclassInspector.inspector import Inspector
@dataclass
class C:
name: str
value: int = 34
inspected = Inspector(C)
print(inspected._generate_code())
При условии, что вывод выглядит следующим образом:
from dataclasses import Field, _MISSING_TYPE, _DataclassParams
class C:
__dataclass_fields__ = {'name': "Field(name='name', type=str, default=_MISSING_TYPE, default_factory=_MISSING_TYPE, init=True, repr=True, hash=None, compare=True, metadata={}, _field_type=_FIELD)", 'value': "Field(name='value', type=int, default=34, default_factory=_MISSING_TYPE, init=True, repr=True, hash=None, compare=True, metadata={}, _field_type=_FIELD)"}
__dataclass_params__ = _DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=False)
name: str
value: int = 34
def __eq__(self, other):
if other.__class__ is self.__class__:
return (self.name, self.value) == (other.name, other.value)
else:
return NotImplemented
__hash__ = None
def __init__(self, name: str, value: int = 34) -> None:
self.name = name
self.value = value
def __repr__(self):
key = (
id(self), _thread.get_ident())
if key in repr_running:
return '...'
else:
repr_running.add(key)
try:
result = user_function(self)
finally:
repr_running.discard(key)
return result
После проверки реализации класса данных методы, похоже, сгенерированы dataclasses._create_fn
. Чтобы получить исходный сгенерированный код, я издевался над функцией как:
import dataclasses
_original_create_fn = dataclasses._create_fn
def _new_create_fn(name, args, body, **kwargs):
args_str = ', '.join(args)
body_str = '\n'.join(' ' + l for l in body)
print(f'def {name}({args_str}):\n{body_str}\n')
return _original_create_fn(name, args, body, **kwargs)
dataclasses._create_fn = _new_create_fn
@dataclasses.dataclass
class A:
x: int
y: int
Которые отображают что-то вроде:
def __init__(self, x:_type_x, y:_type_y):
self.x=x
self.y=y
def __repr__(self):
return self.__class__.__qualname__ + f"(x={self.x!r}, y={self.y!r})"
def __eq__(self, other):
if other.__class__ is self.__class__:
return (self.x,self.y,)==(other.x,other.y,)
return NotImplemented
dis.dis
- person Patrick Haugh   schedule 19.11.2018str(inspect.signature(YourDataClass.__init__))
- person x0s   schedule 29.11.2018