I'm surprised by the reactions to this. It's made immediately clear in the notebook that this is a fun look at some interesting behaviors of Python. There's no implication that there are bugs - I have no idea where anyone got that idea - except for the single actual bug in CPython that is mentioned (https://github.com/python/cpython/issues/54753).
I don't feel strongly about Python, but I do think CPython is a great way for people to learn a bit about how interpreted languages work. The code is quite easy to understand, and you can easily play with these interesting aspects of how languages and runtimes work. Does every Python programmer need to know the difference between `a = 256` and `a = 257`? No. Is it interesting? Yes. Should someone on your team know? Probably.
There's a lot of interesting stuff here. Understanding the difference between the conceptual idea of each line of code and what actually happens is fun, and, in some cases, important.
I think I know what you mean, but there's a line somewhere. Just because everyone doesn't understand the intricacies of the micro ops their cpu is using or every type of cache involved in various situations doesn't mean their computer is full of bugs.
Similarly, as long as the mental model of a Python programmer is in line with the results of executing some computation with Python, all is well.
It probably wasn't a good idea to start it out is showing string constants aren't optimized. If they wanted to talk about really unintuitive reference gotchas initialization in optional function parameters would have been a way better example.
I appreciate the sentiment, but even this is a good example of why this stuff is interesting. Because some strings _are_ optimized (using some definition of "optimized"), but only in some cases.
It's these quirky little things where the code doesn't exactly represent the way execution happens in most straightforward mental models of how execution works. Again, it's not bad and it's not unique to Python. It's just the nitty gritty of how some things work.
I will say that calling it "What The Fuck Python" is certainly more attention-grabbing than "Some Interesting Bits About Python Internals You Probably Don't Need To Know". What're you gonna do. :)
Who writes python this way? Good Python code, being such a flexible language, relies on conventions. Never seen the id() function in production code. "Is" is canonically used for "if var is None", not string comparisons. Python has a rich library of string methods, maybe use them?
You're finding bugs because you're using the language like a QA tester instead of the way its intended. If I saw string_var is "some_string" in a PR, I would reject it. Even the triple boolean comparison example should just be separated with an "and".
Read PEP20 and PEP8. A goal of Python is to be as readable as possible, not to conform to some spec. You may not agree with that design decision, but all these examples have an obviously more readable alternative which would work.
Jeez. It's like me complaining that Rust looks ugly (it does). The language was designed with a different set of priorities.
I mainly see this wtf pages as a hook to talk about the internals of a language/interpretor/etc. As you say, there's no point reading them as a criticism of the language because that's just not what production python looks like. I read the line of code and think 'can I imagine why it does that?' if I can I skip the explanation but sometimes I have absolutely no idea what's going on and they're the ones I like to read the detail on
> A goal of Python is to be as readable as possible, not to conform to some spec.
The goal of every programming language should be to conform to a spec that is as logical and consistent as possible. This prevents bugs by saving tons of mental effort having to worry about and look up edge cases, and not always catching them all.
I don't mind so much if end-user applications have bugs when they're used differently from intended, because their use cases are narrow. If Word crashes when I put 10,000 images on a single page, well obviously it's not built for that.
But I mind very much when programming languages have bugs, because they are the layer on top of which we build so many other things! The use cases for a programming language are almost infinitely wide.
For a language as widely used as Python, we should be testing it out like QA testers. All these inconsistencies add up to tons of bugs when you expect something to follow a consistent pattern and it doesn't.
> While some of the examples you see below may not be WTFs in the truest sense, but they'll reveal some of the interesting parts of Python that you might be unaware of. I find it a nice way to learn the internals of a programming language, and I believe that you'll find it interesting too!
the spirit in which that page is presented is different from what you seem to have taken it to mean
Which ones are bugs? I read the first few sections and glanced through the rest, but it's a long notebook. There were runtime-specific implementation details, operator precedence, genuine language footguns (like scoping rules), but no bugs that I saw.
Not all bugs are equal though, and if I'm only going to run across the bug when I'm standing on my head the third Wednesday of the month while singing Poker Face, it's a bit less concerning than one that happens only on days that end in "Y".
So we should put them into the bug tracking system and prioritize them accordingly. They're very useful to understand, but if one costs $100 every time it happens but it only happens once a year, vs a different bug that costs $1 every time it gets hit, but it happens every day, which one should get fixed first?
I think you're showing some despair in trying to avoid addressing the fact that there are indeed bugs. There is nothing suggesting bugs are obscure or random. You have a list of examples. Is it hard to acknowledge the problems are there? What exactly makes you feel threatened or personally attacked by a bug tracker?
I think you're reading a lot more emotion than exists into my comment. Despair? lol I'm just pointing out that QA testers (and good ones are worth their weight in gold) find bugs by doing things must users wouldn't even consider doing.
People learning the language or learning programming, something Python is supposed to be good for and is widely used for. Also, casual and intermittent users of Python, who might not hold all the details in their head because they work in multiple different languages. These categories would encapsulate the majority of Python users in practice, and all of them end up "QA testing" the language unintentionally and wasting time and effort figuring out inscrutable problems.
A better designed language could prevent a lot of these abuses, give informative error messages, or at the least warnings. These are not inevitable results of language flexibility, but in many cases just the result of poor design that got patched up imperfectly over time - which is understandable given Python's history, but doesn't make the problems go away.
Why are you being so defensive? Every language has bad or surprising (which IMHO is a synonym for bad) design choices. These are just a few of Python's. "You're using it wrong" seems like an unrelated criticism. Languages have the luxury of dictating how you use them.
No one, obviously. This is about understanding how the python interpreter functions.
>Python has a rich library of string methods, maybe use them?
How are you able to get the impression that any of this is a prescription on how python programming should be done? Nowhere does it claim to be, in fact if you read the actual content you would understand why these examples aren't the way to accomplish your goals.
Why are you so incredibly defensive when someone points out that a programming language contains weird behavior most people do not understand? This is true for every language. And almost always these examples are given so that people can refine their understanding of the language.
>Jeez. It's like me complaining that Rust looks ugly (it does). The language was designed with a different set of priorities.
Preach. We need more Python slander. It annoys me to no end when people trash JavaScript as if it was some exception in bad language design. Most languages have something bad that can be picked up and trashed on - some more than others.
Thank you! For years Ive been saying the sheer volume and fury by which Javascript is criticized is much more applicable to Python.
Both aren't perfect languages, and both are far from "the best" but it is maddening that Python get often picked as a darling when its implementation, ecosystem and tooling are fundamentally at odds with correctness and productivity.
I think people pick on js more because it's the only option for scripting frontend for the web, while if you don't like python there's probably another language that can be used instead.
I'd say people pick on JS because they don't learn it. They jump in, use it like python or C++, get frustrated it's not the same as what they're used to, vow to use it as little as possible. This was especially true before es5/es6 with var function scope and no class syntax.
The author of this notebook does not appear to agree with you. They describe Python in the beginning as "beautifully designed".
This is not idiomatic Python code. It's code designed to demonstrate Python performance optimizations, through use of relatively obscure Python functionality which exposes details of the internals in situations where you need that.
"its implementation, ecosystem and tooling are fundamentally at odds with correctness and productivity"
You'll need a lot more than just this notebook to argue for that position.
There could be generational gap here: for many people the first JS was ES5 or later, which is after the language was taken seriously and fixed. The difference between Python2 and ES1-ES3 was much bigger than between modern Python and modern JavaScript, so some protest against old bad fame is understandable.
But, if you have any suspicion that JavaScript has been treated unfairly: it hasn't. It is literally being replaced by TypeScript as we speak. The fact you have to invite people to come up with more Python slander is itself a testimony, JavaScript has never needed an invitation. Having lot more issues than others is what being exception means, and JavaScript is one.
> We need more Python slander. It annoys me to no end when people trash JavaScript as if it was some exception in bad language design.
Python's file system support across platforms is notoriously problematic or even outright broken. Wasn't this enough reason to put it lower in the quality bar than JavaScript?
JavaScript doesn’t even have file system support.. each runtime has its own tacked on implementation in the relevant “standard” library. Now which is notoriously problematic?
Given that it starts with a thing called "id" which is an implementation detail and probably should not be used for equality, it seems like this is a geniuinely well designed language :)
id is not an implementation detail, it's a core built-in function of the language. The value that it returns is an implementation detail, but the properties of the value it returns are not and perfectly valid to rely on.
The "properties of the value it returns" are, by design, implementation-dependent. You're only supposed to use the id function if you need to know something about what's going on under the hood. The id function is working exactly as intended: it's exposing aspects of the implementation, if for some reason you need to know that stuff.
It would not be a positive change to sacrifice Python performance in order to make the output of id() more intuitive. I've spent many hundreds of hours coding Python and I don't believe I ever used that function or saw someone else use it.
`id()` is documented and mandated to exist, and the result provides guarantees that make it suitable for identity (not equality) checks[1] (although of course you should normally use the `is` operator). Pitfalls involving it are generally really pitfalls with understanding object lifetimes[2].
JavaScript is far more jarring than Python depending on your background. They do both have issues of course, but that's the reason JavaScript gets "picked on" more.
If casting from a string to a type works with ints and floats, why not with bools? What possible justification is there?
And of course it doesn't need to work for "no" or "heckno!", that's silly. But it sure seems like it ought to work on whatever the official string representation is. And not produce:
>>> bool(str(False))
True
I'd honestly much prefer bool() threw an exception on receiving a string, rather than act the way it does now.
prefer bool() threw an exception on receiving a string, rather than act the way it does now.
That breaks the truthiness cornerstone of the language. You can write
a = 1 # or [], (), "yo", "false", 3.2, MyFooClass(), (1,), False
if a:
fire_ze_missles()
else:
declare_peace()
Upon encountering `a`, Python is evaluating bool(a). If that no longer works for strings, you now need a separate code path for determining a non-empty string.
It can short-circuit the brain upon reading a word that you know means false, but the Python rules are consistent. "Empty" is False, everything else is True.
> If casting from a string to a type works with ints and floats, why not with bools? What possible justification is there?
> I'd honestly much prefer bool() threw an exception on receiving a string, rather than act the way it does now.
They serve fundamentally different purposes. bool() is a truthiness check and not a type cast like int() and float(). It seems like a lot of people take issue with the name, because it was called something like istruthy() the discussion about it wouldn't be happening.
> bool() is a truthiness check and not a type cast like int() and float(). It seems like your issue is with the name of the function, because if it was more aptly named to something like istruthy() this discussion wouldn't be happening.
Right, the bug is in the inconsistent naming.
It's roughly as bad as having arithmetic operators named +, -, *, / that perform arithmetic as usually understood, except that + actually performs XOR and is documented to do so.
> If casting from a string to a type works with ints and floats, why not with bools? What possible justification is there?
There's no type casting in Python. int(), float() and bool() just create objects of their respective types, passing the arguments to the initializer. They are not fundamentally different from, say, PostgresqlDriver() in this regard.
Different classes will provide different ways to construct objects depending on what is most useful. For int and float, the most useful thing to do when receiving a string is to parse it; for bool, the most useful thing to do is to check its truthiness; for PostgresqlDriver, the most useful thing to do is to interpret it as a database URL and connect to it.
If you search "type casting in Python" you will find lots of articles explaining how to use int(), str(), etc. to cast. This is a common and well-documented concept, even if it's different under the hood from e.g. C.
The idea that you'd name a function bool() and have it act differently is a deeply confusing design decision that it's understandable people would get misled by and frustrated with. This is a function named after a fundamental type. It's not a random user-defined constructor.
bool() is not a function named after a fundamental type. bool is a type. As with any type, you can call bool(...) to construct a new object of that type.
A good tutorial will reflect this fact and use appropriate language.
Both the idea that you can "cast", and the idea that bool is a function, are misunderstandings rooted in the idea that Python should be like C in all ways.
> There's no type casting in Python. int(), float() and bool() just create objects of their respective types, passing the arguments to the initializer
int() and float() behave like type casting, bool() does not. It's a truthiness check that would be more aptly named istruthy(). In python non-empty strings are truthy, so providing any string except for "" to bool() will return True, including "False".
In C, cast converts a value to another type (or a more or less qualified version of the same type). What that conversion means depends on the source and destination type.
A cast of insert a conversion that wouldn't take place:
1/2 vs 1/(double)2
or coerce a conversion that otherwise requires a diagnostic:
bar *b = 0;
foo *f = (foo *) b;
Loosely speaking, casting is another name for coercion, which refers to making a conversion happen that might not otherwise.
Implicit conversion, which is what you're mostly referring to here, is not required. It happens in some languages, and not others.
Because I'm tired, and this is a very basic topic, I'm afraid I'm just going to rip from Wikipedia:
> In most ALGOL-like languages, such as Pascal, Modula-2, Ada and Delphi, conversion and casting are distinctly different concepts. In these languages, conversion refers to either implicitly or explicitly changing a value from one data type storage format to another, e.g. a 16-bit integer to a 32-bit integer. The storage needs may change as a result of the conversion, including a possible loss of precision or truncation. The word cast, on the other hand, refers to explicitly changing the interpretation of the bit pattern representing a value from one type to another. For example, 32 contiguous bits may be treated as an array of 32 Booleans, a 4-byte string, an unsigned 32-bit integer or an IEEE single precision floating point value. Because the stored bits are never changed, the programmer must know low level details such as representation format, byte order, and alignment needs, to meaningfully cast.
> In the C family of languages and ALGOL 68, the word cast typically refers to an explicit type conversion (as opposed to an implicit conversion), causing some ambiguity about whether this is a re-interpretation of a bit-pattern or a real data representation conversion. More important is the multitude of ways and rules that apply to what data type (or class) is located by a pointer and how a pointer may be adjusted by the compiler in cases like object (class) inheritance.
The difference between implicit conversion and explicit is just syntactic sugar: do you have to put some visible tokens into the code to allow a conversion to happen, or not.
Implicit conversion can be seen as the compiler deciding among several possible conversions (or possibly just one) and inserting the coercion/casting operator into the intermediate code to make it happen.
(Of course, this can be a run-time decision based on dynamic types also, but the reasoning is the same. E.g. the run-time sees that a plus operation is adding an integer and float, and can either signal an error (requiring the program to be repaired by inserting a conversion operation into the expression), or just do that conversion, like the integer operand to float.
> I'd honestly much prefer bool() threw an exception on receiving a string, rather than act the way it does now.
`bool` would have no value if it threw an error in this case because if strings can't be passed to it, then no other type would sensibly work either. It would basically just become `bool(False) -> False` and `bool(True) -> True`.
> because if strings can't be passed to it, then no other type would sensibly work either
It's pretty standard to convert integers to bools, where 0 becomes False and everything else becomes True. That is absolutely sensible and useful current behavior.
Nope, because str(False) produces "False", not "".
Not consistent at all.
There's nothing consistent about bool(str(False)) == True.
It's standard in computing for zero integers to represent False. Heck, bool is a subclass of int. Extending that to the length of strings is where things start to go off the rails in terms of consistency... and why you would ever even want that is beyond me.
Sounds like your complaint is with str() and not bool().
Which becomes a different issue - what do you think str(True) and str(False) should produce. Integer representations? That then makes other things unintuitive with changing the form of a boolean.
So therefore bool('False') == False would be consistent.
And bool('foo') would produce an exception the exact same way int('foo') and float('foo') already do.
Who on earth thought it was a good idea that 'foo' could or should be interpreted as a boolean...?? The entire concept of 'truthiness' outside of ints is unhelpful. It's just asking for bugs. That the length of a string, rather than its content, should determine a boolean value is one of the more bizarre things I've come across in programming languages. Use len() if you want that. It's only 5 extra characters.
Honestly once you really think about it just about nothing about these decisions ever makes any real sense which is why JS is made fun of constantly. It's nonsense functions running on nonsense data. "if some_non_bool_value:" should not be valid syntax and truthy and falsy is a concept that would get you locked up in an asylum if you decided to explain it to a doctor.
If I understand raku correctly, the Python example would be more like this:
say "False".Bool
or:
say "False".so
Both of which display True.
Leaving out the quotes means you're testing something else entirely.
Note that the Python example is converting (via `bool`) a string with the text "false" (which would make more sense as "False" which is the actual name of `False` in Python) into a boolean value. It's consistent with how Python behaves, in that the empty string (like in Raku from what I can tell) is false-y and non-empty strings are truth-y. It would be weird to have this use the else branch:
As mentioned else thread, the problem is not so much that book("false") evaluates to true, which is consistent with a lot of the language, but that int("1") evaluates to 1.
Generally python is much better than some other dynamic languages in avoiding stringly typed confusion, but the convenience of the int from string constructor set expectations that the rest of the language can't sanely meet.
String to type construction should have been relegated to a dedicated syntax in all cases.
The first third of this seems to just be complaining that it's not obvious when two objects are actually the same object, and that if you mistake identity-related operators for other operators then you'll have a bad day.
That's a fair critique. It's a little weird that `is` and friends have dedicated, short, nice syntax.
On the other hand, most compiled languages are compatible with an implementation which admits the following optimization:
The language usually doesn't guarantee that different immutable variables have dedicated memory on the stack, or that they even live on the stack in the first place.
That's the same class of error we're seeing here, and even among popular interpreted languages Python is by no means unique. I might get to the bottom 2/3 of the doc later, but the rest was off-putting enough that I don't know I'll bother.
My favourite Python misfeature is that `x += y` sometimes does the same thing as `x = x + y` and sometimes doesn't. This leads to minor WTFs like this:
>>> a = b = (1, 2) >>> a = b = (1, 2)
>>> a = a + (3, 4) >>> a += (3, 4)
>>> b >>> b
(1, 2) (1, 2)
>>> a = b = [1, 2] >>> a = b = [1, 2]
>>> a = a + [3, 4] >>> a += [3, 4]
>>> b >>> b
[1, 2] [1, 2, 3, 4]
And major ones like this:
>>> a = ([1, 2], ['one', 'two'])
>>> a[0] += [3, 4]
TypeError: 'tuple' object does not support item assignment
>>> a
([1, 2, 3, 4], ['one', 'two'])
The operation raises an exception even though it succeeds!
Your first example has to do with the fact that tuples are copied by value, whereas lists are "copied" by reference. This is a special case of an even larger (IMO) misfeature, which is that the language tries very, very hard to hide the concept of a pointer from you. This is a rampant problem in memory-managed languages; Java has similar weirdness (although it's at least a bit more consistent since there are fewer primitives), and Go is doubly odd because it does have a user-controllable value vs. pointer distinction but then hides it in a lot of cases (with the . operator working through pointers, and anything to do with interfaces).
I think the whole thing does a misservice to novice or unwary programmers. It's supposed to be easier to use because you "don't have to worry about it" - but you really, really do. If you're not familiar with most of these details, it's way too easy to wander into code that behaves incorrectly.
> This is a special case of an even larger (IMO) misfeature, which is that the language tries very, very hard to hide the concept of a pointer from you.
When I came to Python from Perl, it only took me about one day of Python programming to realize that Python does not have references the same way that Perl does. This is not flame bait. Example early questions that I had: (1) How do create a reference to a string to pass to a function? (2) How do I create a reference to reference? In the end, I settled on using list of size one to accomplish the same. I use a similar trick in Java, but an array of size one. In hindsight, it is probably much easier for junior programmers to understand the vale and type system in Python compared to Perl. (Don't even get me started about the readability of Perl.) Does anyone still remember the 'bless' keyword in Perl to create a class? That was utterly bizarre to me coming from C++!
> Your first example has to do with the fact that tuples are copied by value, whereas lists are "copied" by reference.
My mental model for Python is that everything is '"copied" by reference', but that some things are immutable and others are mutable.
I believe that's equivalent to immutable objects being 'copied by value' and mutable ones being '"copied" by reference', but "everything is by reference" more accurately reflects the language's implementation.
Yeah, I know that's how it works under the hood - and why you have things like all integers with values in [-5, 256] being assigned to the pre-allocated objects - but I don't think it's a particularly useful model for actually programming. "Pass-by-reference with copy-on-write" is semantically indistinguishable from "pass-by-value".
> I don't think it's a particularly useful model for actually programming
I think “everything is by reference” is a better model for programming than “you need to learn which objects are by reference and which are by value”. As you say, the latter is the case in Go, and it’s one of the few ways the language is more complex than Python.
You could argue that in Python you still have to learn which objects are mutable and which are immutable - but if it weren’t for bad design like `+=` that wouldn’t be necessary. A object would be mutable if-and-only-if it supported mutating methods.
> I'd argue the first one is not a smaller WTF than the latter.
I think the first one probably arises more frequently, but at least there's a fairly straightforward explanation: "`x += y` acts in-place if-and-only-if `x` is mutable".
The second example is more niche but it's much harder to explain what's going on - it can only be understood by first understanding the first example, and what it implies about the implementation of `+=`.
Aside: In the process of submitting this, I learnt that Hacker News apparently has an anti-censorship title transform built-in. I submitted it with the title taken from the notebook - "What the f*ck Python!" - and HN automatically removed the ! and changed "f*ck" to "Fuck".
Some of these just seem to be using Python out of spec and being surprised that implementation details exist, and misunderstanding boolean expressions.
Python's documentation today[1] is clearly more expansive, better formatted and more readily comprehensible than its documentation from 2001[2] (or even 2008[3]). There are multiple entire sections of documentation now that didn't exist before. Standards were just lower back then, partly because a larger percentage of "programmers" were accustomed to wrestling with beasts like C, and partly because systems were much smaller.
> Python's documentation today[1] is clearly more expansive, better formatted and more readily comprehensible than its documentation from 2001[2] (or even 2008[3]).
Documentation is not a specification. Specifications cover all behavior that should be expected, and specify which behavior is implementation-defined or undefined. If something isn't defined them this is a failure in the specification that requires fixing. The point of a specification is to allow independent parties to do clean room implementations that can be used interchangeably.
Hi, author of the repo (https://github.com/satwikkansal/wtfpython) here, pleasantly surprised to wake up to this thread about it. The intention of the project is not to put Python in a bad light relative to other languages, the intention was to explore it more deeply through (sometimes contrived) snippets that give non-obvious outputs. I believe for a regular Python programmer, the "Slippery Slopes" section is a must-know. The first section (Strain your brain), does contain some patterns that you may not ever see in real world, the goal there is to just learn about internals. Agreed some of them are very obvious for a well-learned programmer, and some of them aren't even Python specific, it does help a lot of beginner to intermediate programmers. I remember it being given as a supplementary material in one of the CS-based course.
It is curated with something-for-everyone approach, I expect people to learn a thing or two about Python / CS after going through the snippets. I haven't updated it in a while in a major way, but looking at the feedback I'll try to organise them better, and add new snippets (I have a long personal list that I maintain) in a future release!
I'm happy to complain about python but I got like a third into it and didn't find any actual wtfs.
Java as contrast has interning wtfs because == between java objects does essentioally what python's `is` does. Python actually made a good choice here and made == do what you'd expect.
Is anyone surprised by `(1 > 0) < 1`? There are languages where it will be rejected because of types, but what else would you expect it do than compare a boolean true to 1?
I think there's a few in there. But most people aren't going to write code that way. It's good to know, and keep in your head, but if you have to think too hard in the future if a piece of code is correct, it's probably better to rewrite it anyway. And weird code like that shows up like a sore thumb in reviews.
This is great. Also to be a little meta, this link was posted in 2019 (https://news.ycombinator.com/item?id=21862073) and got the same sort of comments (how js gets flak, and python doesn't)
I didnt make it past the long list of id() misuses, but its a rookie mistake to confuse referential equality with value equality, and when doing so youre in for a bad time.
Small numbers are "eq" in python. It saves space and time, because probably those numbers dont even exist, and the value is derived from the pointer value.
>>> a=3
>>> b=3
>>> a is b
True
>>> a=1e20
>>> b=1e20
>>> a is b
False
But in Commonlisp all numbers are "eq". Very strange. Is there heap of numbers and once created, they reuse those?
[4]> (setq b 1E20)
1.0E20
[5]> (setq a 1E20)
1.0E20
[6]> (eq a b)
T
In Common Lisp, in fact, no numbers are required to be eq! Even (eq 0 0) is not required by ANSI CL to return true.
Why your Lisp implementation produces true for (eq a b) is probably because of the following.
Firstly, in Common Lisp there are single and double precision floats. Floating point tokens written with E or e are single. An implementation is allowed to map that to a 32 bit float. If the implementation has 64 bit values, it can easily arrange for single precision floats to be unboxed without any special tricks; they need only half the word. Try evaluating with 1D+20 to see what that does.
But even 64 bit values can be unboxed with a trick called NAN tagging.
I implemented NAN tagging in TXR Lisp (which has only double-precision floats), enabled on 64 bit targets:
64 bit machine:
1> (eq 1E20 1E20)
t
2> (sizeof val)
8
32 bit machine:
1> (eq 1E20 1E20)
nil
2> (sizeof val)
4
On 32 bits, floating-point objects are on the heap.
It's pretty important to have unboxed floats for intense numerical work.
It's not guaranteed that eq will return true for two numbers of the same value in Common Lisp. It happens to work much of the time, but it is never guaranteed to work. For a given implementation it could even be true all the time (as it's not disallowed), but for portability it should not be relied upon.
I may be wrong, but think a lot of it is undefined behavior. Interpreter may choose to recycle objects or it may not. In case of integers, low enough number in different parts of the program, will be represented by the same object. But if the integer is high enough, different objects will be created. That’s my understanding, but I could be wrong.
Real in what way? I'm not too familiar with them, but they seem about on par with the environment for JavaScript that is html files or "web pages".
Making a html document so someone can easily run some JavaScript, seems like the closest parallel to making a jupiter notebook so someone can easily run some python.
Agreed. Even so, you'd be surprised to find that some companies run them in production. I never got the idea behind that, maybe someone that engages in such a practice could enlighten me. I have yet to hear a good rationale.
Programming encompasses things other than production environments and software engineering. Notebooks are fine programming environments for prototyping, exploration and training.
Yeah, this simply doesn't work for me; ie the expressions did not evaluate, and output was not made visible.
I have never had a successful experience writing or running programs in notebooks like this--although I have not, arguably, tried with high stakes.
As soon as the stakes rise that far, however, I reach for a whole-ass Unix environment with a shell, a filesystem, and a stateful process model.
Is this a crutch? Is it too much overhead? It's arguably absurd that I even ask. (Edit: It's actually killing me how ambiguous I'm being, right now. There's no winning.)
> As soon as the stakes rise that far, however, I reach for a whole-ass Unix environment with a shell, a filesystem, and a stateful process model.
I don't understand what you mean by "reach for". Don't, for example, Linux desktop users have all these things at hand all the time? For me it would take much more effort to set up a "notebook" than a virtual environment.
For you, sure, but for someone that lives and breathes notebooks, running jupyter notebook and going to localhost:8888 already happened, so it's opening a terminal that's the extra step.
You would start the Jupyter application, then use a browser to go to localhost:8888 :)
To start the application, you would typically use the start menu on Windows or the Dock on MacOS. Alternatively on MacOS you could point the Finder at the /Applications folder, then start the application from the Finder window.
(This is not meant as an endorsement of notebooks.)
Some ways of programming in Python require a LOT of mental effort. And some don't. For example you can do a lot in one line in Python, but I usually have to write extensive comments and references to external docs to keep track of what that one line is doing. I think sometimes it would be easier if I just had to write 3 or 4 lines of self evident code and use that as docs instead.
Having written python professionally for more than a decade, I highly recommend not writing complex one-liners -- unless there's some real benefit to doing so.
The more one-liners there are in the code base, the more impenetrable and harder to debug the code is. Unrolling nested functions and switching comprehensions to lists and if statements often make it cleaner and easier to write unit tests for.
If you rely upon code coverage for unit tests, a complex list comprehension won't help you see if you have hit the branch cases.
Personally, I always find multi-layered list comprehensions the hardest to read... And debugging list comprehensions is basically impossible in my experience.
It’s reassuring that a lot (feels like a majority) of these are noted as resolved or turned into errors that make more sense than confusing behaviors in later editions of the language.
Python 3.5, which is what the WTFs use by default, is relatively ancient.
Fp32 or fp64 in a strongly typed language would be no problem as hash keys. Yes, they're imprecise for math but the 64 bits of data should work just like a long as a hash key.
No, they are not suitable for use as hash keys unless you are treating them as opaque binary data. And if you are there, you are better putting your values in some type that means opaque binary data for the start.
They are perfectly suitable as long as you know what you are doing. Here's a real use case: when compiling a scripting language, map float constants to slots in a "constant table" so that all constants with the same value share the same slot.
It's a bit like when non-JS programmers complain about JavaScript. It's mostly nonsense. That's not to say that JavaScript doesn't have flaws and footguns, but the opinion of novices in the language is not who I am interested in learning from. Python is the same. It has flaws and idiosyncrasies, especially in the standard library (cough cough xml.ElementTree), but the core language is fairly consistent and predictable. Expecting casting functions like bool() to parse strings like JavaScript does makes little sense.
Haha these aren't even wtfs! the id changes or not? oh noooo, people use the id() function so much, I've literally seen it 0 times ever in production code. This is nothing like the JS wtfs where there is no way to predict what it will do, and it violates it's own patterns.
edit: to be more clear, I have run into a lot of JS wtf's trying to write code for work. you just run into bugs all the time where JS doesn't do anything like what would be the obvious thing to do, or the thing that works 99.99% of the time fails depending on the value of the string being 'magic'. with Python I rarely if ever learn some bizarre arcane thing accidentally because it almost always behaves as expected. If you think there's an equivalence here you are just a JS fanboy, JS was a language that evolved largely because of hacks vendors put in and those insane hacks got standardized because it was too late, despite being insane. Python was specifically and carefully designed to minimize unexpected behavior.
> It doesn't make sense no matter which way you evaluate it
It makes sense when you evaluate it the way it's intended to be understood (https://stackoverflow.com/questions/6074018). There are other languages that do this, and it was consciously designed to work this way. The idea that you should have to evaluate one of the operators "first" may make sense to someone trying to implement a parser with techniques from the 70s, but for languages like Python, the design is clearly more about what makes sense for the code author.
Arguably it's surprising that `in` is treated as on par with `==`. But on the other hand, even someone trying to implement a parser with techniques from the 70s would have to admit that both are "relational operators".
There is no literal `and` operation occurring here, instead it first does the `False == False`, then jumps if false or continues to the second comparison.
For the literals we've given it, it'll go on to the second comparison which will result in false since `False` is not in `(True,)`.
EDIT: And here's the non-chained version disassembled:
a = False
b = False
c = [False]
(a == b) and (b in c)
is less roughly
a = False
b = False
if not (a == b):
# short circuits https://news.ycombinator.com/item?id=44619401
return False
c = [False]
if not (b in c):
return False
return True
Python is my second language (after C++) and for me the surprising thing here is not "chained comparisons are weird" but rather "`in` is a comparison operator."
So for example `1 in [1,2] in [[1,2],[3,4]]` is True... but off the top of my head I don't see any legitimate use-case for that facility. Intuitively, I'd think a "comparison operator" should be homogeneous — should take the same type on LHS and RHS. So, like, "is-subset-of" could sanely be a comparison operator, but `in` can't be.
>and for me the surprising thing here is not "chained comparisons are weird" but rather "`in` is a comparison operator."
Python documentation calls it a "comparison operator" (https://docs.python.org/3/reference/expressions.html#compari...) but a broader, better term is "relational operator". It expresses a possible relationship between two arguments, and evaluates whether those arguments have that relationship. In this case, "containment" (which intuitively encompasses set membership and substrings).
A finer distinction could have been made, arguably, but it's far too late to change now. (And everyone is traumatized by the 2->3 transition.)
Ah, `in` for strings satisfies my intuition re homogeneity. So I guess that makes sense enough (although if I ran the zoo I wouldn't have done it!). Thanks!
I have used id() in production code, but the circumstances were extremely specific: It was in some monitoring-related code that was trying to dig down into the guts of the interpreter for the purposes of tracking memory usage on the level of individual pages. It was a whole thing.
Which is to say: Users of Python almost certainly don't want to call id(), unless they are mucking around with something related to the functioning of the interpreter itself. The only other circumstance in which I've used it in anything like real code was in a quick hack where what I really wanted was to put a bunch of dicts in a set, but you can't do that, so I made a set of their IDs instead (but this wasn't in what I would call "production code").
In general, most of the "wtfs" listed here are pretty banal. If you're using `is` on immutable objects, you're basically just asking for trouble. The functioning of string and integer interning and constant folding are implementation curiosities, and if you ever write code in which such differences matter, then you have made an error.
I don't think either reach the real wtfs of their language. id() does some weird things with string interring, javascript coerces cross-type arithmetic into weird nonsense instead of throwing a type error. Whatever, neither of these come up in the real world.
Builds so messy that you need to resort to venv, every second DOM API being someFunc(thirdMostImportantArgument, { mostImportantArgument: val }), these are the real wtfs.
> I have run into a lot of JS wtf's trying to write code for work
JS certainly does have plenty of wtfs but honestly, when sticking to somewhat modern JS, it's not so bad anymore these days. Function-scoping-shenanigans can mostly be circumvented by simply sticking to const/let, use a 3rd-party-library like date-fns instead of the dumpsterfire that is native Date and you're almost there - throw typescript into the mix and I have a very hard time imagining running into the meme-worthy JS-warts on a daily basis.
I think they are pretty bad tbh apart from the ID one. Everyone knows about the JS quirks so I think it’s good to know sometimes equality in Python strings can behave extremely strangely and the other issues mentioned. Why does it have to be defended “my language is better than your language”? However I’d like to say Elixir is near perfect, thanks :-)
No you misread it, equality for python strings works exactly the way you expect. the id() value (which is basically the memory address) might not change if you concatenate strings, but literally nobody ever uses id for anything ever. You would use hash() which does change appropriately.
edit: 'wtf!' is always equal to itself. Maybe you are thinking of `is`, which just compares the value of id(). `is` can't be used for comparisons, saying it's a WTF is just being unfamiliar with basics. `is` is not used for equality comparisons. This is how more or less every language works, comparing pointers is not the same as comparing strings in C/rust/etc. Comparing object identity is not the same as testing equality in Java.
I think honestly it is you who misread my comment... I was saying the id() issue isn't a real problem, the weirdness around "wtf!" not being equal to itself under some circumstances is very confusing. Seems Google Colab isn't loading here.
They may or may not be stored in the same place depending on your compiler (clang and gcc yes, msvc no, at least with just a quick default build). If you actually want to do an equality check, you need to use `==` (in Python) which would be like using `strcmp` in C.
> "wtf!" not being equal to itself
It is equal to itself, but multiple variables referencing that string may not hold references to the same (literal, in memory) string.
But "is" doesn't check equality. It checks reference equality. Maybe the real wtf here is they made that check a temptingly short infix operator rather than a function named reference_equals().
Like I've been saying in other recent submissions, since there seems to be a trend for similar discussion: in Python you find WTFs by trying to probe how Python works, pushing deliberately over-flexibly customization hooks to their limits, etc. In JS you find them by merely trying to call builtins with dodgy inputs.
For the former: technically they are about as undefined as they could be on read. On write, it's a different matter (you could decide to resize the array under the hood). Even so, I think arrays with a particular size always were simply an illustration that there is a point where you have to allocate a physical resource to something entirely imaginary and that's where infinite bounds are no longer acceptable. In the 80's that was '256', in the 90's '65535', nowadays it is more like '4G' but no matter how far we advance there are still hard limits. And on the wire it gets even more constrained.
I'm surprised by the reactions to this. It's made immediately clear in the notebook that this is a fun look at some interesting behaviors of Python. There's no implication that there are bugs - I have no idea where anyone got that idea - except for the single actual bug in CPython that is mentioned (https://github.com/python/cpython/issues/54753).
I don't feel strongly about Python, but I do think CPython is a great way for people to learn a bit about how interpreted languages work. The code is quite easy to understand, and you can easily play with these interesting aspects of how languages and runtimes work. Does every Python programmer need to know the difference between `a = 256` and `a = 257`? No. Is it interesting? Yes. Should someone on your team know? Probably.
There's a lot of interesting stuff here. Understanding the difference between the conceptual idea of each line of code and what actually happens is fun, and, in some cases, important.
Bad requirements are bugs.
Bugs in understanding. Bugs in good taste.
A roach in your soup is a bug, even if the recipe says it should be there.
I think I know what you mean, but there's a line somewhere. Just because everyone doesn't understand the intricacies of the micro ops their cpu is using or every type of cache involved in various situations doesn't mean their computer is full of bugs.
Similarly, as long as the mental model of a Python programmer is in line with the results of executing some computation with Python, all is well.
It probably wasn't a good idea to start it out is showing string constants aren't optimized. If they wanted to talk about really unintuitive reference gotchas initialization in optional function parameters would have been a way better example.
I appreciate the sentiment, but even this is a good example of why this stuff is interesting. Because some strings _are_ optimized (using some definition of "optimized"), but only in some cases.
It's these quirky little things where the code doesn't exactly represent the way execution happens in most straightforward mental models of how execution works. Again, it's not bad and it's not unique to Python. It's just the nitty gritty of how some things work.
I will say that calling it "What The Fuck Python" is certainly more attention-grabbing than "Some Interesting Bits About Python Internals You Probably Don't Need To Know". What're you gonna do. :)
I don't know I think "Python doesn't optimize <x>" is one of the least surprising classes of statements in programming.
Who writes python this way? Good Python code, being such a flexible language, relies on conventions. Never seen the id() function in production code. "Is" is canonically used for "if var is None", not string comparisons. Python has a rich library of string methods, maybe use them?
You're finding bugs because you're using the language like a QA tester instead of the way its intended. If I saw string_var is "some_string" in a PR, I would reject it. Even the triple boolean comparison example should just be separated with an "and".
Read PEP20 and PEP8. A goal of Python is to be as readable as possible, not to conform to some spec. You may not agree with that design decision, but all these examples have an obviously more readable alternative which would work.
Jeez. It's like me complaining that Rust looks ugly (it does). The language was designed with a different set of priorities.
I mainly see this wtf pages as a hook to talk about the internals of a language/interpretor/etc. As you say, there's no point reading them as a criticism of the language because that's just not what production python looks like. I read the line of code and think 'can I imagine why it does that?' if I can I skip the explanation but sometimes I have absolutely no idea what's going on and they're the ones I like to read the detail on
> A goal of Python is to be as readable as possible, not to conform to some spec.
The goal of every programming language should be to conform to a spec that is as logical and consistent as possible. This prevents bugs by saving tons of mental effort having to worry about and look up edge cases, and not always catching them all.
I don't mind so much if end-user applications have bugs when they're used differently from intended, because their use cases are narrow. If Word crashes when I put 10,000 images on a single page, well obviously it's not built for that.
But I mind very much when programming languages have bugs, because they are the layer on top of which we build so many other things! The use cases for a programming language are almost infinitely wide.
For a language as widely used as Python, we should be testing it out like QA testers. All these inconsistencies add up to tons of bugs when you expect something to follow a consistent pattern and it doesn't.
> While some of the examples you see below may not be WTFs in the truest sense, but they'll reveal some of the interesting parts of Python that you might be unaware of. I find it a nice way to learn the internals of a programming language, and I believe that you'll find it interesting too!
the spirit in which that page is presented is different from what you seem to have taken it to mean
You seem to have missed the entire purpose of the post.
It's not to give "best practices" but to unveil gotchas and peculiarities in the language implementation and internals.
>A goal of Python is to be as readable as possible, not to conform to some spec.
This doesn't even make sense.
> You're finding bugs because you're using the language like a QA tester instead of the way its intended.
What do you think this means? Are the bugs there or not?
Which ones are bugs? I read the first few sections and glanced through the rest, but it's a long notebook. There were runtime-specific implementation details, operator precedence, genuine language footguns (like scoping rules), but no bugs that I saw.
Not all bugs are equal though, and if I'm only going to run across the bug when I'm standing on my head the third Wednesday of the month while singing Poker Face, it's a bit less concerning than one that happens only on days that end in "Y".
So we shouldn't learn about them? They're offensive to enumerate? No, they're useful to understand.
So we should put them into the bug tracking system and prioritize them accordingly. They're very useful to understand, but if one costs $100 every time it happens but it only happens once a year, vs a different bug that costs $1 every time it gets hit, but it happens every day, which one should get fixed first?
I think you're showing some despair in trying to avoid addressing the fact that there are indeed bugs. There is nothing suggesting bugs are obscure or random. You have a list of examples. Is it hard to acknowledge the problems are there? What exactly makes you feel threatened or personally attacked by a bug tracker?
I think you're reading a lot more emotion than exists into my comment. Despair? lol I'm just pointing out that QA testers (and good ones are worth their weight in gold) find bugs by doing things must users wouldn't even consider doing.
https://news.ycombinator.com/item?id=25851770
> Who writes python this way?
People learning the language or learning programming, something Python is supposed to be good for and is widely used for. Also, casual and intermittent users of Python, who might not hold all the details in their head because they work in multiple different languages. These categories would encapsulate the majority of Python users in practice, and all of them end up "QA testing" the language unintentionally and wasting time and effort figuring out inscrutable problems.
A better designed language could prevent a lot of these abuses, give informative error messages, or at the least warnings. These are not inevitable results of language flexibility, but in many cases just the result of poor design that got patched up imperfectly over time - which is understandable given Python's history, but doesn't make the problems go away.
Why are you being so defensive? Every language has bad or surprising (which IMHO is a synonym for bad) design choices. These are just a few of Python's. "You're using it wrong" seems like an unrelated criticism. Languages have the luxury of dictating how you use them.
“Is” checks memory location of the values.
>Who writes python this way?
No one, obviously. This is about understanding how the python interpreter functions.
>Python has a rich library of string methods, maybe use them?
How are you able to get the impression that any of this is a prescription on how python programming should be done? Nowhere does it claim to be, in fact if you read the actual content you would understand why these examples aren't the way to accomplish your goals.
Why are you so incredibly defensive when someone points out that a programming language contains weird behavior most people do not understand? This is true for every language. And almost always these examples are given so that people can refine their understanding of the language.
>Jeez. It's like me complaining that Rust looks ugly (it does). The language was designed with a different set of priorities.
The only person complaining is you.
Preach. We need more Python slander. It annoys me to no end when people trash JavaScript as if it was some exception in bad language design. Most languages have something bad that can be picked up and trashed on - some more than others.
Thank you! For years Ive been saying the sheer volume and fury by which Javascript is criticized is much more applicable to Python.
Both aren't perfect languages, and both are far from "the best" but it is maddening that Python get often picked as a darling when its implementation, ecosystem and tooling are fundamentally at odds with correctness and productivity.
I think people pick on js more because it's the only option for scripting frontend for the web, while if you don't like python there's probably another language that can be used instead.
I'd say people pick on JS because they don't learn it. They jump in, use it like python or C++, get frustrated it's not the same as what they're used to, vow to use it as little as possible. This was especially true before es5/es6 with var function scope and no class syntax.
Unless you're doing AI, in which case you're royally screwed.
You can get by with R and reticulate.
the Python ecosystem for AI is far ahead than R, sadly (or not :-) )
Reticulate is a bridge that lets you use Python from R so that you don't have to suffer Python the language.
The author of this notebook does not appear to agree with you. They describe Python in the beginning as "beautifully designed".
This is not idiomatic Python code. It's code designed to demonstrate Python performance optimizations, through use of relatively obscure Python functionality which exposes details of the internals in situations where you need that.
"its implementation, ecosystem and tooling are fundamentally at odds with correctness and productivity"
You'll need a lot more than just this notebook to argue for that position.
There could be generational gap here: for many people the first JS was ES5 or later, which is after the language was taken seriously and fixed. The difference between Python2 and ES1-ES3 was much bigger than between modern Python and modern JavaScript, so some protest against old bad fame is understandable.
But, if you have any suspicion that JavaScript has been treated unfairly: it hasn't. It is literally being replaced by TypeScript as we speak. The fact you have to invite people to come up with more Python slander is itself a testimony, JavaScript has never needed an invitation. Having lot more issues than others is what being exception means, and JavaScript is one.
> We need more Python slander. It annoys me to no end when people trash JavaScript as if it was some exception in bad language design.
Python's file system support across platforms is notoriously problematic or even outright broken. Wasn't this enough reason to put it lower in the quality bar than JavaScript?
JavaScript doesn’t even have file system support.. each runtime has its own tacked on implementation in the relevant “standard” library. Now which is notoriously problematic?
> JavaScript doesn’t even have file system support..
https://nodejs.org/api/fs.html
node is a js runtime, not the official spec, you make no point in refuting the above.
Given that it starts with a thing called "id" which is an implementation detail and probably should not be used for equality, it seems like this is a geniuinely well designed language :)
id is not an implementation detail, it's a core built-in function of the language. The value that it returns is an implementation detail, but the properties of the value it returns are not and perfectly valid to rely on.
The "properties of the value it returns" are, by design, implementation-dependent. You're only supposed to use the id function if you need to know something about what's going on under the hood. The id function is working exactly as intended: it's exposing aspects of the implementation, if for some reason you need to know that stuff.
It would not be a positive change to sacrifice Python performance in order to make the output of id() more intuitive. I've spent many hundreds of hours coding Python and I don't believe I ever used that function or saw someone else use it.
`id()` is documented and mandated to exist, and the result provides guarantees that make it suitable for identity (not equality) checks[1] (although of course you should normally use the `is` operator). Pitfalls involving it are generally really pitfalls with understanding object lifetimes[2].
[1]: https://docs.python.org/3/library/functions.html#id
[2]: see e.g. https://stackoverflow.com/questions/52096582
JavaScript is far more jarring than Python depending on your background. They do both have issues of course, but that's the reason JavaScript gets "picked on" more.
wat
https://www.destroyallsoftware.com/talks/wat
I came here to say that. That was an eye opening intro to Javascript for me.
Yeah python deserves a good bashing as well, it's about as bad as JS in this regard. I mean for bool("false") resolves to True ffs.
That is entirely consistent with how truthiness works in Python. The function does not claim to parse strings, that is how you get YAML Norway.
Do you think there should be different results for bool("no"), bool("false"), bool("heckno!"), bool("heckyes!")?
Edit: should have included internationalization: bool("nein!")
> The function does not claim to parse strings
But it's entirely reasonable to think it would. I honestly don't understand why it wouldn't, because:
If casting from a string to a type works with ints and floats, why not with bools? What possible justification is there?And of course it doesn't need to work for "no" or "heckno!", that's silly. But it sure seems like it ought to work on whatever the official string representation is. And not produce:
I'd honestly much prefer bool() threw an exception on receiving a string, rather than act the way it does now.It can short-circuit the brain upon reading a word that you know means false, but the Python rules are consistent. "Empty" is False, everything else is True.
You can write that yes, but I'm calling the police.
You can't go calling the police on people for writing idiomatic Python code.
> If casting from a string to a type works with ints and floats, why not with bools? What possible justification is there?
> I'd honestly much prefer bool() threw an exception on receiving a string, rather than act the way it does now.
They serve fundamentally different purposes. bool() is a truthiness check and not a type cast like int() and float(). It seems like a lot of people take issue with the name, because it was called something like istruthy() the discussion about it wouldn't be happening.
> bool() is a truthiness check and not a type cast like int() and float(). It seems like your issue is with the name of the function, because if it was more aptly named to something like istruthy() this discussion wouldn't be happening.
Right, the bug is in the inconsistent naming.
It's roughly as bad as having arithmetic operators named +, -, *, / that perform arithmetic as usually understood, except that + actually performs XOR and is documented to do so.
> Right, the bug is in the inconsistent naming.
The comment I responded to didn't seem to realize that because they asked why it behaves the way it does, so I explained.
> If casting from a string to a type works with ints and floats, why not with bools? What possible justification is there?
There's no type casting in Python. int(), float() and bool() just create objects of their respective types, passing the arguments to the initializer. They are not fundamentally different from, say, PostgresqlDriver() in this regard.
Different classes will provide different ways to construct objects depending on what is most useful. For int and float, the most useful thing to do when receiving a string is to parse it; for bool, the most useful thing to do is to check its truthiness; for PostgresqlDriver, the most useful thing to do is to interpret it as a database URL and connect to it.
> There's no type casting in Python.
If you search "type casting in Python" you will find lots of articles explaining how to use int(), str(), etc. to cast. This is a common and well-documented concept, even if it's different under the hood from e.g. C.
The idea that you'd name a function bool() and have it act differently is a deeply confusing design decision that it's understandable people would get misled by and frustrated with. This is a function named after a fundamental type. It's not a random user-defined constructor.
If we do it that way, we have worse problems.
bool() is consistent with how "truthiness" happens in python. Otherwise we end up with the YAML norway problem. And that would be different behavior then the above -- creating a nasty inconsistency.Honestly it's not that hard to write something that looks like this:
bool() is not a function named after a fundamental type. bool is a type. As with any type, you can call bool(...) to construct a new object of that type.
https://docs.python.org/3/library/functions.html#bool
A good tutorial will reflect this fact and use appropriate language.
Both the idea that you can "cast", and the idea that bool is a function, are misunderstandings rooted in the idea that Python should be like C in all ways.
> There's no type casting in Python. int(), float() and bool() just create objects of their respective types, passing the arguments to the initializer
int() and float() behave like type casting, bool() does not. It's a truthiness check that would be more aptly named istruthy(). In python non-empty strings are truthy, so providing any string except for "" to bool() will return True, including "False".
It's not casting.
Casting is converting the container, the interpretation.What you're reaching for is type conversion. Some languages have "implicit conversion" when casting, but the word itself doesn't require it.
In C, cast converts a value to another type (or a more or less qualified version of the same type). What that conversion means depends on the source and destination type.
A cast of insert a conversion that wouldn't take place:
or coerce a conversion that otherwise requires a diagnostic: Loosely speaking, casting is another name for coercion, which refers to making a conversion happen that might not otherwise.Implicit conversion, which is what you're mostly referring to here, is not required. It happens in some languages, and not others.
Because I'm tired, and this is a very basic topic, I'm afraid I'm just going to rip from Wikipedia:
> In most ALGOL-like languages, such as Pascal, Modula-2, Ada and Delphi, conversion and casting are distinctly different concepts. In these languages, conversion refers to either implicitly or explicitly changing a value from one data type storage format to another, e.g. a 16-bit integer to a 32-bit integer. The storage needs may change as a result of the conversion, including a possible loss of precision or truncation. The word cast, on the other hand, refers to explicitly changing the interpretation of the bit pattern representing a value from one type to another. For example, 32 contiguous bits may be treated as an array of 32 Booleans, a 4-byte string, an unsigned 32-bit integer or an IEEE single precision floating point value. Because the stored bits are never changed, the programmer must know low level details such as representation format, byte order, and alignment needs, to meaningfully cast.
> In the C family of languages and ALGOL 68, the word cast typically refers to an explicit type conversion (as opposed to an implicit conversion), causing some ambiguity about whether this is a re-interpretation of a bit-pattern or a real data representation conversion. More important is the multitude of ways and rules that apply to what data type (or class) is located by a pointer and how a pointer may be adjusted by the compiler in cases like object (class) inheritance.
The difference between implicit conversion and explicit is just syntactic sugar: do you have to put some visible tokens into the code to allow a conversion to happen, or not.
Implicit conversion can be seen as the compiler deciding among several possible conversions (or possibly just one) and inserting the coercion/casting operator into the intermediate code to make it happen.
(Of course, this can be a run-time decision based on dynamic types also, but the reasoning is the same. E.g. the run-time sees that a plus operation is adding an integer and float, and can either signal an error (requiring the program to be repaired by inserting a conversion operation into the expression), or just do that conversion, like the integer operand to float.
I really wouldn't call something you need to write, to make something happen, "syntactic sugar".
Otherwise types are just sugar. Indicies are just sugar. Lists are just sugar.
Yep, that's why I said "behaves like".
> I'd honestly much prefer bool() threw an exception on receiving a string, rather than act the way it does now.
`bool` would have no value if it threw an error in this case because if strings can't be passed to it, then no other type would sensibly work either. It would basically just become `bool(False) -> False` and `bool(True) -> True`.
> because if strings can't be passed to it, then no other type would sensibly work either
It's pretty standard to convert integers to bools, where 0 becomes False and everything else becomes True. That is absolutely sensible and useful current behavior.
Right. Zero-values become False (empty set, empty string, 0, etc.) and non-zero-values become True. So we agree bool is consistent then.
Nope, because str(False) produces "False", not "".
Not consistent at all.
There's nothing consistent about bool(str(False)) == True.
It's standard in computing for zero integers to represent False. Heck, bool is a subclass of int. Extending that to the length of strings is where things start to go off the rails in terms of consistency... and why you would ever even want that is beyond me.
Sounds like your complaint is with str() and not bool().
Which becomes a different issue - what do you think str(True) and str(False) should produce. Integer representations? That then makes other things unintuitive with changing the form of a boolean.
No, my problem is with bool().
str(False) == 'False' is entirely logical.
So therefore bool('False') == False would be consistent.
And bool('foo') would produce an exception the exact same way int('foo') and float('foo') already do.
Who on earth thought it was a good idea that 'foo' could or should be interpreted as a boolean...?? The entire concept of 'truthiness' outside of ints is unhelpful. It's just asking for bugs. That the length of a string, rather than its content, should determine a boolean value is one of the more bizarre things I've come across in programming languages. Use len() if you want that. It's only 5 extra characters.
Honestly once you really think about it just about nothing about these decisions ever makes any real sense which is why JS is made fun of constantly. It's nonsense functions running on nonsense data. "if some_non_bool_value:" should not be valid syntax and truthy and falsy is a concept that would get you locked up in an asylum if you decided to explain it to a doctor.
Don't forget its transitive dependencies creating non-deterministic builds
yeah
in raku, i checked
If I understand raku correctly, the Python example would be more like this:
or: Both of which display True.Leaving out the quotes means you're testing something else entirely.
Note that the Python example is converting (via `bool`) a string with the text "false" (which would make more sense as "False" which is the actual name of `False` in Python) into a boolean value. It's consistent with how Python behaves, in that the empty string (like in Raku from what I can tell) is false-y and non-empty strings are truth-y. It would be weird to have this use the else branch:
But not this:As mentioned else thread, the problem is not so much that book("false") evaluates to true, which is consistent with a lot of the language, but that int("1") evaluates to 1.
Generally python is much better than some other dynamic languages in avoiding stringly typed confusion, but the convenience of the int from string constructor set expectations that the rest of the language can't sanely meet.
String to type construction should have been relegated to a dedicated syntax in all cases.
thanks for the correction
How does this attract more people to hackernews who come from the python world? If anything, it just shows how toxic people can be on this site.
The first third of this seems to just be complaining that it's not obvious when two objects are actually the same object, and that if you mistake identity-related operators for other operators then you'll have a bad day.
That's a fair critique. It's a little weird that `is` and friends have dedicated, short, nice syntax.
On the other hand, most compiled languages are compatible with an implementation which admits the following optimization:
The language usually doesn't guarantee that different immutable variables have dedicated memory on the stack, or that they even live on the stack in the first place.That's the same class of error we're seeing here, and even among popular interpreted languages Python is by no means unique. I might get to the bottom 2/3 of the doc later, but the rest was off-putting enough that I don't know I'll bother.
Yeah, it would be much clearer if instead of an `a is b` operator, there was a `same_object(a, b)` built-in method.
It doesn't matter much since pointers are never comparable in python, just values.
The whole "problem" is that pointers are being compared when people expect value semantics. What, exactly, do you mean?
My favourite Python misfeature is that `x += y` sometimes does the same thing as `x = x + y` and sometimes doesn't. This leads to minor WTFs like this:
And major ones like this: The operation raises an exception even though it succeeds!Your first example has to do with the fact that tuples are copied by value, whereas lists are "copied" by reference. This is a special case of an even larger (IMO) misfeature, which is that the language tries very, very hard to hide the concept of a pointer from you. This is a rampant problem in memory-managed languages; Java has similar weirdness (although it's at least a bit more consistent since there are fewer primitives), and Go is doubly odd because it does have a user-controllable value vs. pointer distinction but then hides it in a lot of cases (with the . operator working through pointers, and anything to do with interfaces).
I think the whole thing does a misservice to novice or unwary programmers. It's supposed to be easier to use because you "don't have to worry about it" - but you really, really do. If you're not familiar with most of these details, it's way too easy to wander into code that behaves incorrectly.
> Your first example has to do with the fact that tuples are copied by value, whereas lists are "copied" by reference.
My mental model for Python is that everything is '"copied" by reference', but that some things are immutable and others are mutable.
I believe that's equivalent to immutable objects being 'copied by value' and mutable ones being '"copied" by reference', but "everything is by reference" more accurately reflects the language's implementation.
Yeah, I know that's how it works under the hood - and why you have things like all integers with values in [-5, 256] being assigned to the pre-allocated objects - but I don't think it's a particularly useful model for actually programming. "Pass-by-reference with copy-on-write" is semantically indistinguishable from "pass-by-value".
> I don't think it's a particularly useful model for actually programming
I think “everything is by reference” is a better model for programming than “you need to learn which objects are by reference and which are by value”. As you say, the latter is the case in Go, and it’s one of the few ways the language is more complex than Python.
You could argue that in Python you still have to learn which objects are mutable and which are immutable - but if it weren’t for bad design like `+=` that wouldn’t be necessary. A object would be mutable if-and-only-if it supported mutating methods.
I'd argue the first one is not a smaller WTF than the latter. It's a silent failure that also mutates state, arguably worse than getting a crash.
With that said, a = b = [1,2] itself is a bit of a code smell, but I don't take issue with it here because it can arise in more subtle ways sometimes.
> I'd argue the first one is not a smaller WTF than the latter.
I think the first one probably arises more frequently, but at least there's a fairly straightforward explanation: "`x += y` acts in-place if-and-only-if `x` is mutable".
The second example is more niche but it's much harder to explain what's going on - it can only be understood by first understanding the first example, and what it implies about the implementation of `+=`.
Aside: In the process of submitting this, I learnt that Hacker News apparently has an anti-censorship title transform built-in. I submitted it with the title taken from the notebook - "What the f*ck Python!" - and HN automatically removed the ! and changed "f*ck" to "Fuck".
Some of these just seem to be using Python out of spec and being surprised that implementation details exist, and misunderstanding boolean expressions.
Python doesn't have a spec. It barely even has documentation nowadays. (Sad. Twenty five years ago it was the gold standard of documenting software.)
Python's documentation today[1] is clearly more expansive, better formatted and more readily comprehensible than its documentation from 2001[2] (or even 2008[3]). There are multiple entire sections of documentation now that didn't exist before. Standards were just lower back then, partly because a larger percentage of "programmers" were accustomed to wrestling with beasts like C, and partly because systems were much smaller.
https://docs.python.org/3/
https://docs.python.org/2.0/
https://docs.python.org/2.5/
> Python's documentation today[1] is clearly more expansive, better formatted and more readily comprehensible than its documentation from 2001[2] (or even 2008[3]).
Documentation is not a specification. Specifications cover all behavior that should be expected, and specify which behavior is implementation-defined or undefined. If something isn't defined them this is a failure in the specification that requires fixing. The point of a specification is to allow independent parties to do clean room implementations that can be used interchangeably.
The majority of the comment I was replying to was about documentation, and I was responding to that.
Good documentation cannot be "expansive". Good documentation must be a) thorough and b) minimal.
Modern Python documentation is absolutely horrible - there's a shitload of irrelevant rambling while absolutely crucial details are omitted.
Good documentation cannot be "expansive". Good documentation must be a) thorough and b) minimal.
stealing this
and too much casual tone of voice
https://docs.python.org/3/reference/grammar.html and https://docs.python.org/3/reference/index.html look pretty comprehensive to me, and they're backed up by a thorough collection of PEPs: https://peps.python.org/pep-0000/
As a relatively recent example, here's the language reference documentation for the match statement https://docs.python.org/3/reference/compound_stmts.html#the-...
I don’t understand what you mean by “doesn’t have a spec”
The existence of Pypy and CPython and separate but compatible entities shows that there is
CPython is the de-facto reference implementation of Python.
Pypy was developed by reverse engineering CPython and their automated tests feature explicit comparisons with CPyton.
You made the opposite point you thought you were making.
in raku, the test suite (ROAST) is the spec
any compiler that can pass ROAST is valid
That seems uncalled for, the docs are great and the various guides are usually a good starting point to understand and then use the stdlib.
> Twenty five years ago it was the gold standard of documenting software.
That was PHP. Though Python was a close second
Hi, author of the repo (https://github.com/satwikkansal/wtfpython) here, pleasantly surprised to wake up to this thread about it. The intention of the project is not to put Python in a bad light relative to other languages, the intention was to explore it more deeply through (sometimes contrived) snippets that give non-obvious outputs. I believe for a regular Python programmer, the "Slippery Slopes" section is a must-know. The first section (Strain your brain), does contain some patterns that you may not ever see in real world, the goal there is to just learn about internals. Agreed some of them are very obvious for a well-learned programmer, and some of them aren't even Python specific, it does help a lot of beginner to intermediate programmers. I remember it being given as a supplementary material in one of the CS-based course.
It is curated with something-for-everyone approach, I expect people to learn a thing or two about Python / CS after going through the snippets. I haven't updated it in a while in a major way, but looking at the feedback I'll try to organise them better, and add new snippets (I have a long personal list that I maintain) in a future release!
I'm happy to complain about python but I got like a third into it and didn't find any actual wtfs.
Java as contrast has interning wtfs because == between java objects does essentioally what python's `is` does. Python actually made a good choice here and made == do what you'd expect.
Is anyone surprised by `(1 > 0) < 1`? There are languages where it will be rejected because of types, but what else would you expect it do than compare a boolean true to 1?
Is there anything of value later on?
I think there's a few in there. But most people aren't going to write code that way. It's good to know, and keep in your head, but if you have to think too hard in the future if a piece of code is correct, it's probably better to rewrite it anyway. And weird code like that shows up like a sore thumb in reviews.
> Java as contrast has interning wtfs because == between java objects does essentioally what python's `is` does.
It's pretty sensible because i wouldn't expect that operator to call a method - since Java does not have operator overloading.
This is great. Also to be a little meta, this link was posted in 2019 (https://news.ycombinator.com/item?id=21862073) and got the same sort of comments (how js gets flak, and python doesn't)
I didnt make it past the long list of id() misuses, but its a rookie mistake to confuse referential equality with value equality, and when doing so youre in for a bad time.
Small numbers are "eq" in python. It saves space and time, because probably those numbers dont even exist, and the value is derived from the pointer value.
But in Commonlisp all numbers are "eq". Very strange. Is there heap of numbers and once created, they reuse those?In Common Lisp, in fact, no numbers are required to be eq! Even (eq 0 0) is not required by ANSI CL to return true.
Why your Lisp implementation produces true for (eq a b) is probably because of the following.
Firstly, in Common Lisp there are single and double precision floats. Floating point tokens written with E or e are single. An implementation is allowed to map that to a 32 bit float. If the implementation has 64 bit values, it can easily arrange for single precision floats to be unboxed without any special tricks; they need only half the word. Try evaluating with 1D+20 to see what that does.
But even 64 bit values can be unboxed with a trick called NAN tagging.
I implemented NAN tagging in TXR Lisp (which has only double-precision floats), enabled on 64 bit targets:
64 bit machine:
32 bit machine: On 32 bits, floating-point objects are on the heap.It's pretty important to have unboxed floats for intense numerical work.
> But in Commonlisp all numbers are "eq"
No, that's not true.
>> numbers with the same value need not be eq,
http://clhs.lisp.se/Body/f_eq.htm#eq
It's not guaranteed that eq will return true for two numbers of the same value in Common Lisp. It happens to work much of the time, but it is never guaranteed to work. For a given implementation it could even be true all the time (as it's not disallowed), but for portability it should not be relied upon.
See also: https://github.com/satwikkansal/wtfpython
The "interactive notebook" link goes to the colab page.
There are many earlier threads associated with the markdown version, for example:
https://news.ycombinator.com/item?id=21862073 (2019-12-23, 185 comments)
https://news.ycombinator.com/item?id=26097732 (2021-02-11, 163 comments)
https://news.ycombinator.com/item?id=31566031 (2022-05-31, 143 comments)
https://news.ycombinator.com/item?id=37281692 (2023-08-27, 82 comments)
I may be wrong, but think a lot of it is undefined behavior. Interpreter may choose to recycle objects or it may not. In case of integers, low enough number in different parts of the program, will be represented by the same object. But if the integer is high enough, different objects will be created. That’s my understanding, but I could be wrong.
While we're at it. Notebooks aren't a real programming environment.
Real in what way? I'm not too familiar with them, but they seem about on par with the environment for JavaScript that is html files or "web pages".
Making a html document so someone can easily run some JavaScript, seems like the closest parallel to making a jupiter notebook so someone can easily run some python.
Define "real programming environment"
Seems like an odd way to say, "Notebooks don't fit my workflow/usecase."
Huh we must be hallucinating then?
Agreed. Even so, you'd be surprised to find that some companies run them in production. I never got the idea behind that, maybe someone that engages in such a practice could enlighten me. I have yet to hear a good rationale.
Programming encompasses things other than production environments and software engineering. Notebooks are fine programming environments for prototyping, exploration and training.
That in no way relates to the comment I made.
I don't think anyone is disputing that
Jupyter is like if someone decided they don't like neither REPL nor running complete scripts and did something that makes a mockery of both.
Yeah, this simply doesn't work for me; ie the expressions did not evaluate, and output was not made visible.
I have never had a successful experience writing or running programs in notebooks like this--although I have not, arguably, tried with high stakes.
As soon as the stakes rise that far, however, I reach for a whole-ass Unix environment with a shell, a filesystem, and a stateful process model.
Is this a crutch? Is it too much overhead? It's arguably absurd that I even ask. (Edit: It's actually killing me how ambiguous I'm being, right now. There's no winning.)
> As soon as the stakes rise that far, however, I reach for a whole-ass Unix environment with a shell, a filesystem, and a stateful process model.
I don't understand what you mean by "reach for". Don't, for example, Linux desktop users have all these things at hand all the time? For me it would take much more effort to set up a "notebook" than a virtual environment.
For you, sure, but for someone that lives and breathes notebooks, running jupyter notebook and going to localhost:8888 already happened, so it's opening a terminal that's the extra step.
... How do you do those things without a terminal?
You would start the Jupyter application, then use a browser to go to localhost:8888 :)
To start the application, you would typically use the start menu on Windows or the Dock on MacOS. Alternatively on MacOS you could point the Finder at the /Applications folder, then start the application from the Finder window.
(This is not meant as an endorsement of notebooks.)
install https://github.com/jupyterlab/jupyterlab-desktop and double click
Some ways of programming in Python require a LOT of mental effort. And some don't. For example you can do a lot in one line in Python, but I usually have to write extensive comments and references to external docs to keep track of what that one line is doing. I think sometimes it would be easier if I just had to write 3 or 4 lines of self evident code and use that as docs instead.
Having written python professionally for more than a decade, I highly recommend not writing complex one-liners -- unless there's some real benefit to doing so.
The more one-liners there are in the code base, the more impenetrable and harder to debug the code is. Unrolling nested functions and switching comprehensions to lists and if statements often make it cleaner and easier to write unit tests for.
If you rely upon code coverage for unit tests, a complex list comprehension won't help you see if you have hit the branch cases.
Personally, I always find multi-layered list comprehensions the hardest to read... And debugging list comprehensions is basically impossible in my experience.
Use static types and treat python as more concise java. Use dynamic types sparingly. Limit expression complexity.
Static types + IDE features make it much easier to understand any codebase.
Great. I enjoy these kind of articles. My all time favorite book for 'C' is Expert C Programming: Deep C Secrets.
It’s reassuring that a lot (feels like a majority) of these are noted as resolved or turned into errors that make more sense than confusing behaviors in later editions of the language.
Python 3.5, which is what the WTFs use by default, is relatively ancient.
Holly cow, what a mess. Is the miniscule efficiency gain really worth it especially in such a user friendly language as Python?
Exclamation points are in fact part of the ASCII standard.
If you use a floating point number as a key in a `dict` the wtf should not be pointing at Python.
These are not wtfs, please.
These examples read like someone who is learning to program and who is confused.
How can you confuse these two?!Fp32 or fp64 in a strongly typed language would be no problem as hash keys. Yes, they're imprecise for math but the 64 bits of data should work just like a long as a hash key.
No, they are not suitable for use as hash keys unless you are treating them as opaque binary data. And if you are there, you are better putting your values in some type that means opaque binary data for the start.
They are perfectly suitable as long as you know what you are doing. Here's a real use case: when compiling a scripting language, map float constants to slots in a "constant table" so that all constants with the same value share the same slot.
They ARE fixed-width binary data, and they're effectively opaque unless you have IEEE 754 open on your desk, it's all handled in hardware otherwise.
AI
I'm a little flabbergasted at how many comments here boil down to "These aren't WTFs if you understand the language."
It's a bit like when non-JS programmers complain about JavaScript. It's mostly nonsense. That's not to say that JavaScript doesn't have flaws and footguns, but the opinion of novices in the language is not who I am interested in learning from. Python is the same. It has flaws and idiosyncrasies, especially in the standard library (cough cough xml.ElementTree), but the core language is fairly consistent and predictable. Expecting casting functions like bool() to parse strings like JavaScript does makes little sense.
Now do a "What the Fuck Excel"
Good point. The "snap to zero" rules are wild in Excel.
Haha these aren't even wtfs! the id changes or not? oh noooo, people use the id() function so much, I've literally seen it 0 times ever in production code. This is nothing like the JS wtfs where there is no way to predict what it will do, and it violates it's own patterns.
edit: to be more clear, I have run into a lot of JS wtf's trying to write code for work. you just run into bugs all the time where JS doesn't do anything like what would be the obvious thing to do, or the thing that works 99.99% of the time fails depending on the value of the string being 'magic'. with Python I rarely if ever learn some bizarre arcane thing accidentally because it almost always behaves as expected. If you think there's an equivalence here you are just a JS fanboy, JS was a language that evolved largely because of hacks vendors put in and those insane hacks got standardized because it was too late, despite being insane. Python was specifically and carefully designed to minimize unexpected behavior.
I think this one is good:
It doesn't make sense no matter which way you evaluate it. Equality first: or `in` first:> It doesn't make sense no matter which way you evaluate it
It makes sense when you evaluate it the way it's intended to be understood (https://stackoverflow.com/questions/6074018). There are other languages that do this, and it was consciously designed to work this way. The idea that you should have to evaluate one of the operators "first" may make sense to someone trying to implement a parser with techniques from the 70s, but for languages like Python, the design is clearly more about what makes sense for the code author.
Arguably it's surprising that `in` is treated as on par with `==`. But on the other hand, even someone trying to implement a parser with techniques from the 70s would have to admit that both are "relational operators".
Yeah, having the expansion rule operate on operators that are clearly not transitive serves no purpose (1 < 2 < 3 is at least a math notation)
Its apparently trying to allow x < y < z be a legal statement, meaning that we get:
False == False and False in [False] is a expansion of that statement, just like x < y and y < z is the expansion of x < y < z
Oh yeah, that's a great one, there must be a compounded boolean expression logic escaping us
I'll add:
ps: some ast poking note the first tree as one Comparator node with multiple ops, time to read the spec to know how these are evaluatedThe document explains it. When dealing with these comparators, when chained they get separated and you have the equivalent of this:
If you put it into a function like this: And then disassemble it you'll get this byte code (3.13, may vary by version): There is no literal `and` operation occurring here, instead it first does the `False == False`, then jumps if false or continues to the second comparison.For the literals we've given it, it'll go on to the second comparison which will result in false since `False` is not in `(True,)`.
EDIT: And here's the non-chained version disassembled:
Ok I'll admit this is a real WTF
chained comparisons are weird and counter-intuitive, sometimes
Python is my second language (after C++) and for me the surprising thing here is not "chained comparisons are weird" but rather "`in` is a comparison operator."
So for example `1 in [1,2] in [[1,2],[3,4]]` is True... but off the top of my head I don't see any legitimate use-case for that facility. Intuitively, I'd think a "comparison operator" should be homogeneous — should take the same type on LHS and RHS. So, like, "is-subset-of" could sanely be a comparison operator, but `in` can't be.
>and for me the surprising thing here is not "chained comparisons are weird" but rather "`in` is a comparison operator."
Python documentation calls it a "comparison operator" (https://docs.python.org/3/reference/expressions.html#compari...) but a broader, better term is "relational operator". It expresses a possible relationship between two arguments, and evaluates whether those arguments have that relationship. In this case, "containment" (which intuitively encompasses set membership and substrings).
A finer distinction could have been made, arguably, but it's far too late to change now. (And everyone is traumatized by the 2->3 transition.)
Ah, `in` for strings satisfies my intuition re homogeneity. So I guess that makes sense enough (although if I ran the zoo I wouldn't have done it!). Thanks!
I have used id() in production code, but the circumstances were extremely specific: It was in some monitoring-related code that was trying to dig down into the guts of the interpreter for the purposes of tracking memory usage on the level of individual pages. It was a whole thing.
Which is to say: Users of Python almost certainly don't want to call id(), unless they are mucking around with something related to the functioning of the interpreter itself. The only other circumstance in which I've used it in anything like real code was in a quick hack where what I really wanted was to put a bunch of dicts in a set, but you can't do that, so I made a set of their IDs instead (but this wasn't in what I would call "production code").
In general, most of the "wtfs" listed here are pretty banal. If you're using `is` on immutable objects, you're basically just asking for trouble. The functioning of string and integer interning and constant folding are implementation curiosities, and if you ever write code in which such differences matter, then you have made an error.
I don't think either reach the real wtfs of their language. id() does some weird things with string interring, javascript coerces cross-type arithmetic into weird nonsense instead of throwing a type error. Whatever, neither of these come up in the real world.
Builds so messy that you need to resort to venv, every second DOM API being someFunc(thirdMostImportantArgument, { mostImportantArgument: val }), these are the real wtfs.
Yes, but those are definitionally library and ecosystem wtfs, rather than language ones.
I totally agree with you. I have seen a lot of code, but the id() function has never been used in production code
> I have run into a lot of JS wtf's trying to write code for work
JS certainly does have plenty of wtfs but honestly, when sticking to somewhat modern JS, it's not so bad anymore these days. Function-scoping-shenanigans can mostly be circumvented by simply sticking to const/let, use a 3rd-party-library like date-fns instead of the dumpsterfire that is native Date and you're almost there - throw typescript into the mix and I have a very hard time imagining running into the meme-worthy JS-warts on a daily basis.
I think they are pretty bad tbh apart from the ID one. Everyone knows about the JS quirks so I think it’s good to know sometimes equality in Python strings can behave extremely strangely and the other issues mentioned. Why does it have to be defended “my language is better than your language”? However I’d like to say Elixir is near perfect, thanks :-)
No you misread it, equality for python strings works exactly the way you expect. the id() value (which is basically the memory address) might not change if you concatenate strings, but literally nobody ever uses id for anything ever. You would use hash() which does change appropriately.
edit: 'wtf!' is always equal to itself. Maybe you are thinking of `is`, which just compares the value of id(). `is` can't be used for comparisons, saying it's a WTF is just being unfamiliar with basics. `is` is not used for equality comparisons. This is how more or less every language works, comparing pointers is not the same as comparing strings in C/rust/etc. Comparing object identity is not the same as testing equality in Java.
I think honestly it is you who misread my comment... I was saying the id() issue isn't a real problem, the weirdness around "wtf!" not being equal to itself under some circumstances is very confusing. Seems Google Colab isn't loading here.
`is` is more like doing this in C:
They may or may not be stored in the same place depending on your compiler (clang and gcc yes, msvc no, at least with just a quick default build). If you actually want to do an equality check, you need to use `==` (in Python) which would be like using `strcmp` in C.> "wtf!" not being equal to itself
It is equal to itself, but multiple variables referencing that string may not hold references to the same (literal, in memory) string.
But "is" doesn't check equality. It checks reference equality. Maybe the real wtf here is they made that check a temptingly short infix operator rather than a function named reference_equals().
"wtf!" is consistently equal to itself in Python.
The fact that you could have separate string objects both representing that text, is entirely orthogonal to that.
Like I've been saying in other recent submissions, since there seems to be a trend for similar discussion: in Python you find WTFs by trying to probe how Python works, pushing deliberately over-flexibly customization hooks to their limits, etc. In JS you find them by merely trying to call builtins with dodgy inputs.
And now you're using pretty much the same rhetoric than JS guys when facing to wtfjs.
No, I don't think so. The JS wtf's actually make you gasp. These are so obscure and boring.
F'ing JS equality isn't transitive for builtin types.
Even normal JS is WTF-worthy…
Allowing out of bound array indexing and operations using “undefined” seems pretty crazy to me.
For the former: technically they are about as undefined as they could be on read. On write, it's a different matter (you could decide to resize the array under the hood). Even so, I think arrays with a particular size always were simply an illustration that there is a point where you have to allocate a physical resource to something entirely imaginary and that's where infinite bounds are no longer acceptable. In the 80's that was '256', in the 90's '65535', nowadays it is more like '4G' but no matter how far we advance there are still hard limits. And on the wire it gets even more constrained.
[dead]