Repository navigation
Allow __init__ with signature but no return type #604
Description
Activity
Hm, I think the return type for init should be None. A "return " inside an init is almost certainly caused by a misunderstanding. (Maybe you're thinking of new, which does return the object it created?)
Reacted by Lucas VazquezAh, the issue is that the error message is wrong. The message should say that the return type must be None? The message may be a relict from times before Python-compatible syntax.
- added a commit that references this issue
on Mar 19, 2015 But why do I have to specify the return type at all? Can't it default to
None here?On Wednesday, March 18, 2015, Jukka Lehtosalo notifications@github.com
wrote:Closed #604 #604 via 182a70b
182a70b
.—
Reply to this email directly or view it on GitHub
#604 (comment).--Guido van Rossum (on iPad)
Reacted by Daniel Kats, Scorpio, Juan Palacios, Eyad Sibai, Michael Egorov, Никита Конин, Carl Thomé, Arnav Borborah, Lenz Furrer, Russ Warren and 6 moreBecause of consistency. Consider
__init__that takes no arguments, other thanself:def __init__(self): ...Would this be dynamically typed or statically typed? The current approach is to treat any function that has no annotation (no argument or return types) as dynamically typed, and statically typed (i.e. type checked) otherwise. The
Nonereturn type marks the function as type checked.Same goes for functions in general:
def f(): # Dynamically typed ... def f() -> None: # Statically typed ... def g(x: int): # Statically typed, Any return type ...Hm. I think it'd suck if we'd have to develop the habit of adding explicit
"-> None" to all init methods just to make mypy happy.On Thu, Mar 19, 2015 at 8:11 PM, Jukka Lehtosalo notifications@github.com
wrote:Because of consistency. Consider init that takes no arguments, other
than self:def init(self):
...Would this be dynamically typed or statically typed? The current approach
is to treat any function that has no annotation (no argument or return
types) as dynamically typed, and statically typed (i.e. type checked)
otherwise. The None return type marks the function as type checked.Same goes for functions in general:
def f(): # Dynamically typed
...
def f() -> None: # Statically typed
...
def g(x: int): # Statically typed, Any return type
...—
Reply to this email directly or view it on GitHub
#604 (comment).--Guido van Rossum (python.org/~guido)
Reacted by Matthieu Bizien, Daniel Kats, Islam El-Ashi, Daniel Darabos, Dyno Fu, Christoph Zwerschke, Vasiliy Sheredeko, Christophe Tafani-Dereeper, igor, Alexey Kinev and 45 moreOkay, let's reopen this. Here's a proposed new spec. Let me know if it matches your thinking.
-> Nonewould only be needed for type checked__init__methods that take no arguments:def __init__(self) -> None: ...(In my Python corpus, these account for about 15% of all
__init__methods.__init__methods seem to cover about 10% of all methods.)An
__init__method with any argument types would be type checked, and the return type would always beNoneimplicitly, but you can also give an explicitNonereturn type if you want.What about other functions and methods that have arguments with types but don't have an explicit return type? Should they always default to an
Anyreturn type? I see a few potential alternatives to this:- Default to a
Nonereturn type, similar to__init__. - Default to a
Nonereturn type if there is noreturnstatement with an explicit value in the body. Give an error otherwise. - Default to a
Nonereturn type if there is noreturnstatement with an explicit value in the body. Default to anAnyreturn type otherwise. - Try to infer the return type automatically. I don't like this much because it adds complexity, reduces consistency and "explicit is better than implicit" in this case, in my opinion.
- Default to a
It's a tricky issue, since we need to balance the desire to catch errors in the body of the function (what if a return type was intended but accidentally omitted?) with the need to derive the most useful return type for the benefit of checking call sites, as well as making the system be pleasant to use. I think for methods in general the status quo is fine, but (2) and (3) have some slight advantage, and some even slighter downside.
However I still think
__init__is a special case -- its return value is determined by how Python uses it, not by what the user might want it to return. I think the absence of-> Noneshould never result in an error message here (and in fact-> something_elseshould be considered an error, even-> Any). The heuristic over whether a function is considered type-checked or not is somewhat unfortunate but I can live with the requirement to add a dummy-> Noneto type-check an__init__method that has no arguments besidesself.Reacted by Alexey Kinev, Juan Palacios, Shahriar Heidrich, Lenz Furrer, JeroenBos, Will Da Silva, Haopeng Huang, Marcin Wrochna, Felina Rivera Calzadillas (roguh), Stijn de Gooijer and 1 moreI'm leaning towards using the same convention everywhere (2) -- missing return type (if the signature has argument types) is the same as
-> None. One reason is that in many examples of PEP 484 / mypy code I've seen, people tend to omit return types, even though their intention was probably not to have anAnyreturn type -- they just did the easiest/shortest thing. Programmers are lazy, and we probably shouldn't make the rare case (Anyreturn) easier to write than the common case (Nonereturn) when we have a choice.Still, a convention of using an explicit
-> Noneeven when it's not needed (except for__init__, where it isn't useful, as you argued) may be a reasonable thing. However, perhaps it shouldn't be enforced.Here's some more stats (these are actually pretty interesting): In my corpus about half of all methods have signature
(self)-- but only a small fraction of these are__init__methods. Approx. 2/3 of(self)methods seem to haveNoneas the expected return type. Top-level functions that take no arguments but have aNonereturn type are much less frequent.I'm assuming those argument-less None-returning methods are some kind of pattern to set/clear specific flags? E.g. set_debug(). Would be interesting to look at these a bit more, since the stats look a bit suspicious. OTOH it's understandable that there aren't many top-level functions like that -- changing global state is frowned upon more than changing instance state.
I also realized that I'm not sure what your proposal is, exactly. Do you propose to assume ->None for all functions and methods that don't have an explicit return annotation? Or only for those that have at least one argument annotation? Or only for those that have no "return " in their body? (And how would the latter differ?)
32 remaining items
(In case somebody is looking for a script how to add None as return type to every
__init__in the codebase: https://gist.github.com/mristin/073a67f19e4133b9c97ed2269637b2f9The script handles also cases when the return type has been already specified. I couldn't make regular expressions work, so I resorted to AST parsing with asttokens.)
Reacted by Alain VaucherWhat is the status? Is final decision made? Is somebody assigned with the implementation? I can work on it, if there is no one available.
Reacted by Shahriar Heidrich, dlangeland, Lucas Rodrigues, Corey Cole and Filipe Peliz Pinto Teixeira@onlined We are happy to accept a PR that implements the proposal. Nobody is working on this yet.
Reacted by Shahriar Heidrich, Mohammed, Lucas Rodrigues and Corey Cole- added a commit that references this issue
on Oct 1, 2018 - added a commit that references this issue
on Oct 4, 2018 - added a commit that references this issue
on Mar 5, 2024
Code:
Error:
The return type is
Any(implicitly), andAnyshould be a valid return type for__init__.This was reported by Guido.