[Python-ideas] Signature Literals
Blaine Rogers
blaine.w.rogers at gmail.com
Tue Aug 29 04:58:51 EDT 2017
The current syntax for Callable types is unwieldy, particularly when
extended to include varargs and keyword args as in
http://mypy.readthedocs.io/en/latest/kinds_of_types.html#
extended-callable-types. Why not introduce a signature literal? Proposed
syntax:
>>> from inspect import Signature, Parameter
> >>> () ->
> Signature()
> >>> (arg0, arg1, arg2=None, arg3=None) ->
> Signature(
> [Parameter('arg0', Parameter.POSITIONAL_OR_KEYWORD),
> Parameter('arg1', Parameter.POSITIONAL_OR_KEYWORD),
> Parameter('arg2', Parameter.POSITIONAL_OR_KEYWORD, default=None),
> Parameter('arg3', Parameter.POSITIONAL_OR_KEYWORD, default=None)],
> return_annotation=str
> )
> >>> (arg0, arg1: int, arg2=None, arg3: float=None) -> str
> Signature(
> [Parameter('arg0', Parameter.POSITIONAL_OR_KEYWORD),
> Parameter('arg1', Parameter.POSITIONAL_OR_KEYWORD, annotation=int),
> Parameter('arg2', Parameter.POSITIONAL_OR_KEYWORD, default=None),
> Parameter('arg3', Parameter.POSITIONAL_OR_KEYWORD, annotation=float,
> default=None)],
> return_annotation=str
> )
> >>> (:, :, :, arg1, *, arg2) ->
> Signature(
> [Parameter('', Parameter.POSITIONAL_ONLY),
> Parameter('', Parameter.POSITIONAL_ONLY),
> Parameter('', Parameter.POSITIONAL_ONLY),
> Parameter('arg1', Parameter.POSITIONAL_OR_KEYWORD),
> Parameter('arg2', Parameter.KEYWORD_ONLY)]
> )
> >>> (:int, :float, *, keyword: complex) -> str
> Signature(
> [Parameter('', Parameter.POSITIONAL_ONLY, annotation=int),
> Parameter('', Parameter.POSITIONAL_ONLY, annotation=float),
> Parameter('keyword', Parameter.KEYWORD_ONLY, annotation=complex)],
return_annotation=str
)
Compare the above to their equivalents using Callable (and the experimental
extension to Mypy):
>>> Callable[[], Any]
> >>> Callable[[Arg(Any, 'arg0'), Arg(int, 'arg1'), DefaultArg(Any, 'arg2'),
> DefaultArg(float, 'kwarg3')], str]
> >>> Callable[[Arg(), Arg(), Arg(), Arg(Any, 'arg1'), NamedArg(Any,
> 'arg2')], Any]
> >>> Callable[[int, float, NamedArg(complex, 'keyword')], Any]
The proposed signature literal syntax is shorter, just as clear and imo
nicer to read.
Here is what it looks like in annotations:
from typing import TypeVar, Callable
>
> A = TypeVar('A')
> def apply_successor(func: Callable[[A], A], init: A, n_applications: int)
> -> A: ...
> def apply_successor(func: (:A) -> A, init: A, n_applications: int) -> A:
> ...
>
> import tensorflow as tf
> import numpy as np
>
> def run(policy: Callable[[np.ndarray, Arg(Dict[tf.Tensor, np.ndarray],
> 'updated_feeds')], np.ndarray]) -> bool: ...
> def run(policy: (:np.ndarray, updated_feeds: Dict[tf.Tensor, np.ndarray])
> -> np.ndarray) -> bool: ...
> # If Mypy accepted literals for container types (dict, set, list, tuple,
> etc) this would be nicer still
> def run(policy: (:np.ndarray, updated_feeds: {tf.Tensor: np.ndarray}) ->
> np.ndarray) -> bool: ...
Initial thoughts:
- () -> is ugly, but the -> would be necessary to distinguish it from
the empty tuple (). Actually, it can be difficult to tell the difference
between the proposed signature literals and tuples, especially for long
signatures with no annotations or defaults. An alternative would be to
prefix the arguments with an @ or other uncommon symbol (maybe &). () ->
becomes @(), and it is clear from the start that you're reading a
signature.
- Supposing the syntax for function definitions was changed to match the
proposed signature literals, one could make something like the following
possible:
>>> def add(:, :):
... arg0, arg1 = __call_signature__.args
... return arg0.value + arg1.value
>>> add(1, 2)
3
>>> add('hello', 'world')
'helloworld'
Where __call_signature__ is a magic name that evaluates to an
inspect.BoundArguments instance representing the signature of the function
call. I'm not sure why you'd want functions with positional-only arguments,
but now you could have them.
- You could further extend the function definition syntax to allow an
expression that evaluates to a signature instead of a literal
>>> signature = (:, :) ->
>>> def add signature:
... arg0, arg1 = __call_signature__.args
... return arg0 + arg1
Again, not sure how useful this would be.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20170829/cf32b79e/attachment.html>
More information about the Python-ideas
mailing list