#🔒 Dynamic `__init__` signature

9 messages · Page 1 of 1 (latest)

random moat
#

Hello! Is it possible to create dynamic __init__ signatures in Python, similar to those found in dataclasses.dataclass or pydantic.BaseModel? Here is what I have tried:

from inspect import Signature, Parameter
from typing import ClassVar


class Person:
    __signature__: ClassVar[Signature]

    def __init__(self, *args, **kwargs) -> None:
        return


Person.__signature__ = Signature(
    (
        Parameter(
            name="name",
            kind=Parameter.KEYWORD_ONLY,
            annotation=str
        ),
        Parameter(
            name="age",
            kind=Parameter.KEYWORD_ONLY,
            annotation=int
        ),
    ),
    return_annotation=Person
)

The creation of __signature__ would obviously be done in a metaclass, I'm just doing this as an example. The problem is, even though the signature is correctly returned when using inspect.signature, tools like pylance fail to detect the signature, and tools like mypy fail to recognize invalid calls (like Person(xyz=123)).

Something else I have tried is copying __code__ and __annotations__ from a dummy __init__ function (in a metaclass's __new__), but even that doesn't work.

fleet furnaceBOT
#

@random moat

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

junior tapir
# random moat Hello! Is it possible to create dynamic `__init__` signatures in Python, similar...

Your approach is probably possible but perhaps not in a way that a typechecker would recognize (you'd probably need to write a plugin for it). At least here, you would be able to just put it in the __init__:

class Person:
  def __init__(self, *, name: str, age: int) -> None: ...

Another way would be to use an Unpack and a TypedDict. However this would afaik only work for required keyword arguments.

from typing import Unpack

class PersonArgs(TypedDict):
    name: str
    age: int

class Person:
    def __init__(self, **kwargs: Unpack[PersonArgs]) -> None:
        print(args)
#

The approach that dataclasses take is to read the __annotations__ of the class, making "fields" for them and adding in the __init__ and __repr__ via some exec evaluation magic. You can make a dataclass-like class be recognized by a typechecker by using typing.dataclass_transform.

random moat
#

I see. Do you happen to know how Pydantic achieves this?

junior tapir
random moat
#

Gotcha

fleet furnaceBOT
#
Python help channel closed for inactivity

This help channel has been closed. Feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.