Объявление класса/интерфейса Python с использованием списка имен атрибутов

Есть ли в Python способ объявить классоподобный интерфейс со списком атрибутов, отличных от namedtuple? Я хочу сделать миксин из списка имен атрибутов. Моя лучшая попытка на данный момент:

# module1.py (user facing)
CUSTOM_FIELDS = ['eye_color', 'shoe_size']


# module2.py (inside my framework)
from collections import namedtuple
from typing import Union


class BasePerson:
  name: Any
  age: Any

CustomMixin = namedtuple('CustomMixin', CUSTOM_FIELDS)

Person = Union[BasePerson, CustomMixin]


# module3.py (user facing)
def f(a: Person):
    # my objective is for IDEs like PyCharm to recognize a.eye_color and a.name
    a.eye_color
    a.name

Это довольно близко, так как IDE распознают атрибуты. Но он также показывает все остальные атрибуты для NamedTuple и кортежа (например, _asdict и т. д.).

Мне нужно, чтобы пользователи определяли атрибуты как список полей, а не с синтаксисом обычного класса. Во-первых, для согласованности с остальной частью моего фреймворка. Во-вторых, использование синтаксиса класса открыло бы банку червей, поскольку вы можете делать в классах все, что не разрешено моей структурой.

Я также пробовал:

class CustomMixin:
  __slots__ = CUSTOM_FIELDS

Но PyCharm этого не распознает. Я также посмотрел на typing.Protocol, но он также требует объявления класса.


person Chris    schedule 18.02.2021    source источник
comment
dataclasses.make_dataclass() может сделать это; см. дубликат для общего обзора того, что делает модуль dataclasses.   -  person Martijn Pieters    schedule 18.02.2021
comment
dataclass введен после Python3.7   -  person Menglong Li    schedule 18.02.2021
comment
@MartijnPieters Итак, это в основном похоже на type(_, _, _), но для классов данных, верно?   -  person DeepSpace    schedule 18.02.2021
comment
@DeepSpace: это немного сложнее, чем просто type(); входные данные проверяются, имена без подсказки типа получают подсказку typing.Any, а декоратор @dataclass() применяется к результату для создания таких методов, как __init__ и __repr__, в зависимости от используемых аргументов.   -  person Martijn Pieters    schedule 18.02.2021
comment
@DeepSpace: вместо вызова type() функция использует types.new_class(), чтобы правильно обрабатывать пользовательские метаклассы.   -  person Martijn Pieters    schedule 18.02.2021
comment
@MenglongLi: официальный бэкпорт поддерживает Python 3.6. Для более старых выпусков используйте attrs, который поддерживает Python 2.7, 3.5 и выше.   -  person Martijn Pieters    schedule 18.02.2021