23

I am currently doing this:

if x in a and y in a and z in a and q in a and r in a and s in a:
    print(b)

Is there a more Pythonic way to express this if statement?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
blue_zinc
  • 2,410
  • 4
  • 30
  • 38

4 Answers4

29

Using the all function allows to write this in a nice and compact way:

if all(i in a for i in (x, y, z, q, r, s)):
    print b

This code should do almost exactly the same as your example, even if the objects are not hashable or if the a object has some funny __contains__ method. The all function also has similar short-circuit behavior as the chain of and in the original problem. Collecting all objects to be tested in a tuple (or a list) will guarantee the same order of execution of the tests as in the original problem. If you use a set, the order might be random.

Community
  • 1
  • 1
Bas Swinckels
  • 18,095
  • 3
  • 45
  • 62
  • 5
    Nitpicking: this is *not* completely equivalent to the OP code, even if `all` is lazy. The problem is that the tuple `(x, y, z, q, r, s)` is built completely before running any iteration and if `x`, `y` etc. are expressions some of them could raise an exception. In this case the OP's code *could* return `False` and avoid the exception while this code will always raise the exception. – Bakuriu Jul 18 '15 at 05:01
  • 2
    "some funny `__contains__` method" -- and so we reach the trickier corners of duck-typing. The questioner says "in an iterable", but if `a` implements `__contains__` then for the questioner's code it doesn't even matter whether `a` is iterable or not... – Steve Jessop Jul 18 '15 at 11:24
  • 2
    @Bakuriu, but they are not expressions. They are variables -- assuming that the OP is writing Python, and not meta-Python. – Paul Draper Jul 18 '15 at 16:21
  • @PaulDraper I always assume "meta-python" if the code isn't complete. I simply wanted to point out that there **is** a small semantic difference between the two, for future readers. – Bakuriu Jul 18 '15 at 17:38
  • This kind of thing would be nicer if Python had a `.contains()` method and maybe an `if_all` statement. I think the conditional would read better as ```if all (a.__contains__(i) for i in [x, y, z, q, r, s]):``` or if you prefer avoiding .__contains__, then using operator.contains instead: ```if all (contains(a, i) for i in [x, y, z, q, r, s]):``` – pyrocrasty Jul 18 '15 at 21:51
  • @pyrocrasty `a.contains(b)` is written as `b in a` in Python, that is nice enough for me. And `if all` reads better than `if_all`. – Bas Swinckels Jul 18 '15 at 22:32
  • @Bas Swinckels: the `in` operator might be fine in many cases, but `if all(i in a for i in (x, y, z, q, r, s))` looks like gibberish. Having two different meanings for `in` in the same expression makes it worse. It takes about a second to actually parse that statement when you read it, which means if you're scanning code, you have to stall at that line. Really, the blame lies with the terrible syntax for comprehensions/generator expressions, not with the `in` operator itself, but the problem remains. – pyrocrasty Jul 19 '15 at 07:10
  • @Bas Swinckels: Ideally, `all` would be a 2-argument function taking a predicate and an iterable, and a conditional like this would simply use `if all(a.__contains__, (x,y,z,q,r,s))`. The 1-argument `all` function is a particularly bad combination with Python's verbose comprehension syntax. – pyrocrasty Jul 19 '15 at 07:16
  • @pyrocrasty That is your opinion, I much prefer Python as it is. I do concur that there is some confusion due to the double meaning of `in`. Adding parenthesis might make the visual parsing a bit easier: `all((i in a) for i in (...))`. – Bas Swinckels Jul 19 '15 at 07:21
18

Another way to do this is to use subsets:

if {x, y, z, q, r, s}.issubset(a):
    print(b)

REPL example:

>>> {0, 1, 2}.issubset([0, 1, 2, 3])
True
>>> {0, 1, 2}.issubset([1, 2, 3])
False

One caveat with this approach is that all of x, y, z, etc. must be hashable.

Thane Brimhall
  • 9,256
  • 7
  • 36
  • 50
7
if all(v in a for v in {x, y, z, q, r, s}):
    print(b)
3

Converting to set, either:

if len({x, y, z, q, r, s} - set(a)) == 0:
    print b

or

t = {x, y, z, q, r, s}
if t & set(a) == t:
    print b
thebjorn
  • 26,297
  • 11
  • 96
  • 138