If you know nothing about Python or coding, and you see a=b=c, you'd think that's true when all three a,b,c are the same. Python does that beautifully here, and that's the intent. It's not Python that's confusing, it's your previous experience with other languages that's confusing you.
I have some experience in computer language design. The issue here is that `a==b==c` expression is magical - that is it does not follow from extending comparison binary operator. Specifically, `==` is a binary operator that that compares an expression before and after and returns a boolean. In this case, A==B==C is a trinary comparison operator. This is normally ok, except it's rare and the symbol it is using is overloaded with binary comparison operator, so the people will be confused.
Once you know how it works and are used to it, I think it makes the code easier to parse... but there are heavy downsides. For example, it's not clear how short circuiting works. I've used python a bunch and logically I expect ```side_effect3()``` to not be evaluated if results of 1 and 2 are not equal: ```side_effect1() == side_effect2() == side_effect3()```. However, I do not know that for sure, while in other languages I would be able to reason about this from basic principles.
> the symbol it is using is overloaded with binary comparison operator, so the people will be confused
I think most people would expect expressions like `5 < x < 10` to work as they do in math, without necessarily thinking about it in terms of being an overloaded binary operator. The result in other languages that `5 < 12 < 10` = `true < 10` = `true` is more surprising, just that we've (perhaps by being bitten by it early on) gotten used to it.
Yeah, but should that math equivalence hold for programming though? Programming is different. x=x+1 is perfectly legal in programming but does not make sense in algebra math and could confuse mathematicians.
> should that math equivalence hold for programming though?
It's not a hard rule that overrides all other considerations, but I do think Python's choice to follow math is the right decision here.
I'd claim if you teach someone `<` then show them `5 < x < 10` for the first time, they're far more likely to go with the math interpretation than the C one. That is, beyond the fact that someone's familiarity with math reinforces this intepretation, the reason for math using this interpretation in the first place is because it aligns with intuition.
It's also just pretty useful. It's common to want to check if a number is between some bounds, like `0 <= i < len`, or that multiple things are equal. In cases where you really do want the C behavior, I'd say you should already write that as `(x == y) == z` for clarity anyway, regardless of whether the language lacks chaining and thus lets you omit the parentheses.
It's a completely useless question - it's language trivia. It's like asking "what happens if you do X in database Y version Z and up". Who cares? It's something you figure out when you get there. There are tons of examples of something like this where it's language-dependent, so don't even bother memorizing this stuff.
I once passed an interview where it was all just Spring documentation questions. I had never built a Spring app in my life, I just "read the docs" five times.
It’s what you’d do on the job, right? A place which dings you for checking your work is broken.
I’d expect a raised eyebrow if it was some very basic question about something you’ve claimed to be an expert in but given how many, many bugs over the years stem back to confusion about order of evaluation I would consider it a minor plus if someone said “I think I know but I want be certain”, and a major one if they paired that with mention of defensive coding practices like adding tests covering cases where it’d matter and structuring the code so there are no side effects hidden in the test (e.g. the code as written is fine but if it was f1() == f2() == f3() I’d probably write it as a clearer multi-line if block when there are side effects to those calls to make obvious when I’m intentionally not doing them in every case).
I'd prefer a candidate to check the docs or look something up if they were unsure, because that would also provide useful information to me as an interviewer.
Although I'd also want that benefit of the doubt for myself. I'm quite familiar with how Ruby handles collections, for example, but if I tried to use `slices.Collect` in Go the same way as `Enumerable#collect` in Ruby I'd end up stuck and would need clarification.
Agree, also by reasoning from basic principles adding extra brackets ( ) should not change the output result - at least this is what our brain is programmed to believe, but in the case of chained operations it will.
Most programming languages ought not to be optimized for a non-programmer to read, but rather for someone who writes code to read.
There's a lot of options for a language to deal with a statement like this. It could be a syntax error, a compile or lint warning, it could work by doing order of operations and comparing the boolean from one compare with the third element, or it could work in the way you described.
I'd prefer languages that I work with to chose earlier options from that list. In most languages this sort of statement is usually a bug, and deserves explicit parenthesis to clarify the order of operations. I really don't want to have to pull out an operator precedence chart in order to read your code, much less have to look up some esoterica about this specific language.
I'd argue that many programming languages are not optimized for "people who read code" but they are optimized for programs who read code (compilers, interpreters) and programmers can stockholm-syndrome themselves into believing that it is targeted at them.
Code optimised for an interpreter is often also optimised for the programmer, whose primary job is interpreting code. What makes natural language intuitive is that it doesn't have to be precise. You can assume language does what it is intended to do. A programmer, by contrast, must know exactly what the program does. You cannot assume it does what you want it to do because the computer doesn't know what you want it to do. A language cannot be made less precise by the addition of new rules; it is only made more complicated as there is more to remember. In this example, the person writing the ternary expression had to learn that it exists. If it didn't exist, there would have been less learning to do. Perl is the limit of this process – a language with many rules to appear natural and human-like that it's almost impossible to actually read.
Each rule of a language is a cognitive burden. It can only justify itself if the thing it replaces is a greater burden.
"someone who writes code" is very vague. For someone who writes code primarily in Python this behavior is less surprising than the rest that you described.
I've worked primarily in Python for the last two years and I'd still have to look at a reference to be certain of what exactly this code does. And would probably rewrite it to the expanded, parenthesized form unless the company linter insisted.
>I write code for people who can read the language
Programming languages often have syntax that's acknowledged as a source of confusion and bugs and better avoided. The line is not hard and fast, but I write C++ and there's "C++", and then there's "the C++ subset that you should use" (I probably couldn't write C++ code without tools slapping me for using the wrong parts). Operator precedence is debatable but our tools force the use of parentheses to disambiguate.
This is true, and, in this case, "confusion avoided" and "what Python does" coincide. The "double equals doing the right thing" is only confusing if you're both unfamiliar with Python and familiar with language design, which is a very small set of people.
People unfamiliar with programming will assume that "if a == b == c" is only true if all three things are the same, and people familiar with Python will know that that is, indeed, what it means.
The more complex an expression grows, the less well it works to rely on operator precedence to indicate structure to the reader.
Additionally, I think assuming the reader is aware of every. nuance of the language is an extremely bad idea unless you are and will always be the only developer. If you are doing anything weird or tricky or rare, I'd highly recommend linking the documentation or providing an explanation.
Most programming languages ought not to be optimized for a non-programmer to read
Python 'won' mostly because non-programmers could look at it, more or less understand what was going on, and feel that this was something they probably could learn.
What does A == B == C even mean? I mean I know what mathematically A=B=C means. That A, B and C are all equivalent.
But then is the mathematical '=' really a binary operator that maps two numerical inputs to a boolean output? It feels then as an abuse of notation if (A=B)=C doesn't allow A=B to change type?
Because I don't really have much use for a symbol that is some times doing a binary mapping from numbers to booleans and some times becomes some sort of ternary operator which maps 3 inputs to one boolean.
Maybe consider chained comparison operators early textual replacement along the lines of the C preprocessor, although the exact textual replacement would involve temporary variables to avoid multiple side effects and be more complicated than just "(a) == (b) and (b) == (c)". (a==b)==c does not expand, only the version without parentheses expands, so you can still do the boolean comparison if you want.
I dunno, I'd expect the right hand expression (2 == false) to be resolved first, then compared to 1.
Compare this with
a = b = c = 5
You evaluate the innermost (i.e. the tail) expression first, and work your way out to the head. As a sexpr it is a little more obvious that way:
(= a (= b (= c 5)))
An argument could easily be made that python is making up special rules for the equality operator by treating the duplication of the operator as merging the expressions into one.
Instead of what you would expect, per the rules of literally every other expression, 5 == 5 == 5:
(== 5 (== 5 5))
It gets rewritten as
(== 5 5 5)
Which one is "unexpected" is really a matter of perspective: are you reading with the rules of English or the rules of your programming language as your context?
I do concede one caveat to this argument: mathematical operators tend to change operator precedence around to avoid making mathematical mistakes. I am mildly of the opinion that this itself was a mistake. Most calculators don't do it, so it's not like anyone would be* that* thrown off by it.
> An argument could easily be made that python is making up special rules for the equality operator by treating the duplication of the operator as merging the expressions into one.
I guess the argument could be made but it would be wrong. All the comparison operators can be chained in python. a < b < c, for example, or even a == b <= c < d.
> You evaluate the innermost (i.e. the tail) expression first, and work your way out to the head. As a sexpr it is a little more obvious that way: `(= a (= b (= c 5)))`
That's not true in Python though.
a = b = c = <expression>
is equivalent to:
temp = <expression>; a = temp; b = temp; c = temp
I don't know why that order of evaluation was chosen.
> If you know nothing about Python or coding, and you see a=b=c, you'd think that's true when all three a,b,c are the same.
Sure, that's true if you literally know nothing about coding. But that is not a very common audience for reading code. You only need to spend about 10 minutes coding to realise that the compiler follows fixed rules rather than making an LLM-like guess as to the meaning. If you get that far then most people (admittedly after a bit more 10 minutes) go on to realise that the only way to know what code does is carefully pick it apart step by step, rather than glance at it and guess yourself.
I love Python dearly, but this rule was a misstep on my opinion.
The problem isn’t meaning or intent its inconsistency of operator behavior.
1 + 1 + 1 has different operator behavior then 1 == 1 == 1. The operations here are not consistent and it’s not clear what happens if I overload the operators.
On the surface level python looks more beautiful. But mathematically speaking python is actually more ugly and more inconsistent with weird rules to make things look a certain way.
If you know nothing about Python or coding it is not really relevant as the code is probably read and written more by those who know coding and/or Python?
In Lisp, you put the "operator" first, so instead of
2 + 3 + 4
you write
(+ 2 3 4)
This is nice for associative operations like + or *, very very very slightly confusing for - and /
---
You use the same style for comparisons, so instead of
2 == 3 == 4
you write
(== 2 3 4)
To support the fist "infix" syntax, you need some magic in the == operator. But the second "prefix" syntax is totally natural and you need no magic in the parser or the operator.
I knew that Lisp does this operator first thing, it's everything around this that I'm not familiar with. What does ; do? Is the => an arrow or greater than or equal to? What is t? Do I guess correctly that most-negative-fixnum is like INT_MIN?
The semicolon is Lisp syntax for comments. T is the way you write true in Lisp. Most-negative-fixnum is the most negative number Lisp can represent without promotion to bignum (so it can be int_min if int is roughly equivalent to size_t).
I know the point of the piece is the python syntax here, but I got stuck on: "judge things like code quality / speed / conciseness etc."
Do people generally write concise code on right off the bat when confronted with a new problem? I've always taken the same approach I would with writing: get something that works; refactor for concision. Maybe everyone else is playing 3D chess and I'm still on Chutes and Ladders?
Yes, even the "unconcise" first draft of anyone who cares enough to be here on HN is much better than some of the code out there.
There's a basic level of concision you'll have by default if you actually understand the problem and the tools. You won't go away out of the way to complicate things. Maybe it's still a far cry from optimal, and definitely not code golf, but enough that I'd be worried if I saw even a first draft without it.
yes, when writing this as other_bool_variable=!bool_var you make it much better because the code speaks for itself ("other_bool_variable is the opposite of bool_variable").
The risk is that you may end with such concise code that nobody understands what it does. I went that dark path with perl 20 years ago, form "oh what a cool language for someone who comes from C" to "let's squeeze that in half a line, and either do no understand what it does, or add three lines of comments to explain".
Do people generally write concise code on right off the bat when confronted with a new problem?
As someone who has been on the interviewer side: this is an indicator of how much the interviewee "thinks first"; the ones who immediately start spewing tons of code are usually those who don't really have a good understanding of what they're trying to do.
As someone who's done a lot of interviews on both sides: I get where you're coming from but don't really think it's as universal as you think. Some people gain understanding through doing; and there's nothing wrong with that?
Thinking first is a very Cartesian approach to development and not necessarily the best way. Many people think through a more engaged approach of doing
I code similarly to how I write, which is similar to how I draw etc. I start with very rough ideas and sketches, then I gradually iterate over those, refining ideas and throwing bits away. And eventually I have a working version/first draft/etc
I just can't get things done if I were to try and think of the entire solution first before getting anything down on paper. Maybe that's an ADHD thing, needing to let the idea take form lest it runs away and I get lost in the weeds
It's less "spewing tons of code" though and more starting with an outline, stub functions and comments to break up the steps before filling those out.
if you know the domain for the task at hand (+a few very basic soft skills) you can learn a lot from watching and discussing with a candidate while they are doing the task.
In reality - tasks like these are garbage leet-code samples; candidated who have been grinding examples rapidly regurgitate the best solution from the forums and impress the interviewer, but would fail if asked a basic question on what they produced. Those that solve it reasonably from first principles fail the task.
There are problems I have encountered a billion times and of course I have an elegant concise solution for it, if I am an experienced programmer.
if any([a in ["-h", "--help"] for a in args]):
display_help()
Would be one of those. Generally I learned that it can be beneficial to treat things as lists early on, e.g. if you have a program that takes one input file it costs you next to nothing to allow the input to be mutiple files as well.
`any` works on arbitrary iterables, so you can use a generator comprehension instead of a list comprehension:
if any((a in ["-h", "--help"] for a in args)):
display_help()
Which algorithmically is a wash, because the time you save not-materializing the list, you waste in the overhead of iterating through a generator (and it's all +/- nanoseconds in recent versions of Python anyway). However, Python has a special syntax that allows you to omit a layer of parentheses when a generator comprehension is the only argument to a function, leaving us with the very elegant (IMO) syntax:
if any(a in ["-h", "--help"] for a in args):
display_help()
This works for any(), all(), str.join(), and others. Yet another demonstration of why I think the iteration protocol is one of the best features in Python (and underappreciated as such).
That still looks like something that a novice might trip over when reading this. Just describing what it does is already a bit convoluted: it tests whether any value of a list of booleans, produced by a generator expression by testing membership of a value in some other list, is True.
I would have gone with the following, as it requires less mental unpacking:
if set(args) & set(["-h", "--help"]):
display_help()
Also, note that any() works with any iterable, the outer brackets are not needed given the list expression.
if any(a in ["-h", "--help"] for a in args):
display_help()
Being extremely pedantic, omitting the outer ()s is legal for a generator comprehension. That is, the following are equivalent:
foo(x for x in xs)
foo((x for x in xs))
gen = (x for x in xs)
foo(gen)
But these are not the same as:
foo([x for x in xs]
lst = [x for x in xs]
foo(lst)
It doesn't matter in this tiny example, but the difference between generators and lists can be very very important when working on very large amounts of data (e.g. processing a big text corpus), or a potentially-infinite stream of data.
on my phone and a bit discombobulated today, so we’ll see how i go with this
> That still looks like something that a novice might trip over when reading this.
my experience with “juniors” who start with python is that they think in lists and dictionaries. they don’t think in terms of set theory. that’s just the way python is taught.
i would definitely set interview questions where a “decent” solution involves using sets and see who gets it without iterating over lists (ha, setting an interview question on sets… geddit? i’ll see myself out).
> Just describing what it does is already a bit convoluted
yeah, but python only/main “juniors” primarily think in terms of lists, see above.
so, between making sure all juniors can read the code versus doing it succinctly, but juniors all need to learn about sets — practicality wins out quite often.
> I would have gone with the following, as it requires less mental unpacking:
> if set(args) & set(["-h", "--help"]):
> display_help()
if i were reviewing this code i’d ask you to switch it to
if set(args).intersection(set(["-h", "--help"])):
it is more verbose and more brackets, but it is more explicit and obvious what is happening to some junior who has never dealt with sets before (hmm what’s an intersection? should probably google it)
& is also used for the bitwise AND operator. plus some other stuff in various frameworks like django. so could lead to some confusion.
> Also, note that any() works with any iterable, the outer brackets are not needed given the list expression.
i generally HATE the overuse of generator expressions in python. everyone writes absolutely everything as an inline generator expression, making it hard to test.
but, rant aside, yes, this is probably the one i would pick during a review (likely after a discussion about how using the intersection method makes things less legible and then this gets suggested somewhere).
people don’t realise not every genexp needs to be a list. easy mistake to make with the “everything is a list or a dict” mental model.
Yeah - when I went through University the mantra I was taught was
1. Get it working
2. Get it working well
If you're faced with an unfamiliar domain, you don't rock up to it and throw all the super optimised fastest code at it. in fact I would view someone who did as having done the test before (making it worthless).
Maybe I misunderstood the piece, but it seems the last sentence, "Well the code is indeed correct" suggests it _did_ work (although I'm also a little confused about what it means when all values are '-' which I interpreted as 'no piece played').
The code might compile, but it does not functionally with in an empty game state. Resulting in an always win for the first player if he does not play on any diagonal
I also judge code quality at the interviews that I perform. I just give the candidates time to improve their code after they come up with something working.
I'm confused about this blog post. Python is mostly C inspired but actually more pseudocode inspired (which helps its popularity) which is why chained expressions exist.
Also, why would you conduct an interview in a language where even if you don't know the syntax (and this is obscure) you could have looked it up or disallowed the interview to be done in Python? I think the due diligence with this issue is more to the interviewer than Python.
> Also, why would you conduct an interview in a language where even if you don't know the syntax (and this is obscure) you could have looked it up or disallowed the interview to be done in Python?
The norm in most of my interviews has been that candidates can solve coding problems in whatever language they are most comfortable with. For most languages like Python etc, it would be a mistake to reject a candidate just because they don't have experience with that specific language.
The norm in most of my interviews has been that candidates can solve coding problems in whatever language they are most comfortable with.
I assume they've already filtered out candidates whose "most comfortable language" isn't the one they're hiring for, or they're going to have a difficult time when they come across the one who wants to use APL or x86 Asm.
I heard a tale of a candidate white boarding in an obscure language (in a bit of an attempt to hide a deficiency), unfortunately for the candidate the interviewer happened to be well versed in the language and saw right through the charade.
Hah. Even if the interviewer didn't see through the charade they have plenty of time to check the code after the fact. Once or twice a candidate has done something I didn't know about so I just googled it. Nice little learning opportunity for me.
Mate, let's be honest, we've all been in interviews where the interviewer had a serious misunderstanding of the language the job was supposedly using, let alone some other language.
In this respect, Python makes a lot more sense since that is how you'd normally write such an equality in math, and generally how people chain them: A=B=C means A=B and B=C.
Part of the problem here is that we treat true/false as "just another value" in programming, and thus the usual operators are used to compare them. In math notation, if you wanted to compare the result of comparison A=B to a Boolean value C, you'd normally write something like (A=B)≡C, using different operators for equality and equivalence.
Interestingly, programming languages haven't always treated Booleans in this way. If you look at ALGOL 60, it starts by cleanly separating expressions into arithmetic and logical ones, and defines completely different sets of operators for each (the reference language also uses = for numeric comparisons and ≡ for Booleans; and boolean inequality is, of course, just XOR).
I think that the main issue here is not treating true/false as values but allowing them to be implicitly converted or compared to numbers with the assumption that true equals 1 and false equals 0.
I think that Rust got this right.
It doesn't allow you to add integer to boolean or multiply boolean by float etc, because it is unclear what does it even mean mathematically.
Also, most languages implicitly assume that any value of any type is either "truthy" or "falsy" thus you can do something like `while(1) { do_stuff() }`. Rust doesn't allow this BS, `if` and `while` expect a boolean so you have to provide a boolean.
That resolves the problem for other types, but you still have the case of (a == b == c) being parsed and evaluated as ((a == b) == c) if all three are booleans, which is still not an intuitive interpretation. I think the PL either has to ban such construct outright and require parentheses, or treat it as an n-ary operator like Python does (albeit perhaps with a few more constraints on what you can chain, for the sake of readability).
A lot of languages have no boolean primitive to begin with. Often in older languages, the values for `true` and `false` are aliases for `0` and `1` respectively. Perl and earlier versions of C, Python, and JavaScript are notable.
Perl is probably the most awkward due to context-sensitive casting. e.g. the string `"0"` in a boolean context evaluates as an integer, and the boolean interpretation for `0` is false.
JavaScript had Booleans as a primitive type as far back as I can remember. The problem, rather, is that it's altogether too eager to convert everything into everything else.
I don't see how you could interpret "a != b != c" as equivalent to "not (a == b == c)" in the first place. In the first expression a doesn't equal b and b doesn't equal c (no restriction on a and c). In the second expression you could have a == b, but b != c (and vice versa), clearly that's not equivalent to the first expression.
If you treat each "!" as a NOT, then (a != b != c) has two NOTs, but !(a == b == c) has a single one, so this is a bit like expecting that !(a && b) is the same as (!a && !b).
This is a tangent, but checking if all the elements in the diagonal position are the same is not sufficient. You also need to check that any of the elements are not '-'.
Stopped judging candidates on "style" due to stuff like this. I only comment if the code doesn't work, if it works i don't really bother to correct code style or provide feedback on it.
45 minutes interviews aren't really the place to evaluate this.
The blog itself isn't clear, and I think that's confusing some of the people here:
> The candidate this time was programming in python
But the code blocks aren't in python. But in the paragraph afterwards they capitalize "True" which is a python thing. Then afterwards mention they're using javascript as their reference, which uses "true" instead and the code blocks could be javascript.
It feels like the author has more language confusion going on than just this one feature of python.
That's why i put "style" in quotes, the interviewer didn't know about the feature but still decided it was "wrong" or "ugly" even though the code works.
If your knowledge of Python comes from JavaScript, I would not blame Python for it. It's the failure of the person to not "read the instructions" and assume instead. Maybe conduct interviews in languages that you're familiar with?
If you were programming long enough, you easily had contact with dozens of languages. How do you pick up a new language? You can't really treat it as your first one. Especially if you need it "for yesterday". You won't read about "if" or "for". No, you scan for what's different from what you already know. If you're lucky you'll find "python for javascript programmers", but that probably won't go into non-core details like this. In practice, you learn basics first and start coding. Then you find a piece of code somewhere that you don't understand. That's a learning opportunity! However, it's easy if it's a function since it's easily googlable. Operators are harder to search for, for instance in new versions of C# you have operators like "?." (just an example). Since googling is hard you go to some "operators documentation" and try to find it there. And hope that it covers the new version. For cases like this story it's even harder because it describes a concept (chaining) and you maybe don't even know which name is used for that.
At least ChatGPT recognized and explained it correctly, so it makes picking up new features easier than it used to be. I'm making a mental note to ask LLMs whenever I encounter unknown constructs.
If it’s a language I don’t know, I’d still read a book or check the doc for a tour of the syntax. I can scan one in a couple of hours and get an overview I can refer to later for a more specific question. Even if I needed it for yesterday.
I am idiot but I would, and have in the past, have wasted the whole interview time argue in that I am right and he is wrong.
Yes, John, some switches do start sending the packet before finish receiving it. It’s called cut-though switching. Hope you googled it. And yes it was worth losing the job offer over it.
John says you were wrong then and you are still wrong now. When talking about the problem domain John also mentions some switches do start sending the packet before finish receiving, it's called cut-though switching.
Python chained operators can be confusing, especially as it allows you to write things like:
— x < y > z
— a in b not in c
WalterBright mentioned that D will give you an error. When designing Starlark (a derivative of Python), I also decided to give an error for this, by making the comparison operators non associative.
I find these expressions a lot more readable for bound checks (like `if 0 <= n < len(something):`) compared to the imperative `if (n >= 0 && n < something.length)`. This was especially true when I used it for homework, where constraints described in mathematical form were a lot more common than when dealing with real life software.
The way it works in Python allows for some hilariously unreadable code, but the concept itself is good. I think it just needs more constraints to maintain readability, such as not allowing flipping the comparison sign (like in x<y>z).
Banning these chained comparison operators is not necessarily a bad idea as comparison operators can easily cause confusion in that manner, but the reason they cause confusion is that many programming languages use mathematical symbols differently than actual math. A=B=C is perfectly understandable and more readable than A=B ∧ B=C. Restraining ourselves to "operand operator operand" as the only infix expression model made sense decades ago when computers had storage counted in kilobits. Today, we can let go of the limitations of yore and use mathematical expressions in a way that plain sense.
Unit tests should be part of the interview too. As should googling the behaviour live. Missed opportunities to go beyond the bland exercise to see what it is like pairing up with this person.
I disagree: if you are demonstrating your mastery of a language (and with Python, these things are important: using appropriate syntax is the difference between dog slow code and fast code), you should use idiomatic patterns like the above.
Another of Python features is a great REPL: when unsure or confused by an interviewer, I'd just fire python from shell and type in 'x' == 'x' == 'x' to confirm and demonstrate it does the right thing (or write tests).
Obviously, the interviewer should be careful not to sidetrack the candidate much, and let them do the work and attempt to help only if things don't work out.
This is my opinion as well, but unfortunately isn’t always held by others. I once interviewed and by dint of knowing Python’s itertools module, absolutely destroyed the interviewer’s questions that they clearly thought would take some time to do.
I was told later that while I “obviously knew Python quite well, it didn’t give a good signal for my capabilities.”
>I once interviewed and by dint of knowing Python’s itertools module, absolutely destroyed the interviewer’s questions that they clearly thought would take some time to do.
If I see such a situation I usually ask the interviewer whether they want a concise solution using libraries or they want to see how I would do this if I had to do it from scratch.
Or I just offer both, I show that I can do it, but that I know it would be easier to do with x.
I think this is a great opportunity to show you're a good communicator as well as a problem solver.
In this case the candidate evidently didn't understand it either, but was repeating a pattern they had seen before, which IMO is a form of anti-mastery: don't do things that you don't understand, especially when you're supposed to be demonstrating your skill and understanding.
Having a candidate understand all of the edge cases of a language syntax is a very high bar to clear: there is a lot of programmer between a "master" and "anti-master".
I've used Python for 20+ years, and while I'd confidently use a == b == c or 1 < a < 10, I didn't know the specifics and wouldn't use it in cases like 10 > a < 8 (or really, any other case chaining supports).
I believe myself to be an expert at Python and I'll explain differences between different loop types (while/for, ranges/iterators/generators, list comprehensions, functools, external C libraries like pandas or numpy), I think I wouldn't be confused only because I am aware I don't know the details and have a quick way to prove it works.
Maybe I interpreted the article differently than you did.
I got the impression that the candidate was repeating the x == y == z pattern because they'd seen it in other people's code and was pretty sure it worked, not because they knew about comparison chaining. At minimum I'd expect a candidate to be able to clarify that (x == y) == z is not the same thing, not just "idk it works whenever I do it". My reaction to that was: at some point, you do need to at least be able to reason about your own code.
In a more generous light, yes, I agree with everything you wrote. The precise details of comparison chaining are out of scope and I'm sure that most Python developers (myself included) don't know or remember them.
I agree that anyone (including an obviously juniorish candidate) using it should be able to explain it to an extent, but maybe not if you confidently and wrongly claim it doesn't work as an interviewer :)
It's not a trick though. It's normal Python code. It's so common that you don't even think about it. It's just how equality comparisons work in the language.
My usage of "yield" (alongside knowledge of "prange" from numba) in an interview instead of return has sealed the deal on a 200K offer (ML role circa 2020).
"Confusing" the interviewer is a totally valid strategy! It's the only strategy when python async is involved!
I suspect the interviewer would have likely been fine if they’d been able to explain chained comparisons and how they work. I would probably consider the interviewer using them without understanding them to be a negative signal.
If the interviewer is a good interviewer - they inadvertently gave the candidate an oportunity to demonstrate a range of skills.
An outstanding candidate when questioned would mention that in most languages this evalates to that, but in python it evaluates to this, and offer an alternate method chaining and's on the spot in response to the confustion.
If they are a bad interviewer - they would allow their ego to derail the interview.
An outstanding candidate might be an average candidate in a wrong setting.
With my last job, I fumbled an easy question for me that I usually wouldn't, but it was a 10pm interview after I got up at 4am that morning and have almost fallen asleep when putting my kids to sleep at 9pm. I got the job (and then got promoted twice in 18 months), but if interviewer "inadvertently" confused me on top of my mental state, it might have been game over for me (and them, since I turned out to be an "outstanding" candidate).
The interviewer made no mistake though, they made an observation; they were confused by the boolean expression. The candidate said they understood the interviewers concerns and was also confused, and didn't have an immediate answer. This is all normal and healthy, and something you'd commonly see in healthy and competent development teams.
The only thing I see that's wrong is that the interviewer thinks something went wrong.
> ...the statement even though logical, is not technically right because if we start from the left cell[0][0] == cell[1][1] would evaluate to True and then True == cell[2][2] would evaluate to False given that the cell contains a char.
Interviewer seemed confident ("is not technically right") which confused the candidate:
> I told the candidate about the same and he agreed but he was confused at the same time
Candidates are already in a high stress situation, you don't want to add to it unless you really know what you are doing (i.e. you are specifically looking for deep language mastery).
Interviewer has correctly realized they've done this and learned from it: kudos to them even if too late for this candidate :)
As someone who trained in mathematics, I would 100% say that mathematicians would evaluate it as true. It's not the cleanest way to write it, but intent is clear.
D solved the problem with what happens with (a == b == c) in an unusual way. It gives an error message. Both the C way and the Python way are considered wrong. You're going to have to add ( ) to clarify.
Rust also gives a custom error message [0] that explicitly explains that "comparison operators cannot be chained" and shows you how to rewrite the expression using `&&`.
Which makes me wonder whether they came up with this idea themselves or if they borrowed it from D.
In any case, I think that this is the only reasonable solution for new languages. Because if you go with C way it will be surprising for people who expect Python way and vice versa. So, making this syntax illegal is the only way to uphold the principle of least astonishment [1].
I gave several presentations that included this back in the 2010-2012 timeframe, and it had already been in the language for maybe a decade.
When I implemented it, I had seen many knock-down drag-out debates about which was the best way to deal with it, and none of them mentioned simply making it illegal.
It was another one of those features that were based on my experience designing aircraft systems.
Another one adopted by pretty much all languages since D is the _ in numeric literals (although C++ used a backtick). It was just something that was obviously right once you saw it. But I didn't originate it, I cribbed it from Ada. I've always had a soft spot for Ada.
I remember when that went into Python. It seemed too cute. There was a lot of that around the Python 3.4, 3.5 era.
The case where all items are the same type is reasonable, but if implicit type conversions are invoked, it gets really complicated. Whatever the types are, Python will probably do something. Just not necessarily what you want.
I could see having this if there was a compile-time constraint that all variables be the same type. That would disallow the bad cases.
In defense of Python here, a==b==c -> (a ==_string b) ==_bool c is an extremely confusing way to write (predicate) nxor c. A principle here could be "operators that look the same shouldn't do different things when they're written close together."
Ah. I'm thinking of [1] from 2016, when there was a plan to add short-cut evaluation to short-cut comparisons. That avoids evaluating all the terms when not necessary. But that was deferred.
> Whatever the types are, Python will probably do something. Just not necessarily what you want.
Are you sure you are not confusing it with PHP? Python is not statically typed, but it is strongly typed. There is no implicit type conversion unless it's safe to do so (such as between a float and an int).
For example, print(1 > "foo") will raise a TypeError, and print("1" == 1) will print False.
In Python, "==" calls "__eq__", a class function. The left argument to "==" determines which class is involved. The right argument is just passed into the selected class member function. There's an asymmetry there. Operators are not necessarily commutative.
This can lead to some strange operator semantics in mixed-mode expressions.
Whether chained expressions left-associate or right-associate matters.
numpy.array operations do a lot of implicit conversions. You can use Python arrays on many operations that expect a numpy.array.
Mixing them works. Mostly.
python3
Python 3.10.12 (main, Sep 11 2024, 15:47:36) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
>>> a1 = numpy.array([1,2,3])
>>> a2 = [1,2,3]
>>> a1 == a2
array([ True, True, True])
>>> a2 == a1
array([ True, True, True])
>>> a1 == a2 == a1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> a2 == a2 == a2
True
>>> a1 == a1 == a1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
The candidate was writing an accounting program in COBOL and he wrote "ADD 2 TO SUM.". None of the handful of languages I kind of know support that syntax. It turns out that COBOL actually supports this nonsense. I explained to the candidate that when he is writing valid COBOL syntax, it is confusing to people who don't know COBOL and he agreed.
This is a fail for the interviewer, not the interviewee, nor for Python.
I'd be more confused about the curly brackets, I only know python and have never seen them used like that? My brain goes like: Here comes a set or dict.
Also, why not do `all(cell[0][0],cell[1][1]),cell[2][2])` if you want to have True when all 3 are True?
Perhaps I'm missing something, I don't exactly consider myself a 1337 coder.
And the nerve to put Javascript as the example about how equality should work, a language known for:
0 == '' evaluates to True
0 == '0' evaluates to True
false == 'false' evaluates to False
false == '0' evaluates to True
' \t\r\n ' == 0 evaluates to True
and the === operator you need to overcome the above. I don't have anything against JS and I understand why it is done that way, but if some language has weird and confusing comparison operators is JS.
The basic rule is "if there's a number on either side, convert the other side to a number before doing the comparison". I think false == '0' is the only one that fails that test, unless you think of false as a special case of 0 which makes false == 'false' the one that fails with that rule.
Paraphrasing: "Well done, Javascript… for finding yet another way to confuse us all."
I don't know many programming languages, but of the few I know any of them has a === because == is so confusing that you don't know exactly if (or when) it would bite you. You can't attack any language for their comparison operators from the Javascript camp.
It that blog post removed the sentence "And this is how it would definitely happen if the code was written in javascript" they would be fine. But you actually don't know what would definitely happen in Javascript with the line in question "cell[0][0] == cell[1][1] == cell[2][2]", unless you know 100% what is in these cells. For example:
cell[0][0] is "0", cell[1][1] is 0, cell[2][2] is true
and "cell[0][0] == cell[1][1] == cell[2][2]" evaluates to True.
> cell[0][0] is "0", cell[1][1] is 0, cell[2][2] is true
If those values came from <input> elements, the second and third examples aren't possible without having manually converted it beforehand. Input values are always strings, even a checkbox. That's where javascript's conversion rules and lax equality came from, an attempt to have sane defaults for simple uses: If a string is being compared to a number, the string is likely from an input and the number was typed into the code, so convert the string to a number before comparison.
>Well done, Python… for finding yet another way to confuse us all.
Would have liked to see some humility from the author, especially since he's an interviewer, but instead he deflects the fault onto Python as being confusing, rather than attributing the fault to himself and his own lack of understanding.
Unfortunately, this kind of blame game is par for the course with enginerds. I say this all the time: engineers really should do a sales or customer service job before getting into engineering. This will help you build empathy, which will build your social skills and kindle some humility within you. These traits will give you an edge over other nerds and take you further in your career.
>> I have a very basic question which I usually ask in a interview which is to implement a tic tac toe game. I like this because the logic is straightforward and it helps to judge things like code quality / speed / conciseness etc.
"I like this because the logic is straightforward and it helps to judge things like code quality / speed / conciseness etc."
No, it doesn't.
Everyone who comes up with their own pet way of evaluating and interviewing thinks - nay has the unshakable belief - that their way of evaluating developers is 100% spot on. However it's always something they just made up, justify with plain old words and never science.
How did you get "unshakable belief" from that paragraph?
Interviewing is inherently messy, imprecise and unscientific. The process involves a lot of guesswork and feeling, rather than accurate measurements of any kind. It creates a fictional work environment that is often stressful for candidates, so any signal you may get out if it is also inaccurate.
It's natural for interviewers to have a preference for certain questions, topics or ways of conducting an interview. You're free to disagree, of course, but claiming that your way is superior is silly.
Fascinating, the task was supposed to be straightforward and a way to judge code quality etc. Yet, when the candidate solved it in a simple way, they were told they had a bug that they didn't have. This is okay of course, coding is hard. It could have been an interesting opportunity for discussion, I guess.
What's interesting to me is the conclusion that it's somehow Python's fault. I wonder if that attitude would work if it came from the candidate.
I think we should be more careful with tests like this. They need to be done with more humility, so it's great that the post was written!
In my mind,I just automatically translate a EXPRESSION1 b EXPRESSION2 c as (a EXPRESSION1 b) and (b EXPRESSION2 c), so there's no ambiguity. But I agree, it can be very confusing,so I tend to write code that is easy to read without being a master. For example, what would have guessed this evaluates to:
False is False is False. If you thought, True, you were right :)
I would recommend to take a look at one of my favourite repos for more[1].
I don't code for a living anymore, but I think if I were writing Python I would probably AVOID using that idiom because of how jarring it could be to people who are unfamiliar with it, mostly due to how strange it is coming from other languages.
There doesn't seem to be a great upside to using it, and adding confusion to the codebase for whatever poor bum has to work on it later is bad karma.
What does the feature discussed in TFA (chained comparisons) have to do with Python 2 vs Python now.
As another commenter pointed out [0] this feature existed since 1.4 [1].
Also, I don't understand why so many people seem to be so confused/surprised by it. I can't be 100% sure because it was a long time ago, but I think that a lot of tutorials/introductory articles mention it. Like, "Hey, did you know that Python has this cool syntaxic sugar for this thing? Fascinating!". I think that I learned about chained comparisons long before I wrote my first line of Python.
Sounds like he didn't know the details, but had seen this pattern in Python code and knew it worked. When asked, he was confused about the exact details of why it worked.