# Making functions subscriptable at runtime

**URL:** <https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463>\
**Category:** Ideas\
**Tags:** typing, builtins\
**Created:** [May 3, 2023, 8:21pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463 "2023-05-03T20:21:40Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Gobot1234](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gobot1234/32/7165_2.png) [@Gobot1234](https://discuss.python.org/u/Gobot1234)\
**Post date:** [May 3, 2023, 8:21pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/1 "2023-05-03T20:21:40Z")

</div>

I’d like to propose implementing `function. __getitem__ ` which returns a `GenericAlias` which should allow generic functions to be subscripted at runtime.

```py
def construct[T]() -> list[T]: ...

construct[int]() # currently would raise a TypeError

```

It’s probably worth mentioning that would require a small change to `GenericAlias. __call__ ` to avoid setting ` __orig_class__ ` on the returned object.

Thoughts?

---

<div class="post-metadata">

**Author:** ![youngchief](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/youngchief/32/12361_2.png) [@youngchief](https://discuss.python.org/u/youngchief)\
**Post date:** [May 3, 2023, 9:41pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/2 "2023-05-03T21:41:57Z")

</div>

I like the idea. What use cases do you have in mind?

---

<div class="post-metadata">

**Author:** ![Gobot1234](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gobot1234/32/7165_2.png) [@Gobot1234](https://discuss.python.org/u/Gobot1234)\
**Post date:** [May 3, 2023, 9:54pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/3 "2023-05-03T21:54:57Z")

</div>

At runtime this would do pretty much nothing, it just makes functions consistent with every other builtin class that is technically generic at type time.

---

<div class="post-metadata">

**Author:** ![tusharsadhwani](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tusharsadhwani/32/12278_2.png) [@tusharsadhwani](https://discuss.python.org/u/tusharsadhwani)\
**Post date:** [May 3, 2023, 10:28pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/4 "2023-05-03T22:28:33Z")

</div>

+1 from me. This is the same as the change for collection types in 3.9, so we should probably add this in sooner rather than later.

Is this not part of PEP695? It should’ve been IMO.

---

<div class="post-metadata">

**Author:** ![Gobot1234](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gobot1234/32/7165_2.png) [@Gobot1234](https://discuss.python.org/u/Gobot1234)\
**Post date:** [May 3, 2023, 10:38pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/5 "2023-05-03T22:38:58Z")

</div>

> [@tusharsadhwani](#):
>
> Is this not part of PEP695? It should’ve been IMO.

No, I don’t think it should have been included as it’s not really related to type-parameter syntax.

---

<div class="post-metadata">

**Author:** ![storchaka](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/storchaka/32/217_2.png) [@storchaka](https://discuss.python.org/u/storchaka)\
**Post date:** [May 3, 2023, 11:12pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/6 "2023-05-03T23:12:46Z")

</div>

What is the difference between `construct[int]()` and `construct[str]()`? How do you pass a type to a function which does not have parameters?

---

<div class="post-metadata">

**Author:** ![Gobot1234](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gobot1234/32/7165_2.png) [@Gobot1234](https://discuss.python.org/u/Gobot1234)\
**Post date:** [May 3, 2023, 11:25pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/7 "2023-05-03T23:25:49Z")

</div>

For all intents and purposes there is no difference and you’re right you can’t (for now, one day I’d like to be able to access the type parameter at runtime similarly to a normal parameter similar to how some other languages handle type parameters)

---

<div class="post-metadata">

**Author:** ![tusharsadhwani](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tusharsadhwani/32/12278_2.png) [@tusharsadhwani](https://discuss.python.org/u/tusharsadhwani)\
**Post date:** [May 4, 2023, 5:38am UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/8 "2023-05-04T05:38:11Z")

</div>

> [@storchaka](#):
>
> What is the difference between `construct[int]()` and `construct[str]()`

Nothing really, but the type hint here is important for type checkers to work.

It’s the same as how `x = list[str]()` was allowed in Python 3.9, the `str` does nothing at runtime but is important for the type system to be nice to work with.

---

<div class="post-metadata">

**Author:** ![merwok](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/merwok/32/29_2.png) [@merwok](https://discuss.python.org/u/merwok)\
**Post date:** [May 4, 2023, 3:09pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/9 "2023-05-04T15:09:35Z")

</div>

The proposal should have started with a use case and explain what the proposed construct means.

«Consistent» does not say anything, and also can’t be a goal in itself.  
«the type hint here is important for type checkers» in what way? what does the information mean / what do the type checkers do with it?

`list[int]` documents something as a list of ints, that’s more specific than `list`.  
`SomeClass[str]` adds a parameter that can be used in method signatures for example.

What does `function[str]` mean? Is it setting a local type variable that can be used in the return signature of the function? Or do you mean to annotate function calls like `function[str]()` and then what would it mean?

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [May 4, 2023, 4:33pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/10 "2023-05-04T16:33:53Z")

</div>

Funny, I was thinking of this same idea this morning. It makes sense to me, regardless of PEP 695. Say we have a generic function:

```py
T = TypeVar("T")

def foo(a: list[T]) -> T:
    ...

```

Now suppose we have another function that takes a callback like this:

```py
def bar(callback: Callable[[list[int]], int]):
    ...

```

If we want to call `bar(foo)` but express the type correctly we might want to write `bar(foo[int])`. (In this case I think most type checkers can infer that, but there are likely cases where they can’t.)

We might also want to be explicit about the type when calling `bar([])`, to indicate that we want a specific list type: `bar[int]([])`.

I think we can rewrite all these use cases easily enough without adding this feature, but in terms of types, I do think that just as we can have a generic class `C` that we can make specific by writing `C[int]`, we could have a generic function `f` that can be made specific by writing `f[int]`.

I don’t feel strongly about it though.

---

<div class="post-metadata">

**Author:** ![Pyprohly](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pyprohly/32/5768_2.png) [@Pyprohly](https://discuss.python.org/u/Pyprohly)\
**Post date:** [May 4, 2023, 4:46pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/11 "2023-05-04T16:46:08Z")

</div>

Explicitly specialising a generic function call is often not necessary for a type checker since they can usually infer the types of the generic type parameters from the given arguments. Unlike functions, classes often need their type parameters explicitly written since their constructors are essentially functions that return generic objects but don’t take generic parameters in which the type parameters can be inferred from.

In the given example, there are no generic parameters for `construct()` in which the type parameter can be inferred, so explicit specialisation is actually required here. But then it’s hard to write a meaningful implementation for some method if there are no generic arguments and you can’t access the type of the type parameters at runtime.

It’s usually a mistake to use a type parameter in a function signature if it only appears once, unless the type parameter is constrained, in which case you’re probably using the type parameter to help work around the fact that Python doesn’t have proper overloading. And if you’re doing overloading in Python, the ability to inspect the type parameter type at runtime is important.

I was just recently looking into how the the look of generic functions can be faked in Python in order to overload a method by return type. It can already be done nicely.

```python
from typing import TypeVar, Protocol, cast

Y = TypeVar('Y', int, str)

class Foo:
    class GenericOverload(Protocol[Y]):
        def __call__ (self) -> list[Y]: ...

    def __call__ (self) -> float:
        return 1.5

    def __getitem__ (self, key: type[Y]) -> GenericOverload[Y]:
        try:
            v = {
                int: self.y_int,
                str: self.y_str,
            }[key]
            return cast(" __class__.GenericOverload[Y]", cast(object, v))
        except KeyError as e:
            raise TypeError from e

    def y_int(self) -> list[int]:
        return [1, 2, 3]

    def y_str(self) -> list[str]:
        return ['a', 'b', 'c']

foo = Foo()

print(foo()) # 1.5
print(foo[int]()) # [1, 2, 3]
print(foo[str]()) # ['a', 'b', 'c']

```

I don’t mind the suggestion but I doubt it’s going to be used very much. It could be useful if you for some reason wanted to indicate that the concrete type parameter type for a particular function call is important and should not be changed, i.e., accidentally.

---

<div class="post-metadata">

**Author:** ![ntessore](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ntessore/32/10786_2.png) [@ntessore](https://discuss.python.org/u/ntessore)\
**Post date:** [May 4, 2023, 5:40pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/12 "2023-05-04T17:40:05Z")

</div>

This would also be nice for functions where the return type depends on the value of a passed argument. Without PEP 695, I imagine it could be explicitly genericised with an `@generic[T]` or `@genericfunction[T]` decorator.

Imagine a fictional array library:

```py
@generic[T]
def zeros(shape: tuple[int, ...], dtype: DType) -> array[T]:
    ...

a = zeros[int]((4, 4), DType.INT)

```

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [May 4, 2023, 6:38pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/13 "2023-05-04T18:38:10Z")

</div>

There’s no need to make up new syntax to define a generic function. You can already write this:

```py
T = TypeVar("T")
def zeros(shape: tuple[int, ...], dtype: DType) -> array[T]:
    ...

```

You just can’t write the following yet, and it would be nice if you could – that’s the proposal here:

```py
a = zeros[int]((4, 4), DType.INT)

```

(There’s a question about generic functions using their type variable in only one position – that applies to argument types, not to retun types.)

---

<div class="post-metadata">

**Author:** ![Gobot1234](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gobot1234/32/7165_2.png) [@Gobot1234](https://discuss.python.org/u/Gobot1234)\
**Post date:** [May 17, 2023, 8:06pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/14 "2023-05-17T20:06:40Z")

</div>

So it seems no one is particularly opposed to this. Would this need a PEP or could it just be a standard change with an issue?

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [May 17, 2023, 8:29pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/15 "2023-05-17T20:29:12Z")

</div>

Asking the question, alas, is answering it – you probably need a PEP. That PEP should also answer the question “why wasn’t this done at the time PEP 585 was written” and “why are we no longer satisfied with that decision”.

---

<div class="post-metadata">

**Author:** ![Gobot1234](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gobot1234/32/7165_2.png) [@Gobot1234](https://discuss.python.org/u/Gobot1234)\
**Post date:** [May 17, 2023, 8:41pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/16 "2023-05-17T20:41:42Z")

</div>

I’m gonna guess the answer is no but do you have any ideas or discussion on the former or should I get in touch with @ambv?

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [May 17, 2023, 9:09pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/17 "2023-05-17T21:09:06Z")

</div>

I don’t recall, there may be traces of this in the public discussion around PEP 585 but they may be hard to find (some of it on typing-sig for sure). So asking if @ambv recalls is totally fair.

My own guess is that we probably didn’t have much of a use case for this. While it’s essential to be able to write `a: list[int]`, you can’t write `f: foo[int]` (since `foo` isn’t a type, it’s a specific generic function). And writing `list[int](a)`, while allowed, is very rare – similarly, `foo[int](args)` would be very rare, and more likely resolved by forcing the type of one of the arguments.

---

<div class="post-metadata">

**Author:** ![ambv](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ambv/32/25884_2.png) [@ambv](https://discuss.python.org/u/ambv)\
**Post date:** [May 18, 2023, 3:43pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/18 "2023-05-18T15:43:22Z")

</div>

There isn’t any particular reason why PEP 585 doesn’t propose making function objects runtime-subscriptable, too. The use case for this didn’t appear to me at the time as a function isn’t a type.

Funnily enough, at some point in the past, I **did** suggest that treating functions like types would make for very nice typing of complex callables. The arguments that killed the idea at the time were:

- actually, a function is an instance and we want a type, which we don’t have a way to express in Python;
- if we made a `CallableLike[some_func]` construct to solve the problem above, we would put possibly irrelevant details about the function signature into the required callable type; for example: what if we don’t care about the names of the positional arguments?

As for my current thoughts: given Guido’s concrete example it makes sense for me to have this.

---

<div class="post-metadata">

**Author:** ![NeilGirdhar](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/neilgirdhar/32/5776_2.png) [@NeilGirdhar](https://discuss.python.org/u/NeilGirdhar)\
**Post date:** [May 18, 2023, 4:06pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/19 "2023-05-18T16:06:42Z")

</div>

> [@ambv](#):
>
> if we made a `CallableLike[some_func]` construct

Something like this would be pretty useful for eliminating wordy protocols.

---

<div class="post-metadata">

**Author:** ![storchaka](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/storchaka/32/217_2.png) [@storchaka](https://discuss.python.org/u/storchaka)\
**Post date:** [May 18, 2023, 6:02pm UTC](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463/20 "2023-05-18T18:02:33Z")

</div>

> [@guido](#):
>
> There’s no need to make up new syntax to define a generic function.

Why then generic classes need to be inherited from `Generic[]`? I think that a special decorator is needed to distinguish free variables from bound variables.

```auto
@genericfunction[T]
def f(a: str, b: U) -> dict[T, U]:
    ...
x: dict[int, str] = f[str]('spam', 42)

```

No need to make functions subscriptable. It only needed to add `genericfunction`, so that `genericfunction[]()` returns a subscriptable object. I think it is easy.

[Next page](https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463.md?page=2)
