Python, provide type hints for a mixin that a property exists

Question:

For example this mixin:

from lib import stringlib

class NiceNameMixin:
    @property
    def nice_first_name(self):
        return stringlib.clean_name(self.first_name)

    @property
    def nice_last_name(self):
        return stringlib.clean_name(self.last_name)

warns me:

Cannot access member "first_name" for type "NiceNameMixin" Member "first_name" is unknown.

What is the correct was of telling the type checker that the attributes self.first_name and self.last_name will exist, and it’s okay?

Note I normally just # type: ignore these kind of things, but I am starting to think there must be a more suitable way.

Note: Using Pyright

Update: Class property example for @chepner

from rest_framework import serializers

class UtilsMixin:
    @classproperty
    def model_class(cls: serializers.ModelSerializer):  # Type of parameter Cls must be a supertype of its class "UtilsMixin"
        return cls.Meta.model  # Cannot access member "Meta" for type "ModelSerializer"   Member "Meta" is unknown
Asked By: run_the_race

||

Answers:

You can simply add annotated, but uninitialized, names to the class:

class NiceNameMixin:
    first_name: str
    last_name: str

    @property
    def nice_first_name(self):
        return stringlib.clean_name(self.first_name)

    @property
    def nice_last_name(self):
        return stringlib.clean_name(self.last_name)

This does not guarantee that a class inheriting from NiceNameMixin will actually have those attributes, but it makes pyright happy with NiceNameMixin itself.

Answered By: chepner

Adding to the excellent answer by @chepner. If one wants to express with types, that the child class has to implement the first_name and last_name attributes, consider annotating self with an appropriate typing.Protocol.

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Protocol


class HasFirstAndLastName(Protocol):
    first_name: str
    last_name: str


class NiceNameMixin(ABC):
    @property
    def nice_first_name(self: HasFirstAndLastName) -> str:
        return self.first_name.capitalize()

    @property
    def nice_last_name(self: HasFirstAndLastName):
        return self.last_name.capitalize()


@dataclass
class User(NiceNameMixin):
    first_name: str
    last_name: str


@dataclass
class User2(NiceNameMixin):
    first_name: str


User("John", "Wick").nice_first_name  # OK
User2("John").nice_first_name  # Err
Answered By: Paweł Rubin
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.