On Saturday, March 21, 2015, Steven D'Aprano
On Fri, Mar 20, 2015 at 09:59:32AM -0700, Guido van Rossum wrote:
Union types ----------- [...] As a shorthand for ``Union[T1, None]`` you can write ``Optional[T1]``;
That only saves three characters. Is it worth it?
I think it is worth it because will be a very common case, and the shortcut is more readable. cheers, Luciano
An optional type is also automatically assumed when the default value is ``None``, for example::
def handle_employee(e: Employee = None): ...
Should that apply to all default values or just None? E.g. if I have
def spam(s: str = 23): ...
should that be inferred as Union[str, int] or be flagged as a type error? I think that we want a type error here, and it's only None that actually should be treated as special. Perhaps that should be made explicit in the PEP.
[...]
For the purposes of type hinting, the type checker assumes ``__debug__`` is set to ``True``, in other words the ``-O`` command-line option is not used while type checking.
I'm afraid I don't understand what you are trying to say here. I would have expected that __debug__ and -O and the type checker would be independent of each other.
[...]
To mark portions of the program that should not be covered by type hinting, use the following:
* a ``@no_type_check`` decorator on classes and functions
* a ``# type: ignore`` comment on arbitrary lines
.. FIXME: should we have a module-wide comment as well?
I think so, if for no other reason than it will reduce the fear of some people that type checks will be mandatory.
Type Hints on Local and Global Variables ========================================
No first-class syntax support for explicitly marking variables as being of a specific type is added by this PEP. To help with type inference in complex cases, a comment of the following format may be used::
x = [] # type: List[Employee]
In the case where type information for a local variable is needed before it is declared, an ``Undefined`` placeholder might be used::
from typing import Undefined
x = Undefined # type: List[Employee] y = Undefined(int)
How is that better than just bringing forward the variable declaration?
x = [] # type: List[Employee] y = 0
Casts =====
Occasionally the type checker may need a different kind of hint: the programmer may know that an expression is of a more constrained type than the type checker infers. For example::
from typing import List
def find_first_str(a: List[object]) -> str: index = next(i for i, x in enumerate(a) if isinstance(x, str)) # We only get here if there's at least one string in a return cast(str, a[index])
The type checker infers the type ``object`` for ``a[index]``, but we know that (if the code gets to that point) it must be a string. The ``cast(t, x)`` call tells the type checker that we are confident that the type of ``x`` is ``t``.
Is the type checker supposed to unconditionally believe the cast, or only if the cast is more constrained than the infered type (like str and object, or bool and int)?
E.g. if the type checker infers int, and the cast says list, I'm not entirely sure I would trust the programmer more than the type checker.
My feeling here is that some type checkers will unconditionally trust the cast, and some will flag the mismatch, or offer a config option to swap between the two, and that will be a feature for type checkers to compete on.
I'm also going to bike-shed the order of arguments. It seems to me that we want to say:
cast(x, T) # pronounced "cast x to T"
rather than Yoda-speak "cast T x to we shall" *wink*. That also matches the order of isinstance(obj, type) calls and makes it easier to remember.
At runtime a cast always returns the expression unchanged -- it does not check the type, and it does not convert or coerce the value.
I'm a little concerned about cast() being a function. I know that it's a do-nothing function, but there's still the overhead of the name lookup and function call. It saddens me that giving a hint to the type checker has a runtime cost, small as it is.
(I know that *technically* annotations have a runtime cost too, but they're once-only, at function definition time, not every time you call the function.)
Your point below that cast() can be used inside expressions is a valid point, so there has to be a cast() function to support those cases, but for the example given here where the cast occurs in a return statement, wouldn't a type comment do?
return some_expression # type: T
hints that some_expression is to be treated as type T, regardless of what was infered.
Casts differ from type comments (see the previous section). When using a type comment, the type checker should still verify that the inferred type is consistent with the stated type. When using a cast, the type checker trusts the programmer. Also, casts can be used in expressions, while type comments only apply to assignments.
Stub Files ========== [...] Stub files may use the ``.py`` extension or alternatively may use the ``.pyi`` extension. The latter makes it possible to maintain stub files in the same directory as the corresponding real module.
I don't like anything that could cause confusion between stub files and actual Python files. If we allow .py extension on stub files, I'm sure there will be confusing errors where people somehow manage to get the stub file imported instead of the actual module they want.
Is there any advantage to allowing stub files use a .py extension? If not, then don't allow it.
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org javascript:; https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Luciano Ramalho | Author of Fluent Python (O'Reilly, 2015) | http://shop.oreilly.com/product/0636920032519.do | Professor em: http://python.pro.br | Twitter: @ramalhoorg