Sam Rockett
Sam Rockett

Reputation: 3205

Indicating that a parameter should be a mutable reference

With the type hinting syntax specified in PEP 484 and 585, is there any way to indicate that a function's parameter should be a mutable reference that would be modified by the function?

For instance, C# has ref paramters, so in Python, is there any equivalent? e.g.

>>> def foo(spam: "Mutable[List[int]]"):
...     spam.append(sum(spam))
...
>>> a = [1, 2, 3]
>>> foo(a)
>>> a
[1, 2, 3, 6]

or if not, how could I define such a type without causing the inspection logic to think that it was a special Mutable class instead of a List[int]? Obviously this would be used as a tool for the developer to understand a method more easily, instead of one that would be used to fundamentally change the program.

For clarity, I'm aware that Lists by definition are mutable, but I'm wondering if there is a way to define when it will be mutated, for example

>>> def bar(sandwich: Mutable[List[str]], fridge: List[str]):
...     sandwich.extend(random.sample(fridge, k=3))

Upvotes: 5

Views: 393

Answers (2)

Michael0x2a
Michael0x2a

Reputation: 64228

Lists, by default, are considered to always be mutable. So if you want to indicate some list will never be changed, it's better to indicate that explicitly by using some read-only interface or protocol such as typing.Sequence or typing.Collection instead.

These two types are meant to have the same semantics as the corresponding collections.abc types. I suppose you can kind of think of them as roughly the same as C#'s IReadOnlyCollection.

from typing import Sequence, overload

def foo(seq: Sequence[int]) -> None:
    # This type checks
    for item in seq:
        print(seq)

    # ...but this does not. Mypy reports a
    # '"Sequence[int]" has no attribute "append" error', for example
    seq.append(5)

# This type checks, since lists satisfy the Sequence protocol
foo([1, 2, 3, 4])

# Same thing with tuples
foo((1, 2, 3, 4))

class CustomSequence(Sequence[int]):
    @overload
    def __getitem__(self, i: int) -> int: ...
    @overload
    def __getitem__(self, s: slice) -> Sequence[int]: ...
    def __getitem__(self, x: Union[int, slice]) -> Union[int, Sequence[int]]:
        if isinstance(x, int):
            return 1
        else:
            return [1, 2]

    def __len__(self) -> int:
        return 1

# Or any other kind of Sequence, actually.
foo(CustomSequence())

And if you want a general-purpose mutable sequence, use MutableSequence. Note that lists satisfy both the Sequence and MutableSequence protocols.

Upvotes: 3

amanb
amanb

Reputation: 5473

Lists are mutable in Python and thus an explicit Mutable class reference is not required:

In [3]: from typing import List

In [7]: def foo(spam:List[int]):
   ...:     spam.append(sum(spam))
   ...:     return spam  

In [8]: a = [1,2,3]   

In [9]: foo(a)

Out[9]: [1, 2, 3, 6]

Upvotes: 3

Related Questions