On 24 September 2016 at 01:58, Chris Angelico
Default arguments trip some people up because they expect them to be evaluated when the function's called, but it can easily be explained. Function annotations are exactly the same. Making them magically late-evaluate would have consequences for the grokkability of the language - they would be special. Now, that can be done
Folks have been assuming that's straightforward, but the way class namespaces work actually makes it significantly harder than it first appears. Using lambda and default arguments to illustrate the problem: >>> class Example: ... attr = 10 ... @staticmethod ... def good_method(eager=attr): ... return eager ... @staticmethod ... def bad_method(lazy=(lambda:attr)): ... return lazy() ... >>> Example().good_method() 10 >>> Example().bad_method() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 8, in bad_method File "<stdin>", line 7, in <lambda> NameError: name 'attr' is not defined By design, function scopes can't see attributes defined in containing class scopes, and we don't currently have any other kind of scope that supports delayed evaluation (unlike function bodies, class bodies are evaluated eagerly at class definition time, and all the other delayed evaluation constructs are syntactic sugar for some particular flavour of function scope definition - even generators and coroutines use the same basic name resolution scheme as regular functions, they just use different execution models). If it was still 2006 or 2007 and Python 3.0 hadn't been released yet, lazy annotations could seriously be considered as an option. It's 2016 though, eager annotations have been out in the wild since December 2008, and the existing "string literals are Python's de facto lazy evaluation syntax" approach works well enough for the purpose, since type checkers can say they're actually going to parse those string literals when they appear to be type hints. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia