[Python-ideas] Conventions for function annotations

Guido van Rossum guido at python.org
Thu Dec 6 00:06:01 CET 2012


On Wed, Dec 5, 2012 at 12:34 PM, Masklinn <masklinn at masklinn.net> wrote:
> On 2012-12-05, at 20:22 , Guido van Rossum wrote:
>>
>>> The most bothersome part is that I "feel" "either X or Y" (aka `X | Y`)
>>> should be a set of type (and thus the same as {X, Y}[0]) but that doesn't
>>> work with `isinstance` or `issubclass`. Likewise, `(a, b, c)` in an
>>> annotation feels like it should mean the same as `tuple[a, b, c]` ("a
>>> tuple with 3 items of types resp. a, b and c") but that's at odds with
>>> the same type-checking functions.
>>
>> Note that in Python 3 you can override isinstance, by defining
>> __instancecheck__ in the class:
>> http://docs.python.org/3/reference/datamodel.html?highlight=__instancecheck__#class.__instancecheck__
>>
>> So it shouldn't be a problem to make isinstance(42, Int) work.
>
> My problem there was more about having e.g. Int | Float return a set,
> but isinstance not working with a set. But indeed it could return a
> TypeSet which would implement __instancecheck__.

Right, that's what I meant.

>> - Tuples. Sometimes you want to say e.g. "a tuple of integers, don't
>> mind the length"; other times you want to say e.g. "a tuple of fixed
>> length containing an int and two strs". Perhaps the former should be
>> expressed using ImmutableSequence[Int] and the second as Tuple[Int,
>> Str, Str].
>
>
>
>> - Unions. We need a way to say "either X or Y". Given that we're
>> defining our own objects we may actually be able to get away with
>> writing e.g. "Int | Str" or "Str | List[Str]", and isinstance() would
>> still work. It would also be useful to have a shorthand for "either T
>> or None", written as Optional[T] or Optional(T).
>
> Well if `|` is the "union operator", as Ben notes `T | None` works well,
> is clear and is sufficient. Though that's if and only if "Optional[T]"
> is equivalent to "T or None" which Bruce seems to disagree with. There's
> some history with this pattern:
> http://journal.stuffwithstuff.com/2010/08/23/void-null-maybe-and-nothing/
> (bottom section, from "Or Some Other Solution")

Actually, I find "T|None" somewhat impure, since None is not a type
but a value. If you were allow this, what about "T|False"? And then
what about "True|None"? (There's no way to make the latter work!) And
I think "T|NoneType" is obscure; hence my proposal of Optional(T).
(Not Optional[T], since Optional is not a type.)

>> - Whether to design notations to express other constraints. E.g.
>> "integer in range(10, 100)", or "one of the strings 'r', 'w' or 'a'",
>> etc. You can go crazy on this.
>
> Yes this is going in Oleg territory, a sound core is probably a
> good starting idea. Although basic enumerations ("one of the strings
> 'r', 'w' or 'a'") could be rather neat.
>
>> - Composability (Nick's pet peeve, in that he is against it). I
>> propose that we reserve plain tuples for this. If an annotation has
>> the form "x: (P, Q)" then that ought to mean that x must conform to
>> both P and Q. Even though Nick doesn't like this, I don't think we
>> should do everything with decorators. Surly, the decorators approach
>> is good for certain use cases, and should take precedence if it is
>> used. But e.g. IDEs that use annotations for suggestions and
>> refactoring should not require everything to be decorated -- that
>> would just make the code too busy.
>>
>> - Runtime enforcement. What should we use type annotations for? IDEs,
>> static checkers (linters) and refactoring tools only need the
>> annotations when they are parsing the code.
>
> For IDEs, that's pretty much all the time though, either they're parsing
> the code or they're trying to perform static analysis on it, which uses
> the annotations.

Yeah, they're parsing it, but they're not executing it.

>> While it is tempting to
>> invent some kind of runtime checking that automatically checks the
>> actual types against the annotations whenever a function is called, I
>> think this is rarely useful, and often prohibitively slow.
>
> Could be useful for debug or testing runs though, in the same way
> event-based profilers are prohibitively slow and can't be enabled all
> the time but are still useful. Plus it might be possible to
> enable/disable this mechanism with little to no source modification via
> sys.setprofile (I'm not sure what hooks it provides exactly and the
> documentation is rather sparse, so I'm not sure if the function object
> itself is available to the setprofile callback, looking at
> Lib/profiler.py it might only get the code object).

Hence my idea of using a decorator to enable this on specific functions.

-- 
--Guido van Rossum (python.org/~guido)



More information about the Python-ideas mailing list