# PEP 718: subscriptable functions

**URL:** <https://discuss.python.org/t/pep-718-subscriptable-functions/28457>\
**Category:** PEPs\
**Tags:** typing, builtins\
**Created:** [June 24, 2023, 9:18pm UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457 "2023-06-24T21:18:14Z")\
**Posts on this page:** 20\
**Page:** 5

<div class="post-metadata">

**Author:** ![rsdenijs](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rsdenijs/32/21739_2.png) [@rsdenijs](https://discuss.python.org/u/rsdenijs)\
**Post date:** [October 27, 2025, 4:21pm UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/82 "2025-10-27T16:21:18Z")

</div>

This may be out of scope for this PEP, but has the use case of specializing generic functions been discussed? I have a generic implementation that takes various type variables, but parts of the application only use a fixed value for that type variable and I want to save users from having to know the underlying implementation is generic. I essentially want an alias for the function but with some type vars clamped.  
Example

````python
def mygenericfun[A, B](a: A, b:B) -> A: ...

## Specialization (desired)
myfun[B] = mygenericfun[str, B]

# Users import `myfun` not `mygenericfun`
### LSPs/type checkers/linters now can show a signature for myfun as myfun[B](a: str, B) -> str ```

````

Currently this possible with possible with classes through inheritance and to some degree with type aliases. AFAIK for functions the only solution is to write a full wrapper.

EDIT: Added an example [here](https://discuss.python.org/t/is-it-ok-to-use-async-new/104579/3)

---

<div class="post-metadata">

**Author:** ![PabloRuizCuevas](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pabloruizcuevas/32/17298_2.png) [@PabloRuizCuevas](https://discuss.python.org/u/PabloRuizCuevas)\
**Post date:** [October 28, 2025, 11:01am UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/83 "2025-10-28T11:01:30Z")

</div>

> [@rsdenijs](#):
>
> have a generic implementation th

Great observation, I didn’t realize that this was a thing. But that function[TypeVar] returns a GenericAlias I think it is already part of this PEP because it is how it works in existing constructors:

```python
my_list = list[int]
a = my_list([]) # a: list[int]
type(my_list) # types.GenericAlias

```

We can also create a partial generic of dict:

```python
my_dict = dict[str] # works but some lints complain, dict[str,Any] better
a = my_dict() # a: dict[str, Unknown]. or dict[str, Any]
type(my_dict) # types.GenericAlias

```

Your “generic specialization” is also possible and already allowed but the TypeVar B as of now needs to be defined beforehand:

```python
from typing import TypeVar

B = TypeVar("B")
my_specialized_dict = dict[str, B]
my_very_specialized_dict = my_specialized_dict[int]

a = my_very_specialized_dict() # a: dict[str, int]

```

Are you proposing to allow defining TypeVars in assignations?

`my_specialized_dict[B] = dict[str, B]`

For me this looks slightly odd.

---

<div class="post-metadata">

**Author:** ![rsdenijs](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rsdenijs/32/21739_2.png) [@rsdenijs](https://discuss.python.org/u/rsdenijs)\
**Post date:** [October 28, 2025, 11:22am UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/84 "2025-10-28T11:22:15Z")

</div>

> [@PabloRuizCuevas](#):
>
> Your “generic specialization” is also possible and already allowed but the TypeVar B as of now needs to be defined beforehand:

It works ~~to create an alias, but ideally there is a way to create something callable too~~

> [@PabloRuizCuevas](#):
>
> Are you proposing to allow defining TypeVars in assignations?

I don’t know what the syntax should be. But I think specializing a function would be nice. The workaround is to create classes and abuse ` __new__ `, and use pyright since mypy is very opinionated on what ` __new__ ` is allowed to do. This works today

```python
class my_generic_function[A, B]:
    def __new__ (cls, a: A, b: B) -> tuple[A, B]:
        return a,b

class my_specialized_function[B](my_generic_function[str, B]): ...

my_specialized_function('hello', 1) # OK
my_specialized_function(1, 1) # Pyright correctly complains

```

but maybe there are more idiomatic solutions.

---

<div class="post-metadata">

**Author:** ![PabloRuizCuevas](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pabloruizcuevas/32/17298_2.png) [@PabloRuizCuevas](https://discuss.python.org/u/PabloRuizCuevas)\
**Post date:** [October 28, 2025, 11:26am UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/85 "2025-10-28T11:26:51Z")

</div>

what is wrong with the example i gave you? (the one of `my_specialized_dict`) of course it will only work when PEP718 is approved or using any of the workarounds you can read in this thread: callable class or typeform.

---

<div class="post-metadata">

**Author:** ![rsdenijs](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rsdenijs/32/21739_2.png) [@rsdenijs](https://discuss.python.org/u/rsdenijs)\
**Post date:** [October 28, 2025, 11:33am UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/86 "2025-10-28T11:33:26Z")

</div>

> what is wrong with the example i gave you?

Nothing. You are right. If after this PEP regular functions behave like list/dict then we can specialize generic functions to domains.

---

<div class="post-metadata">

**Author:** ![rsdenijs](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rsdenijs/32/21739_2.png) [@rsdenijs](https://discuss.python.org/u/rsdenijs)\
**Post date:** [October 28, 2025, 1:17pm UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/87 "2025-10-28T13:17:31Z")

</div>

I had not considered using a traditional TypeVar. Given that this PEP would enable support for specializing functions, I think this use case should be added explicitly to the PEP. This is not something “smarter linters” could provide.

---

<div class="post-metadata">

**Author:** ![PabloRuizCuevas](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pabloruizcuevas/32/17298_2.png) [@PabloRuizCuevas](https://discuss.python.org/u/PabloRuizCuevas)\
**Post date:** [October 28, 2025, 2:11pm UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/88 "2025-10-28T14:11:55Z")

</div>

I also think it is interesting to document this already @Gobot1234, plus possibly the usage in generators. and maybe a discussion on the alternatives that were found.

I think for this usage to works we only depends on the linter knowing subscription on functions will return a GenericAlias so I think it would probably be straight forward.

---

<div class="post-metadata">

**Author:** ![PabloRuizCuevas](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pabloruizcuevas/32/17298_2.png) [@PabloRuizCuevas](https://discuss.python.org/u/PabloRuizCuevas)\
**Post date:** [November 13, 2025, 2:55am UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/89 "2025-11-13T02:55:13Z")

</div>

Hi! all, I will be helping coauthoring the last changes on this PEP as @Gobot1234 currently has little time to finish it.

I have already some examples to motivate this PEP (the ones affect my use-cases), but just for completeness, if anyone has more examples, both easy and advanced, or examples that affect important libraries please let me know.

Special mention to @rsdenijs, @jorenham, @mikeshardmind as you looked somewhat interested on this.

Besides that any help is welcomed as I didn’t participate before in writing a PEP.

---

<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:** [November 13, 2025, 3:25am UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/90 "2025-11-13T03:25:45Z")

</div>

@PabloRuizCuevas I want to thank you for the help here 🙂

I think one of the most important ones that I have missed is the ability to skip a cast where you know an ABC ahead of time (this example is from discord.py and was something I found myself trying quite naturally

```python
    channel = guild.get_channel[discord.TextChannel](1132731894954475561)
    message = await channel.fetch_message(1430262684795928798)

```

---

<div class="post-metadata">

**Author:** ![tmk](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tmk/32/10935_2.png) [@tmk](https://discuss.python.org/u/tmk)\
**Post date:** [November 16, 2025, 1:46pm UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/91 "2025-11-16T13:46:16Z")

</div>

Sorry to drag out the discussion more here, but I’ve been thinking about this PEP in connection with this recent idea (basically, a DSL for typing): [Idea: Simpler and More Expressive Type Annotations](https://discuss.python.org/t/idea-simpler-and-more-expressive-type-annotations/104730/) and the newly accepted PEP on lazy imports. For both it would be great if the type parameter to a function call could be treated as special by the compiler. This would require specialized syntax and couldn’t just rely on ` __getitem__ `.

For the sake of this discussion, let’s assume this syntax (inspired by Rust):

```python
def make_list[T](*args: T) -> list[T]: ...

lst = make_list::[int]()

```

**Relevance to lazy imports**

We could then write something like this

```python
lazy from collections.abc import Sequence

list_of_sequences = make_list::[Sequence[int]]()

```

and the import can be skipped because we would have deferred evaluation for the type parameter.

**Relevance to Imogen’s proposal**

In [Imogen’s proposal](https://discuss.python.org/t/idea-simpler-and-more-expressive-type-annotations/104730), `tuple[int, str]` can be written as `(int, str)` as long as it’s in a place where the compiler knows that only type annotations may appear. This would then allow

```python
list_of_tuples = make_list::[(int, str)]()

```

which couldn’t be supported with the ` __getitem__ ` syntax because it can’t be treated differently by the compiler.

**As a competitor to PEP 747**

[PEP 747](https://peps.python.org/pep-0747/) enables functions that take complex type expressions as runtime parameters. This PEP could fill the same role if the value of the type parameter could be made available in the function body at runtime. This would then allow writing function like this:

```python
def checkcast[T](value: object) -> T:
    typ = T. __value__ # (or something like that)
    assert isinstance(value, typ)
    return value

```

which can be called like this

```python
an_int = checkcast::[int](maybe_an_int)

```

I think this wouldn’t cover _all_ the use cases of PEP 747, but a large majority of them.

---

<div class="post-metadata">

**Author:** ![rsdenijs](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rsdenijs/32/21739_2.png) [@rsdenijs](https://discuss.python.org/u/rsdenijs)\
**Post date:** [November 16, 2025, 7:07pm UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/92 "2025-11-16T19:07:51Z")

</div>

Maybe a simple example, let me know if this is useful.

```python
# Generic function for creating email messages. Signatures with type variables and bounds can be intimidating to non-expert users. 
def create_email[T: Hashable, S: DestinationType](content: T, destination: S) -> Message[T, S]: ...

# In practice, in one part of the code where we only create simple email messages. Thus we setup a GenericAlias.
create_regular_email = create_email[str, str]

# In a different part of the code we always send rich emails to multiple senders with some custom types
create_rich_bulk_email = create_email[RichString, EmailList]

# When operating in these domains, users see restricted signatures in the IDE and when validating the calls through the static type checker

create_email(content: str, destination: str) -> Message[str, str]
create_rich_bulk_email(content: RichString, destination: EmailList) -> Message[RichString, EmailList]

```

This use case can’t be solved with smarter linters, and already works today with classes. IMO functions deserve the same level of support.

---

<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:** [November 17, 2025, 1:17am UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/93 "2025-11-17T01:17:29Z")

</div>

I really hope this makes it in. It solves a real pain point for some scenarios, and adds helpful consistency between class calls and function calls (both of which may invoke generics, but only class currently can specify the type variables).

---

<div class="post-metadata">

**Author:** ![PabloRuizCuevas](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pabloruizcuevas/32/17298_2.png) [@PabloRuizCuevas](https://discuss.python.org/u/PabloRuizCuevas)\
**Post date:** [November 17, 2025, 11:10am UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/94 "2025-11-17T11:10:31Z")

</div>

@samwgoldman did you find the agreement, I’m now working on this part, and prefer to have your informed opinion.

For now I think it makes sense that is possible to define the type of the method (cls,static) even without defining the one of the class (letting it generic)

---

<div class="post-metadata">

**Author:** ![JoBe](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jobe/32/26961_2.png) [@JoBe](https://discuss.python.org/u/JoBe)\
**Post date:** [November 19, 2025, 8:04pm UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/95 "2025-11-19T20:04:33Z")

</div>

Type-annotations are already lazily evaluated in 3.14+, so some `x: Sequence[...]` works, even if I don’t (yet) have `Sequence` imported.

This might also be transferred to subscription of function, where the ` __getitem__ ` function of some `function` object (if that would exist at runtime) just returns the function itself. That would mean, that some `x[...] = x` (or a copy of `x`, with ` __getitem__ set to none now, so `x`can't be subscripted twice). So if`x[…] == x`, why not make `x[…]`not even call` **getitem** `. Because if we do that, any expression (with valid syntax) can be passed, and your concerns (and new syntax?!) are addressed.

Also, this would probably be worth a new topic, if you want to go after this idea further. It combines multiple ideas unrelated to this topic itself. Generally though, I find it unlikely that a new syntax is added for lazy evaluation of type hints (which can also be done with `ForwardRef`s). And the idea of evaluating the expression within `[...]` lazily, can work, if we make functions special in that case, which would mean that neither new syntax, nor new attributes or similar would be required, and existing code would still work.

---

<div class="post-metadata">

**Author:** ![ilevkivskyi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ilevkivskyi/32/26_2.png) [@ilevkivskyi](https://discuss.python.org/u/ilevkivskyi)\
**Post date:** [February 24, 2026, 11:59pm UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/96 "2026-02-24T23:59:30Z")

</div>

I somehow completely missed this PEP until now. Just wanted to add that I am a strong supporter of this idea. There are many cases where type inference is ambiguous and type checkers are forced to rely on various heuristics (often inconsistent between type checkers).

An additional benefit is that this gives type-checkers more flexibility w.r.t. how to handle variables with and without annotations. For example, use traditional/rigid “variable“ semantics for variables with annotations, and use more pythonic/lenient “name“ semantics for variables without annotations. Currently this option is limited, because users are forced to add annotations in situations with ambiguous inference.

---

<div class="post-metadata">

**Author:** ![PabloRuizCuevas](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pabloruizcuevas/32/17298_2.png) [@PabloRuizCuevas](https://discuss.python.org/u/PabloRuizCuevas)\
**Post date:** [April 24, 2026, 5:20am UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/97 "2026-04-24T05:20:31Z")

</div>

PEP 718 has been [updated](https://peps.python.org/pep-0718/). The PEP now includes additional examples, justifications and binding rules.

We believe this PEP is complementary to the recently accepted TypeForm (PEP 747). TypeForm addresses cases where types need to be inspected or passed as values at runtime, while PEP 718 addresses a different problem: giving a consistent and ergonomic way to guide the type checker where inference is ambiguous or unsolvable:

```python
# Already works for classes
list[int]() # list[int]

# PEP 718: the same consistency for functions
make_list[int]() # list[int]

```

Unlike TypeForm or callable class workarounds, PEP 718 works universally at runtime for all existing functions, including stdlib and third-party libraries — with no changes to their source code. Enabling full type inference for specific functions may still require typeshed stub updates, but that is significantly lighter work than rewriting functions as callable classes or extra arguments.

The proposal has received strong support from Guido van Rossum (sponsor) and Ivan Levkivskyi, and has been discussed extensively with no fundamental technical objections to the runtime mechanism itself.

We would appreciate the Typing Council’s recommendation on PEP 718.

---

<div class="post-metadata">

**Author:** ![Jelle](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jelle/32/1049_2.png) [@Jelle](https://discuss.python.org/u/Jelle)\
**Post date:** [April 24, 2026, 1:16pm UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/98 "2026-04-24T13:16:45Z")

</div>

Please open an issue on [Issues · python/typing-council · GitHub](https://github.com/python/typing-council/issues) to get the Typing Council’s recommendation.

---

<div class="post-metadata">

**Author:** ![davidhalter](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/davidhalter/32/2935_2.png) [@davidhalter](https://discuss.python.org/u/davidhalter)\
**Post date:** [April 25, 2026, 11:49pm UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/99 "2026-04-25T23:49:33Z")

</div>

I’m definitely for accepting this PEP. I feel like it makes Python more consistent and therefore better.

I feel like there is a small oversight in overloads that might need to be clarified. There is the example:

```python
@overload
def serializer_for[T: str]() -> StringSerializer: ...
@overload
def serializer_for[T: list]() -> ListSerializer: ...

def serializer_for(): ...

```

At runtime serializer\_for does now not have any type parameters. This is a problem, because the implementation is not actually subscript-able (unless you plan to make non-generic functions subscript-able, which I would oppose). I’m not sure there is a great solution for this. Type checkers can error here, but this is not specified in the spec. Should this get a bit more of an explanation in the PEP?

Is there a plan how the transition of builtin functions with function/method generics (e.g. `def aiter(async_iterable: SupportsAiter[_SupportsAnextT_co], /) -> _SupportsAnextT_co: ...`) works? I’m generally a bit worried that if typeshed (partially) migrates to this new syntax, which then seems usable from a type checker perspective, but is not actually, because CPython does not support subscription for that specific function yet. I think the hard part here is probably not the type checkers, but keeping typeshed and Python in sync.

I’m glad functions with multiple TypeVarTuples was mentioned, because that was a potential problem. I think now the only weird thing is that classes cannot have multiple TypeVarTuples and functions can. That is unfortunate, but I’m not sure we can easily get out of that.

---

<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:** [April 26, 2026, 4:28am UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/100 "2026-04-26T04:28:43Z")

</div>

> [@davidhalter](#):
>
> I feel like there is a small oversight in overloads that might need to be clarified. There is the example:

I think we can address this by using `(...)` instead of `()` in the overloads and in the implementation, for both examples (serializer\_for and make).

(I don’t think we need to alter the PEP text regarding multiple TypeVarTuples, and that’s fine with me.)

---

<div class="post-metadata">

**Author:** ![Jelle](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jelle/32/1049_2.png) [@Jelle](https://discuss.python.org/u/Jelle)\
**Post date:** [April 26, 2026, 5:04am UTC](https://discuss.python.org/t/pep-718-subscriptable-functions/28457/101 "2026-04-26T05:04:54Z")

</div>

> [@davidhalter](#):
>
> unless you plan to make non-generic functions subscript-able, which I would oppose

The PEP as written (along with the runtime reference implementation) makes all functions and builtin functions subscriptable.

Making only generic functions subscriptable is an interesting way to narrow the impact of the PEP, but it would require some mechanism to deal with functions in untyped code that are treated as generic for type checkers.

[Previous page](https://discuss.python.org/t/pep-718-subscriptable-functions/28457.md?page=4)

[Next page](https://discuss.python.org/t/pep-718-subscriptable-functions/28457.md?page=6)
