pythonpython-typing

Is it possible to hint that a function parameter should not be modified?


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.


Solution

  • 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.

    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.