0

Let A be a list of values and a a string/value whose I occurrence in A I want to check, is there a difference in speed or result between the two following versions?

A = [1, 2, 3]
a = 4

if a not in A:
    print("a is not in A!")

if not a in A:
    print("not a is in A!")

The Python docs on Expressions talk about the existence of the not in operator. Does that mean the first version is more canonical?

ojdo
  • 8,280
  • 5
  • 37
  • 60
  • 3
    There is a difference but in CPython that difference is optimised away at compile time. – Martijn Pieters Jul 22 '13 at 15:10
  • @MartijnPieters: very astute! could you please elaborate? I'm genuinely interested now – inspectorG4dget Jul 22 '13 at 15:11
  • @inspectorG4dget: See the duplicate, it is a peephole optimisation (where the compiler looks at the stack and changes bytecode to be more optimal). – Martijn Pieters Jul 22 '13 at 15:12
  • 1
    IIRC, `not in` is technically a different operator than `in`. Thus, technically, `not (x in xs)` is two operations, where `x (not in) xs` is one. Where "technically" means "disregarding the optimisation that compiles both to the same bytecode". – millimoose Jul 22 '13 at 15:15
  • @millimoose Interestingly, it seems the `operator` module does not contain a documented function equivalent to `not in`, just for `in` (namely, `contains()`), even though it has functions for both `is` and `is not` operations. Does the CPython compiler still optimize cases when using functions from the `operator` module in ways such as `not_(contains(a, b))`? – JAB Jul 22 '13 at 15:56
  • @JAB I'd doubt that although I haven't verified either way. `operator` is a way to access attributes "by reflection". (Probably not the right term but you get the point.) That is, all the compiler sees are plain old function calls without knowing what they mean. It's certainly possible to special-case handling them. Disassembly would tell you more. – millimoose Jul 22 '13 at 16:21
  • @JAB At the bytecode level the difference between `in` and `not in` is the same as the difference between `is` and `is not`. And, in fact, they're *all* in the same relationship to one another - different parameters to `COMPARE_OP`. I'm guessing the fact one doesn't have a method in `operator` doesn't mean anything in particular. – millimoose Jul 22 '13 at 16:24
  • @JAB As for whether calls to `operator` functions are optimised: [not by the compiler](http://ideone.com/FOxdZw) as far as I can tell. – millimoose Jul 22 '13 at 16:29
  • @millimoose So in terms of the functions in `operator`, it would indeed be more efficient for there to be an explicit `not_contains` function or somesuch to avoid the extra function call. ...Oh, silly me, it seems that last year I even commented on a question with that as the topic. http://stackoverflow.com/questions/11435206/python-operator-no-operator-for-not-in – JAB Jul 22 '13 at 16:53
  • @JAB I think at the point where you're using the `operator` module at all you've introduced so much overhead over using the operators directly there's little point in doing this sort of optimisation at all. Function calls in Python are pretty expensive, and certainly much much more expensive than a bytecode dispatch. – millimoose Jul 22 '13 at 17:17

0 Answers0