[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