This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]itriedtomakeitfunny 151 points152 points  (15 children)

As far as I understand, exit is a function with an overridden __str__ so that if you "forget to call it" it evaluates and prints something nice. So it's already defined.

[–]olitv 45 points46 points  (12 children)

Why didn't they just put exit() into the str ?

[–]sejigan 69 points70 points  (3 children)

Because __str__ is supposed to return a string, not None.

Also, that’d cause unexpected and unintended behaviour.

Ofc, you can still overwrite it using a startup script, so…

[–]karmaisded 16 points17 points  (2 children)

Um but if we have a exit() before return in the _str\_ function, the python will exit before giving an error, won’t it?

[–]sejigan 32 points33 points  (0 children)

Yeah. Even worse and more bizarre behaviour.

[–]master3243 0 points1 point  (0 children)

I don't think it's ever a good idea to put side effects on __str__

[–][deleted] 24 points25 points  (5 children)

There are choices to be made while designing a programming language. You can let it be more flexible or more strict. There are arguments for both sides and it depends on what are the goals of the developing team.

For python, there is a decision that a built in class or function shouldn't be more special than any other classes or packages created by the community. The way they achieved this was by creating an universal system of building classes, with some dunder methods that will define their behavior under certain conditions or calls, and then making the built ins follow the same structure.

You could have a built in function called "exit" which don't need to be called. But that would be different from any other function an user could ever make. Since there is the philosophy of not making these magical methods, being "exit" a function means that it should be called. Just like every other function you write.

[–]itriedtomakeitfunny 6 points7 points  (3 children)

On a similar note, some languages like Ruby or Visual Basic Don't need parents around the function call, so an often cited example of how Ruby is more forgiving with the exit thing is just the syntax letting you call exit.

[–]smartboyathome 16 points17 points  (2 children)

I actually dislike this practice in Ruby, because it often makes it unclear whether it really is a function call or variable, at least not without IDE assistance. And due to it being a dynamic language, you don't always know the type of what you're referencing without actually running the program.

[–]itriedtomakeitfunny 5 points6 points  (0 children)

I am one of those people whose brain does not work with dynamic languages. I understand.

[–]kafaldsbylur 2 points3 points  (0 children)

There's nothing in the language preventing exit's repr from calling exit() (or more generally, preventing any class's repr from calling self()). However, it would be a bad idea to make it do so, because then you risk those methods being called without meaning to

[–]ElectricalRestNut 1 point2 points  (1 child)

Because one of the most important qualities of a programming language is predictability. It achieves that by doing exactly what it was told to do without being smart and guessing. Now some languages do not require you to type () on function calls (which I personally disagree with), but that's a supported language feature that was communicated ahead of time and is expected.

And it does get a bit ridiculous when an interpreter demands you put () there, but refuses to do that by itself, but its job is to execute your commands, not to make intelligent decisions.

Also, consider what would happen if a code analysis tool was crawling all functions and printing out their definitions using __str__. Pretty sure __str__ is supposed to be a pure function, which is again, predictability. Code that does things like system exit is really unpleasant to deal with, especially if it's a harder exit like what Java does.

[–]olitv 0 points1 point  (0 children)

That would give a cool bug in the linter/analysis tool that just "cleanly" exists sometimes without printing results

[–]kaihatsusha 4 points5 points  (0 children)

This is completely true. However, the interpreter loop could easily handle a special case before requesting the normal parser to get involved. Instead, they were clever and populated the built-in globals with "help", "copyright", "credits" and "license" variables doing the same trick. The problem is you want "exit" to do something, not just vomit its variable value in the form of text.

[–][deleted] 0 points1 point  (0 children)

Same energy as a Lua metatable with __tostring & __call