For example, I might have an abstract base class with some abstract method that takes some mutable type as a parameter:
from abc import *
class AbstractClass(metaclass=ABCMeta):
@abstractmethod
def abstract_method(self, mutable_parameter: list | set):
raise NotImplementedError
Is there some way of hinting to the function implementer that this parameter should not be modified in any implementation of this method?
I imagine maybe something like ReadOnly
could exist:
def abstract_method(self, mutable_parameter: ReadOnly[list]):
but I can't seem to find anything suggesting such a thing does exist.
From looking at the typing module docs I would have assumed that Final
was what I am looking for but PyCharm tells me 'Final' could not be used in annotations for function parameters
.
You might be able to find a structural superclass that provides the behaviors you want. There's a good summary of the available collections classes at collections.abc
, but as a quick, non-exhaustive summary.
Iterable
.in
operator to check for membership, you can use Container
.Collection
is Iterable
plus Container
plus the len
function.Sequence
gives you Collection
, __getitem__
(i.e. the square brackets operator), and several other nice list-y things. This is a good general-purpose candidate for list-like structures.None of these protocols have any mutating methods, so if you adhere strictly to your types (and don't downcast), then you can be assured that a function taking these types will not mutate the argument.
Note that, prior to PEP 585 (released in Python 3.9), the collections.abc
classes were non-generic. If you need to be compatible with Python versions prior to that, the typing
module contains the same class names as collections.abc
, and those were generic since day one.
And, finally, the glory of structural superclasses is that you can define them anytime with typing.Protocol
. If you can't find a protocol that suits your needs, then just make one. Are you planning to call __getitem__
but want to support structures that don't have a len
? Here's your protocol!
from typing import Protocol, TypeVar
T = TypeVar("T", covariant=True)
class Gettable(Protocol[T]):
def __getitem__(self, index: int, /) -> T:
...
Since this class inherits from Protocol
, when used in type hints it will behave as a structural subtype, meaning any object which has a __getitem__
that takes an int
and returns a T
counts as a Gettable[T]
, even without an explicit runtime subtyping relationship.