Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

PEP 718 – Subscriptable functions

Author:
James Hilton-Balfe <gobot1234yt at gmail.com>
Sponsor:
Guido van Rossum <guido at python.org>
Discussions-To:
Discourse thread
Status:
Draft
Type:
Standards Track
Topic:
Typing
Created:
23-Jun-2023
Python-Version:
3.15
Post-History:
24-Jun-2023

Table of Contents

Abstract

This PEP proposes making function objects subscriptable for typing purposes. Doing sogives developers explicit control over the types produced by the type checker wherebi-directional inference (which allows for the types of parameters of anonymousfunctions to be inferred) and other methods than specialisation are insufficient. Italso brings functions in line with regular classes in their ability to besubscriptable.

Motivation

Unknown Types

Currently, it is not possible to infer the type parameters to generic functions incertain situations:

defmake_list[T](*args:T)->list[T]:...reveal_type(make_list())# type checker cannot infer a meaningful type for T

Making instances ofFunctionType subscriptable would allow for this constructor tobe typed:

reveal_type(make_list[int]())# type is list[int]

Currently you have to use an assignment to provide a precise type:

x:list[int]=make_list()reveal_type(x)# type is list[int]

but this code is unnecessarily verbose taking up multiple lines for a simple functioncall.

Similarly,T in this example cannot currently be meaningfully inferred, sox isuntyped without an extra assignment:

deffactory[T](func:Callable[[T],Any])->Foo[T]:...reveal_type(factory(lambdax:"Hello World"*x))

If function objects were subscriptable, however, a more specific type could be given:

reveal_type(factory[int](lambdax:"Hello World"*x))# type is Foo[int]

Undecidable Inference

There are even cases where subclass relations make type inference impossible. However,if you can specialise the function type checkers can infer a meaningful type.

deffoo[T](x:Sequence[T]|T)->list[T]:...reveal_type(foo[bytes](b"hello"))

Currently, type checkers do not consistently synthesise a type here.

Unsolvable Type Parameters

Currently, with unspecialised literals, it is not possible to determine a type forsituations similar to:

deffoo[T](x:list[T])->T:...reveal_type(foo([]))# type checker cannot infer T (yet again)
reveal_type(foo[int]([]))# type is int

It is also useful to be able to specify in cases in which a certain type must be passedto a function beforehand:

words=["hello","world"]foo[int](words)# Invalid: list[str] is incompatible with list[int]

Allowing subscription makes functions and methods consistent with generic classes wherethey weren’t already. Whilst all of the proposed changes can be implemented usingcallable generic classes, syntactic sugar would be highly welcome.

Due to this, specialising the function and using it as a new factory is fine

make_int_list=make_list[int]reveal_type(make_int_list())# type is list[int]

Monomorphisation and Reification

This proposal also opens the door tomonomorphisation andreified types.

This would allow for a functionality which anecdotally has been requested many times.

Please note this feature is not being proposed by the PEP, but may be implemented inthe future.

The syntax for such a feature may look something like:

deffoo[T]():returnT.__value__assertfoo[int]()isint

Rationale

Function objects in this PEP is used to refer toFunctionType,MethodType,BuiltinFunctionType,BuiltinMethodType andMethodWrapperType.

ForMethodType you should be able to write:

classFoo:defmake_list[T](self,*args:T)->list[T]:...Foo().make_list[int]()

and have it work similarly to aFunctionType.

ForBuiltinFunctionType, so builtin generic functions (e.g.max andmin)work like ones defined in Python. Built-in functions should behave as much likefunctions implemented in Python as possible.

BuiltinMethodType is the same type asBuiltinFunctionType.

MethodWrapperType (e.g. the type ofobject().__str__) is useful forgeneric magic methods.

Specification

Function objects should implement__getitem__ to allow for subscription at runtimeand return an instance oftypes.GenericAlias with__origin__ set as thecallable and__args__ as the types passed.

Type checkers should support subscripting functions and understand that the parameterspassed to the function subscription should follow the same rules as a generic callableclass.

Setting__orig_class__

Currently,__orig_class__ is an attribute set inGenericAlias.__call__ to theinstance of theGenericAlias that created the called class e.g.

classFoo[T]:...assertFoo[int]().__orig_class__==Foo[int]

Currently,__orig_class__ is unconditionally set; however, to avoid potentialerasure on any created instances, this attribute should not be set if__origin__ isan instance of any function object.

The following code snippet would fail at runtime without this change as__orig_class__ would bebar[str] and notFoo[int].

defbar[U]():returnFoo[int]()assertbar[str]().__orig_class__==Foo[int]

Interactions with@typing.overload

Overloaded functions should work much the same as already, since they have no effect onthe runtime type. The only change is that more situations will be decidable and thebehaviour/overload can be specified by the developer rather than leaving it to orderingof overloads/unions.

Backwards Compatibility

Currently these classes are not subclassable and so there are no backwardscompatibility concerns with regards to classes already implementing__getitem__.

Reference Implementation

The runtime changes proposed can be found herehttps://github.com/Gobot1234/cpython/tree/function-subscript

Acknowledgements

Thank you to Alex Waygood and Jelle Zijlstra for their feedback on this PEP and Guidofor some motivating examples.

Copyright

This document is placed in the public domain or under the CC0-1.0-Universal license,whichever is more permissive.


Source:https://github.com/python/peps/blob/main/peps/pep-0718.rst

Last modified:2025-05-06 20:54:28 GMT


[8]ページ先頭

©2009-2025 Movatter.jp