This PEP introduces a concise and friendly syntax for callable types,supporting the same functionality astyping.Callable but with anarrow syntax inspired by the syntax for typed functionsignatures. This allows types likeCallable[[int,str],bool] tobe written as(int,str)->bool.
The proposed syntax supports all the functionality provided bytyping.Callable andtyping.Concatenate, and is intended towork as a drop-in replacement.
One way to make code safer and easier to analyze is by making surethat functions and classes are well-typed. In Python we have typeannotations, the framework for which is defined inPEP 484, to providetype hints that can find bugs as well as helping with editor toolinglike tab completion, static analysis tooling, and code review.
Consider the following untyped code:
defflat_map(func,l):out=[]forelementinl:out.extend(func(element))returnoutdefwrap(x:int)->list[int]:return[x]defadd(x:int,y:int)->int:returnx+yflat_map(wrap,[1,2,3])# no runtime error, output is [1, 2, 3]flat_map(add,[1,2,3])# runtime error: `add` expects 2 arguments, got 1
We can add types to this example to detect the runtime error:
fromtypingimportCallabledefflat_map(func:Callable[[int],list[int]],l:list[int])->list[int]:.......flat_map(wrap,[1,2,3])# type checks okay, output is [1, 2, 3]flat_map(add,[1,2,3])# type check error
There are a few usability challenges withCallable we can see here:
list anddict.Possibly as a result,programmers often fail to write completeCallable types.Such untyped or partially-typed callable types do not check theparameter types or return types of the given callable and thus negatethe benefits of static typing. For example, they might write this:
fromtypingimportCallabledefflat_map(func:Callable[...,Any],l:list[int])->list[int]:.......flat_map(add,[1,2,3])# oops, no type check error!
There’s some partial type information here - we at least know thatfuncneeds to be callable. But we’ve dropped too much type information fortype checkers to find the bug.
With our proposal, the example looks like this:
defflat_map(func:(int)->list[int],l:list[int])->list[int]:out=[]forelementinl:out.extend(f(element))returnout...
The type(int)->list[int] is more concise, uses an arrow similarto the one indicating a return type in a function header, avoidsnested brackets, and does not require an import.
TheCallable type is widely used. For example,as of October 2021it wasthe fifth most common complex type in typeshed, afterOptional,Tuple,Union, andList.
The others have had their syntax improved and the need for importseliminated by eitherPEP 604 orPEP 585:
typing.Optional[int] is writtenint|Nonetyping.Union[int,str] is writtenint|strtyping.List[int] is writtenlist[int]typing.Tuple[int,str] is writtentuple[int,str]Thetyping.Callable type is used almost as often as these othertypes, is more complicated to read and write, and still requires animport and bracket-based syntax.
In this proposal, we chose to support all the existing semantics oftyping.Callable, without adding support for new features. We madethis decision after examining how frequently each feature might beused in existing typed and untyped open-source code. We determinedthat the vast majority of use cases are covered.
We considered adding support for named, optional, and variadicarguments. However, we decided against including these features, asour analysis showed they are infrequently used. When they are reallyneeded, it is possible to type these usingcallback protocols.
We are proposing a succinct, easy-to-use syntax fortyping.Callable that looks similar to function headers in Python.Our proposal closely follows syntax used by several popular languagessuch asTypescript,Kotlin, andScala.
Our goals are that:
decorator example above.Consider this simplified real-world example from a web server, writtenusing the existingtyping.Callable:
fromtypingimportAwaitable,Callablefromapp_logicimportResponse,UserSettingdefcustomize_response(response:Response,customizer:Callable[[Response,list[UserSetting]],Awaitable[Response]])->Response:...
With our proposal, this code can be abbreviated to:
fromapp_logicimportResponse,UserSettingdefcustomize_response(response:Response,customizer:async(Response,list[UserSetting])->Response,)->Response:...
This is shorter and requires fewer imports. It also has far lessnesting of square brackets - only one level, as opposed to three inthe original code.
ParamSpecA particularly common case where library authors leave off type informationfor callables is when defining decorators. Consider the following:
fromtypingimportAny,Callabledefwith_retries(f:Callable[...,Any])->Callable[...,Any]:defwrapper(retry_once,*args,**kwargs):ifretry_once:try:returnf(*args,**kwargs)exceptException:passreturnf(*args,**kwargs)returnwrapper@with_retriesdeff(x:int)->int:returnxf(y=10)# oops - no type error!
In the code above, it is clear that the decorator should produce afunction whose signature is like that of the argumentf otherthan an additionalretry_once argument. But the use of...prevents a type checker from seeing this and alerting a user thatf(y=10) is invalid.
WithPEP 612 it is possible to type decorators like this correctlyas follows:
fromtypingimportAny,Callable,Concatenate,ParamSpec,TypeVarR=TypeVar("R")P=ParamSpec("P")defwith_retries(f:Callable[P,R])->Callable[Concatenate[bool,P]R]:defwrapper(retry_once:bool,*args:P.args,**kwargs:P.kwargs)->R:...returnwrapper...
With our proposed syntax, the properly-typed decorator example becomesconcise and the type representations are visually descriptive:
fromtypingimportAny,ParamSpec,TypeVarR=TypeVar("R")P=ParamSpec("P")defwith_retries(f:(**P)->R)->(bool,**P)->R:...
Many popular programming languages use an arrow syntax similarto the one we are proposing here.
InTypeScript,function types are expressed in a syntax almost the same as the one weare proposing, but the arrow token is=> and arguments have names:
(x:int,y:str)=>bool
The names of the arguments are not actually relevant to the type. So,for example, this is the same callable type:
(a:int,b:str)=>bool
Function types inKotlin permitan identical syntax to the one we are proposing, for example:
(Int,String)->Bool
It also optionally allows adding names to the arguments, for example:
(x:Int,y:String)->Bool
As in TypeScript, the argument names (if provided) are just there fordocumentation and are not part of the type itself.
Scalauses the=> arrow for function types. Other than that, their syntax isthe same as the one we are proposing, for example:
(Int,String)=>Bool
Scala, like Python, has the ability to provide function arguments by name.Function types can optionally include names, for example:
(x:Int,y:String)=>Bool
Unlike in TypeScript and Kotlin, these names are part of the type ifprovided - any function implementing the type must use the same names.This is similar to the extended syntax proposal we describe in ourRejected Alternatives section.
In all of the languages listed above, type annotations for functiondefinitions use a: rather than a->. For example, in TypeScripta simple add function looks like this:
functionhigher_order(fn:(a:string)=>string):string{returnfn("Hello, World");}
Scala and Kotlin use essentially the same: syntax for returnannotations. The: makes sense in these languages because theyall use: for type annotations ofparameters and variables, and the use for function return types issimilar.
In Python we use: to denote the start of a function body and-> for return annotations. As a result, even though our proposalis superficially the same as these other languages the context isdifferent. There is potential for more confusion in Python whenreading function definitions that include callable types.
This is a key concern for which we are seeking feedback with our draftPEP; one idea we have floated is to use=> instead to make it easierto differentiate.
Languages in the ML family, includingF#,OCaml,andHaskell, all use-> to represent function types. All of them use a parentheses-freesyntax with multiple arrows, for example in Haskell:
Integer->String->Bool
The use of multiple arrows, which differs from our proposal, makessense for languages in this family because they use automaticcurrying of function arguments,which means that a multi-argument function behaves like a single-argumentfunction returning a function.
Type checkers should treat the new syntax with exactly the samesemantics astyping.Callable.
As such, a type checker should treat the following pairs exactly thesame:
fromtypingimportAwaitable,Callable,Concatenate,ParamSpec,TypeVarTupleP=ParamSpec("P")Ts=TypeVarTuple('Ts')f0:()->boolf0:Callable[[],bool]f1:(int,str)->boolf1:Callable[[int,str],bool]f2:(...)->boolf2:Callable[...,bool]f3:async(str)->strf3:Callable[[str],Awaitable[str]]f4:(**P)->boolf4:Callable[P,bool]f5:(int,**P)->boolf5:Callable[Concatenate[int,P],bool]f6:(*Ts)->boolf6:Callable[[*Ts],bool]f7:(int,*Ts,str)->boolf7:Callable[[int,*Ts,str],bool]
The proposed new syntax can be described by these AST changes toParser/Python.asdl:
expr=<prexisting_expr_kinds>|AsyncCallableType(callable_type_argumentsargs,exprreturns)|CallableType(callable_type_argumentsargs,exprreturns)callable_type_arguments=AnyArguments|ArgumentsList(expr*posonlyargs)|Concatenation(expr*posonlyargs,exprparam_spec)
Here are our proposed changes to thePython Grammar:
expression: | disjunction disjunction 'else' expression | callable_type_expression | disjunction | lambdefcallable_type_expression: | callable_type_arguments '->' expression | ASYNC callable_type_arguments '->' expressioncallable_type_arguments: | '(' '...' [','] ')' | '(' callable_type_positional_argument* ')' | '(' callable_type_positional_argument* callable_type_param_spec ')'callable_type_positional_argument: | !'...' expression ',' | !'...' expression &')'callable_type_param_spec: | '**' expression ',' | '**' expression &')'IfPEP 646 is accepted, we intend to include support for unpackedtypes in two ways. To support the “star-for-unpack” syntax proposed inPEP 646, we will modify the grammar forcallable_type_positional_argument as follows:
callable_type_positional_argument: | !'...' expression ',' | !'...' expression &')' | '*' expression ',' | '*' expression &')'
With this change, a type of the form(int,*Ts)->bool shouldevaluate the AST form:
CallableType(ArgumentsList(Name("int"),Starred(Name("Ts")),Name("bool"))
and be treated by type checkers as equivalent to orCallable[[int,*Ts],bool] orCallable[[int,Unpack[Ts]],bool].
-> binds less tightly than other operators, both inside types andin function signatures, so the following two callable types areequivalent:
(int)->str|bool(int)->(str|bool)
-> associates to the right, both inside types and in functionsignatures. So the following pairs are equivalent:
(int)->(str)->bool(int)->((str)->bool)deff()->(int,str)->bool:passdeff()->((int,str)->bool):passdeff()->(int)->(str)->bool:passdeff()->((int)->((str)->bool)):pass
Because operators bind more tightly than->, parentheses arerequired whenever an arrow type is intended to be inside an argumentto an operator like|:
(int)->()->int|()->bool# syntax error!(int)->(()->int)|(()->bool)# okay
We discussed each of these behaviors and believe they are desirable:
A|B according toPEP 604) arevalid in function signature returns, so we need to allow operatorsin the return position for consistency.-> it is correctthat a type likebool|()->bool must be a syntax error. Weshould be sure the error message is clear because this may be acommon mistake.-> to the right, rather than requiring explicitparentheses, is consistent with other languages like TypeScript andrespects the principle that valid expressions should normally besubstitutable when possible.async KeywordAll of the binding rules still work for async callable types:
(int)->async(float)->str|bool(int)->(async(float)->(str|bool))deff()->async(int,str)->bool:passdeff()->(async(int,str)->bool):passdeff()->async(int)->async(str)->bool:passdeff()->(async(int)->(async(str)->bool)):pass
(,)->bool is a syntaxerror.((int,)->bool==(int)->bool((int,**P,)->bool==(int,**P)->bool((...,)->bool)==((...)->bool)
Allowing trailing commas also gives autoformatters more flexibilitywhen splitting callable types across lines, which is always legalfollowing standard python whitespace rules.
... as an Argument TypeUnder normal circumstances, any valid expression is permitted where wewant a type annotation and... is a valid expression. This isnever semantically valid and all type checkers would reject it, butthe grammar would allow it if we did not explicitly prevent this.
Since... is meaningless as a type and there are usabilityconcerns, our grammar rules it out and the following is a syntaxerror:
(int,...)->bool
We decided that there were compelling reasons to do this:
(...)->bool are different from(T)->boolfor any valid type T:(...) is a special form indicatingAnyArguments whereasT is a type parameter in the argumentslist.... is used as a placeholder default value to indicate anoptional argument in stubs and callback protocols. Allowing it inthe position of a type could easily lead to confusion and possiblybugs due to typos.tuple generic type, we special-case... to mean“more of the same”, e.g. atuple[int,...] means a tuple withone or more integers. We do not use... in a a similar wayin callable types, so to prevent misunderstandings it makes senseto prevent this.* and**The use of**P for supportingPEP 612ParamSpec rules out anyfuture proposal using a bare**<some_type> to typekwargs. This seems acceptable because:
kwargs in callable types, we would prefer(int,**kwargs:str) rather than(int,**str).*<some_type> forargs. Thekwargs case is similar enough that this rules outa bare**<some_type> anyway.To the best of our knowledge there is no active discussion ofarrow-style lambda syntax that we are aware of, but it is nonethelessworth considering what possibilities would be ruled out by adoptingthis proposal.
It would be incompatible with this proposal to adopt the same aparenthesized->-based arrow syntax for lambdas, e.g.(x,y)->x+y forlambdax,y:x+y.
Our view is that if we want arrow syntax for lambdas in the future, itwould be a better choice to use=>, e.g.(x,y)=>x+y.Many languages use the same arrow token for both lambdas and callabletypes, but Python is unique in that types are expressions and have toevaluate to runtime values. Our view is that this merits usingseparate tokens, and given the existing use of-> for return typesin function signatures it would be more coherent to use-> forcallable types and=> for lambdas.
The new AST nodes need to evaluate to runtime types, and we have two goals for thebehavior of these runtime types:
typing.Callable.We intend to create new builtin types to which the new AST nodes willevaluate, exposing them in thetypes module.
Our plan is to expose a structured API as if they were defined as follows:
classCallableType:is_async:boolarguments:Ellipsis|tuple[CallableTypeArgument]return_type:objectclassCallableTypeArgument:kind:CallableTypeArgumentKindannotation:object@enum.global_enumclassCallableTypeArgumentKind(enum.IntEnum):POSITIONAL_ONLY:int=...PARAM_SPEC:int=...
The evaluation rules are expressed in terms of the followingpseudocode:
defevaluate_callable_type(callable_type:ast.CallableType|ast.AsyncCallableType:)->CallableType:returnCallableType(is_async=isinstance(callable_type,ast.AsyncCallableType),arguments=_evaluate_arguments(callable_type.arguments),return_type=evaluate_expression(callable_type.returns),)def_evaluate_arguments(arguments):matcharguments:caseast.AnyArguments():returnEllipsiscaseast.ArgumentsList(posonlyargs):returntuple(_evaluate_arg(arg)forarginargs)caseast.ArgumentsListConcatenation(posonlyargs,param_spec):returntuple(*(evaluate_arg(arg)forarginargs),_evaluate_arg(arg=param_spec,kind=PARAM_SPEC))ifisinstance(arguments,AnyreturnEllipsisdef_evaluate_arg(arg,kind=POSITIONAL_ONLY):returnCallableTypeArgument(kind=POSITIONAL_ONLY,annotation=evaluate_expression(value))
To get backward compatibility with the existingtypes.Callable API,which relies on fields__args__ and__parameters__, we can definethem as if they were written in terms of the following:
importitertoolsimporttypingdefget_args(t:CallableType)->tuple[object]:return_type_arg=(typing.Awaitable[t.return_type]ift.is_asyncelset.return_type)arguments=t.argumentsifisinstance(arguments,Ellipsis):argument_args=(Ellipsis,)else:argument_args=(arg.annotationforarginarguments)return(*arguments_args,return_type_arg)defget_parameters(t:CallableType)->tuple[object]:out=[]forarginget_args(t):ifisinstance(arg,typing.ParamSpec):out.append(t)else:out.extend(arg.__parameters__)returntuple(out)
types.CallableTypeAs with theA|B syntax for unions introduced inPEP 604:
__eq__ method should treat equivalenttyping.Callablevalues as equal to values constructed using the builtin syntax, andotherwise should behave like the__eq__ oftyping.Callable.__repr__ method should produce an arrow syntax representation that,when evaluated, gives us back an equaltypes.CallableType instance.Many of the alternatives we considered would have been more expressivethantyping.Callable, for example adding support for describingsignatures that include named, optional, and variadic arguments.
To determine which features we most needed to support with a callabletype syntax, we did an extensive analysis of existing projects:
We decided on a simple proposal with improved syntax for the existingCallable type because the vast majority of callbacks can be correctlydescribed by the existingtyping.Callable semantics:
(int,str)->boolParamSpec andConcatenate types like(**P)->bool and(int,**P)->bool. These are commonprimarily because of the heavy use of decorator patterns in pythoncode.*args to some other function.Features that other, more complicated proposals would support accountfor fewer than 2% of the use cases we found. These are alreadyexpressible using callback protocols, and since they are uncommon wedecided that it made more sense to move forward with a simpler syntax.
Another alternative was for a compatible but more complex syntax thatcould express everything in this PEP but also named, optional, andvariadic arguments. In this “extended” syntax proposal the followingtypes would have been equivalent:
classFunction(typing.Protocol):deff(self,x:int,/,y:float,*,z:bool=...,**kwargs:str)->bool:...Function=(int,y:float,*,z:bool=...,**kwargs:str)->bool
Advantages of this syntax include: - Most of the advantages of theproposal in this PEP (conciseness,PEP 612 support, etc) -Furthermore, the ability to handle named, optional, and variadicarguments
We decided against proposing it for the following reasons:
fromtypingimportoverload,ProtocolclassOverloadedCallback(Protocol)@overloaddef__call__(self,x:int)->float:...@overloaddef__call__(self,x:bool)->bool:...def__call__(self,x:int|bool)->float|bool:...f:OverloadedCallback=...f(True)# boolf(3)# float
We confirmed that the current proposal is forward-compatible withextended syntax byimplementinga grammar and AST for this extended syntax on top of our referenceimplementation of this PEP’s grammar.
One alternative we had floated was a syntax much more similar tofunction signatures.
In this proposal, the following types would have been equivalent:
classFunction(typing.Protocol):deff(self,x:int,/,y:float,*,z:bool=...,**kwargs:str)->bool:...Function=(x:int,/,y:float,*,z:bool=...,**kwargs:str)->bool
The benefits of this proposal would have included:
Key downsides that led us to reject the idea include the following:
/, for example(int,/)->bool whereour proposal allows(int)->bool/ for positional-only arguments hasa high risk of causing frequent bugs - which often would not bedetected by unit tests - where library authors would accidentallyuse types with named arguments.ParamSpec is key, but thescoping rules laid out inPEP 612 would have made this difficult.An idea we looked at very early on was toallow using functions as types.The idea is allowing a function to stand in for its own callsignature, with roughly the same semantics as the__call__ methodof callback protocols:
defCallableType(positional_only:int,/,named:str,*args:float,keyword_only:int=...,**kwargs:str)->bool:...f:CallableType=...f(5,6.6,6.7,named=6,x="hello",y="world")# typechecks as bool
This may be a good idea, but we do not consider it a viablereplacement for callable types:
ParamSpec, which we consider acritical feature to support.Callable.In the Rust language, a keywordfn is used to indicate functionsin much the same way as Python’sdef, and callable types areindicated using a hybrid arrow syntaxFn(i64,String)->bool.
We could use thedef keyword in callable types for Python, forexample our two-parameter boolean function could be written asdef(int,str)->bool. But we think this might confuse readersinto thinkingdef(A,B)->C is a lambda, particularly becauseJavascript’sfunction keyword is used in both named and anonymousfunctions.
We considered a parentheses-free syntax that would have been even moreconcise:
int,str->bool
We decided against it because this is not visually as similar toexisting function header syntax. Moreover, it is visually similar tolambdas, which bind names with no parentheses:lambdax,y:x==y.
A concern with the current proposal is readability, particularlywhen callable types are used in return type position which leads tomultiple top-level-> tokens, for example:
defmake_adder()->(int)->int:returnlambdax:x+1
We considered a few ideas to prevent this by changing rules aboutparentheses. One was to move the parentheses to the outside, sothat a two-argument boolean function is written(int,str->bool).With this change, the example above becomes:
defmake_adder()->(int->int):returnlambdax:x+1
This makes the nesting of many examples that are difficult tofollow clear, but we rejected it because
(int,str->bool) as a tuple whose first element is an int,rather than a two-parameter callable type.We also considered requiring parentheses on both the parameter list and theoutside, e.g.((int,str)->bool). With this change, the example abovebecomes:
defmake_adder()->((int)->int):returnlambdax:x+1
We rejected this change because:
IntToIntFunction:(int)->intdefmake_adder()->IntToIntFunction:returnlambdax:x+1
-> bind tighter than|In order to allow both-> and| tokens in type expressions wehad to choose precedence. In the current proposal, this is a functionreturning an optional boolean:
(int,str)->bool|None# equivalent to (int, str) -> (bool | None)
We considered having-> bind tighter so that instead the expressionwould parse as((int,str)->bool)|None. There are two advantagesto this:
None|(int,str)->bool as a syntax error.None as a default value is a standard Python idiom.Having-> bind tighter would make these easier to write.We decided against this for a few reasons:
deff()->int|None:... is legaland indicates a function returning an optional int. To be consistentwith function headers, callable types should do the same.-> and| tokens in type expressions, and they have| bindtighter. While we do not have to follow their lead, we prefer to doso.| bind tighter forces extra parentheses, which makes thesetypes harder to write. But code is read more often than written, andwe believe that requiring the outer parentheses for an optional callabletype like((int,str)->bool)|None is preferable for readability.Another idea was adding a new “special string” syntax and putting the typeinside of it, for examplet”(int,str)->bool”. We rejected thisbecause it is not as readable, and seems out of step withguidancefrom the Steering Council on ensuring that type expressions do notdiverge from the rest of Python’s syntax.
If we do not want to add new syntax for callable types, we couldlook at how to make the existing type easier to read. One proposalwould be to make the builtincallable function indexable sothat it could be used as a type:
callable[[int,str],bool]
This change would be analogous toPEP 585 that made built in collectionslikelist anddict usable as types, and would make importsmore convenient, but it wouldn’t help readability of the types themselvesmuch.
In order to reduce the number of brackets needed in complex callabletypes, it would be possible to allow tuples for the argument list:
callable[(int,str),bool]
This actually is a significant readability improvement formulti-argument functions, but the problem is that it makes callableswith one arguments, which are the most common arity, hard towrite: because(x) evaluates tox, they would have to bewritten likecallable[(int,),bool]. We find this awkward.
Moreover, none of these ideas help as much with reducing verbosityas the current proposal, nor do they introduce as strong a visual cueas the-> between the parameter types and the return type.
The hard requirements on our runtime API are that:
typing.Callable via__args__ and__params__.We considered having the runtime datatypes.CallableType use amore structured API where there would be separate fields forposonlyargs andparam_spec. The current proposal waswas inspired by theinspect.Signature type.
We use “argument” in our field and type names, unlike “parameter”as ininspect.Signature, in order to avoid confusion withthecallable_type.__parameters__ field from the legacy APIthat refers to type parameters rather than callable parameters.
__args__ for async typesIt is debatable whether we are required to preserve backward compatibilityof__args__ for async callable types likeasync(int)->str. Thereason is that one could argue they are not expressible directlyusingtyping.Callable, and therefore it would be fine to set__args__ as(int,int) rather than(int,typing.Awaitable[int]).
But we believe this would be problematic. By preserving the appearanceof a backward-compatible API while actually breaking its semantics onasync types, we would cause runtime type libraries that attempt tointerpretCallable using__args__ to fail silently.
It is for this reason that we automatically wrap the return type inAwaitable.
This PEP proposes a major syntax improvement overtyping.Callable,but the static semantics are the same.
As such, the only thing we need for backward compatibility is toensure that types specified via the new syntax behave the same asequivalenttyping.Callable andtyping.Concatenate values theyintend to replace.
There is no particular interaction between this proposal andfrom__future__importannotations - just like any other type annotationit will be unparsed to a string at module import, andtyping.get_type_hints should correctly evaluate the resultingstrings in cases where that is possible.
This is discussed in more detail in the Runtime Behavior section.
We have a workingimplementationof the AST and Grammar with tests verifying that the grammar proposedhere has the desired behaviors.
The runtime behavior is not yet implemented. As discussed in theRuntime Behavior portion of the spec we have a detailed plan forboth a backward-compatible API and a more structured API ina separate docwhere we are also open to discussion and alternative ideas.
We have attempted to provide a complete behavior specification intheRuntime Behavior section of this PEP.
But there are probably more details that we will not realize weneed to define until we build a full reference implementation.
SyntaxError messagesThe current reference implementation has a fully-functional parser andall edge cases presented here have been tested.
But there are some known cases where the errors are not as informativeas we would like. For example, because(int,...)->bool isillegal but(int,...) is a valid tuple, we currently produce asyntax error flagging the-> as the problem even though the realcause of the error is using... as an argument type.
This is not part of the specificationper se but is an importantdetail to address in our implementation. The solution will likelyinvolve addinginvalid_.* rules topython.gram and customizingerror messages.
PEP 484 specifiesa very similar syntax for function type hintcomments for use incode that needs to work on Python 2.7. For example:
deff(x,y):# type: (int, str) -> bool...
At that time we used indexing operations to specify generic types liketyping.Callable because we decided not to add syntax fortypes. However, we have since begun to do so, e.g. withPEP 604.
Maggie proposed better callable type syntax as part of a largerpresentation on typing simplificationsat the PyCon Typing Summit 2021.
Stevenbrought up this proposal on typing-sig.We had several meetings to discuss alternatives, andthis presentationled us to the current proposal.
Pradeepbrought this proposal to python-devfor feedback.
Thanks to the following people for their feedback on the PEP and helpplanning the reference implementation:
Alex Waygood, Eric Traut, Guido van Rossum, James Hilton-Balfe,Jelle Zijlstra, Maggie Moss, Tuomas Suutari, Shannon Zhu.
This document is placed in the public domain or under theCC0-1.0-Universal license, whichever is more permissive.
Source:https://github.com/python/peps/blob/main/peps/pep-0677.rst
Last modified:2025-10-25 21:23:03 GMT