0

In Python, is there any way to declare a class-like interface with a list of attributes, other than namedtuple? I want to make a mixin from a list of attribute names. My best attempt so far is:

# 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

This is pretty close, since IDEs recognize the attributes. But it also shows all the other attributes for NamedTuple and tuple (such as _asdict, etc).

I need users to define the attributes as a list of fields, not with regular class syntax. Firstly, for consistency with the rest of my framework. Secondly, using class syntax would open up a can of worms, since you can do all kinds of things in classes that are not permitted by my framework.

I also tried:

class CustomMixin:
  __slots__ = CUSTOM_FIELDS

But PyCharm doesn't recognize this. I also looked at typing.Protocol but it also requires declaring a class.

Chris
  • 300
  • 1
  • 6
  • 1
    [`dataclasses.make_dataclass()`](https://docs.python.org/3/library/dataclasses.html#dataclasses.make_dataclass) can do this; see the duplicate for a general overview of what the `dataclasses` module does. – Martijn Pieters Feb 18 '21 at 11:32
  • dataclass is introduced after Python3.7 – Menglong Li Feb 18 '21 at 11:36
  • @MartijnPieters So this is basically like `type(_, _, _)` but for dataclasses, right? – DeepSpace Feb 18 '21 at 11:36
  • 1
    @DeepSpace: it's a bit more involved than just `type()`; the inputs are validated, names without a type hint are given the `typing.Any` hint, and the `@dataclass()` decorator is applied to the result to generate methods like `__init__` and `__repr__`, depending on arguments used. – Martijn Pieters Feb 18 '21 at 11:50
  • 1
    @DeepSpace: and rather than call `type()`, the function uses [`types.new_class()`](https://docs.python.org/3/library/types.html#types.new_class), to handle custom metaclasses correctly. – Martijn Pieters Feb 18 '21 at 11:52
  • @MenglongLi: the [official backport](https://pypi.org/project/dataclasses/) supports Python 3.6. For older releases, use [`attrs`](https://pypi.org/project/attrs/), which supports Python 2.7, 3.5 and up. – Martijn Pieters Feb 18 '21 at 11:53

0 Answers0