837

I'm trying to make a function that will compare multiple variables to an integer and output a string of three letters. I was wondering if there was a way to translate this into Python. So say:

x = 0
y = 1
z = 3
mylist = []

if x or y or z == 0:
    mylist.append("c")
if x or y or z == 1:
    mylist.append("d")
if x or y or z == 2:
    mylist.append("e")
if x or y or z == 3: 
    mylist.append("f")

which would return a list of:

["c", "d", "f"]
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
user1877442
  • 8,561
  • 3
  • 13
  • 5
  • 8
    use `1` in (tuple) –  Dec 05 '17 at 21:49
  • 7
    When you want to evaluate a list of statements in a any/all manner you can use `any`/`all` functions. For example: `all([1, 2, 3, 4, False])` will return False `all([True, 1, 2, 3])` will return True `any([False, 0, 0, False])` will return False `any([False, 0, True, False])` will return True – eddd Jun 04 '18 at 16:17
  • I did a summary post based on answers here: https://medium.com/codervlogger/python-how-to-test-multiple-variables-against-a-value-7338857b1fec – KenanBek Feb 12 '19 at 09:30
  • 11
    This question is a very popular duplicate target, but I think it's suboptimal for that purpose. Most people try to do something like `if x == 0 or 1:`, which is of course similar to `if x or y == 0:`, but might be a little confusing for newbies nonetheless. Given the sheer volume of *"Why isn't my `x == 0 or 1` working?"* questions, I would much rather use [this question](https://stackoverflow.com/q/20002503/1222951) as our canonical duplicate target for these questions. – Aran-Fey Apr 10 '19 at 10:06
  • 2
    Take extra care when comparing to "falsey" values like `0`, `0.0` or `False`. You can easily write wrong code which gives the "right" answer. – smci Apr 10 '19 at 10:09
  • 4
    For the opposite, see [Comparing a string to multiple items in Python](/questions/6838238/comparing-a-string-to-multiple-items-in-python) – tripleee Jan 08 '20 at 10:27
  • 1
    @tripleee this question really conflates two separate aspects: "Why doesn't this work?" and "How should I write it instead?" In the opposite case (comparing a single value to multiple candidates, rather than multiple values to the same candidate), https://stackoverflow.com/questions/6838238/comparing-a-string-to-multiple-items-in-python answers "How should I write it?". For "Why doesn't it work?", see https://stackoverflow.com/q/20002503 (as Aran-Fey linked). – Karl Knechtel Jul 01 '22 at 04:43
  • 1
    See also: https://stackoverflow.com/questions/6159313/how-to-test-the-membership-of-multiple-values-in-a-list for testing multiple variables for *membership in a list*. – Karl Knechtel Jul 06 '22 at 03:39

31 Answers31

1111

You misunderstand how boolean expressions work; they don't work like an English sentence and guess that you are talking about the same comparison for all names here. You are looking for:

if x == 1 or y == 1 or z == 1:

x and y are otherwise evaluated on their own (False if 0, True otherwise).

You can shorten that using a containment test against a tuple:

if 1 in (x, y, z):

or better still:

if 1 in {x, y, z}:

using a set to take advantage of the constant-cost membership test (i.e. in takes a fixed amount of time whatever the left-hand operand is).

Explanation

When you use or, python sees each side of the operator as separate expressions. The expression x or y == 1 is treated as first a boolean test for x, then if that is False, the expression y == 1 is tested.

This is due to operator precedence. The or operator has a lower precedence than the == test, so the latter is evaluated first.

However, even if this were not the case, and the expression x or y or z == 1 was actually interpreted as (x or y or z) == 1 instead, this would still not do what you expect it to do.

x or y or z would evaluate to the first argument that is 'truthy', e.g. not False, numeric 0 or empty (see boolean expressions for details on what Python considers false in a boolean context).

So for the values x = 2; y = 1; z = 0, x or y or z would resolve to 2, because that is the first true-like value in the arguments. Then 2 == 1 would be False, even though y == 1 would be True.

The same would apply to the inverse; testing multiple values against a single variable; x == 1 or 2 or 3 would fail for the same reasons. Use x == 1 or x == 2 or x == 3 or x in {1, 2, 3}.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 154
    I wouldn't be so quick to go for the `set` version. Tuple's are very cheap to create and iterate over. On my machine at least, tuples are faster than sets so long as the size of the tuple is around 4-8 elements. If you have to scan more than that, use a set, but if you are looking for an item out of 2-4 possibilities, a tuple is still faster! If you can arrange for the most likely case to be first in the tuple, the win is even bigger: (my test: `timeit.timeit('0 in {seq}'.format(seq=tuple(range(9, -1, -1))))`) – SingleNegationElimination Oct 24 '13 at 15:27
  • 71
    @dequestarmappartialsetattr: In Python 3.3 and up, the set is stored as a constant, bypassing the creation time altogether, eliminating the creation time. Tuples *can* be cheap to create as Python caches a bundle of them to avoid memory churn, making that the biggest difference with sets here. – Martijn Pieters Oct 24 '13 at 15:29
  • 18
    @dequestarmappartialsetattr: If you time *just* the membership test, for integers sets and tuples are equally fast for the ideal scenario; matching the first element. After that tuples lose out to sets. – Martijn Pieters Oct 24 '13 at 15:37
  • 26
    @MartijnPieters: Using the `set` literal notation for this test isn't a savings unless the contents of the `set` literal are also literals, right? `if 1 in {x, y, z}:` can't cache the `set`, because `x`, `y` and `z` could change, so either solution needs to build a `tuple` or `set` from scratch, and I suspect whatever lookup savings you might get when checking for membership would be swamped by greater `set` creation time. – ShadowRanger Sep 04 '16 at 00:37
  • 14
    @ShadowRanger: yes, peephole optimisation (be it for `in [...]` or `in {...}`) only works if the contents of the list or set are immutable literals too. Future language updates may change this. – Martijn Pieters Sep 04 '16 at 07:58
  • 1
    out of curiosity, i have a list of URLs as strings whereby i'm checking if the string contains multiple substrings (i.e. `'word1' in string and 'word2' in string...`). would the same syntax (i.e. `string in (word1, word2):`) work in this scenario or, as i am expecting, would it fail? – oldboy Jul 08 '18 at 21:30
  • 3
    @Anthony: that's the wrong test; `string` is not contained in the sequence `(word1, word2)`. You can use `all(w in string for w in sequence_of_words_to_test)` if you have multiple words to test in one string. Or use the `word1 in string and word2 in string` approach if you have just 2 or 3 such strings to test, and they are hardcoded. – Martijn Pieters Jul 09 '18 at 11:27
  • `in` is not constant-time "whatever the left-hand operand is" - technically with `set` you'd require at most the time taken to return the hash (calculate it if you didn't cache it) and do an equals check, which can very well take longer than constant time. Although this cost also exists with tuples. – Bernhard Barker Sep 24 '18 at 12:56
  • @Dukeling: 'constant time' is not a fixed quantity. It means the algorithmic complexity is O(1); it doesn't matter how many elements are in the set, containment testing will take the same amount of time, roughly, independently of the size of the set. – Martijn Pieters Sep 24 '18 at 13:12
  • @Dukeling: containment testing against a list or tuple or other sequence takes *linear* time, O(N), as each element is tested one by one. So when the tuple or list or sequence doubles, so does your average time taken. Sets and dictionaries thus beat sequences, especially as the number of elements grows. – Martijn Pieters Sep 24 '18 at 13:14
  • @Dukeling: calculating the hash is a separate O(something) operation, which is independent of the N size of the set, dict, tuple, etc. on the right-hand side. Python immutable types cache the value anyway. – Martijn Pieters Sep 24 '18 at 13:15
  • @MartijnPieters I know, but I'm still not sure why you say "whatever the left-hand operand is", because the cost changes depending on which elements you have in the set (even if it's always pseudo-constant) - I'd be fine with that paragraph if you remove that part, or replace it with "regardless of how many elements you have in the set\*". – Bernhard Barker Sep 24 '18 at 13:18
  • @Dukeling: I really don't see a need. Bad hash implementations are the exception, not the norm. We'd have to qualify `listobj[index]` as *O(1) (provided `index` is not a custom Python object with a bad `__index__` implementation)*, or sorting a list as *O(N log N) (provided the contents do not have complex ordering implementations that throw off comparison costs). For `contains` operations, the cost can be assumed to be constant. If the hash cost for a 'bad actor' object is large, it'll still be the same cost independent of the size of the set you test against. – Martijn Pieters Sep 24 '18 at 13:50
  • @SingleNegationElimination sure, tuples are faster than sets when comparing 2-3 values, but I would rank an answer imparting good practices from a time complexity perspective much higher than an answer that takes advantage of implementation details (which are subject to change) to achieve microptimizations on the level of "seriously, don't worry about it". – cs95 Dec 27 '18 at 09:19
  • a) The `set` approach is non-obvious, and there's always the generic pitfall with "falsey" non-integer values: `False in (1,0,2)` and `0.0 in (1,0,2)` both give `True`. b) Aliasing: accidental use of `if x is y` with small integers and [interned strings](https://stackoverflow.com/questions/1504717/why-does-comparing-strings-using-either-or-is-sometimes-produce-a-differe) can also give the wrong answer. Are this question and its answers only for integers? c) `in` is overloaded for both strings and sequences; consider: `'77' in '777'` is True, but `'77' in ('666','777')` is False. d) ... – smci Apr 10 '19 at 10:17
  • d) Subclasses/superclasses: `dict() == collections.Counter()` is True. e) Iterators and ranges: `[0,1,2] == range(3)` is False. Although `[0,1,2] == list(range(3))` is True – smci Apr 10 '19 at 10:20
  • @smci: I don't know what you are getting at here. You are describing normal equality testing rules, sets don't alter those. `False == 0` is also True, and so is `0.0 == 0`. And at least with a set membership test you avoid the sometimes-made `is` error. I'm not saying anything about using `in` on strings or tuples. – Martijn Pieters Apr 10 '19 at 18:33
  • I even found `if 1 in (x, y, z)` slightly faster than `if x == 1 or y == 1 or z == 1` – aerijman Aug 22 '19 at 13:54
118

Your problem is more easily addressed with a dictionary structure like:

x = 0
y = 1
z = 3
d = {0: 'c', 1:'d', 2:'e', 3:'f'}
mylist = [d[k] for k in [x, y, z]]
River
  • 8,585
  • 14
  • 54
  • 67
dansalmo
  • 11,506
  • 5
  • 58
  • 53
78

As stated by Martijn Pieters, the correct, and fastest, format is:

if 1 in {x, y, z}:

Using his advice you would now have separate if-statements so that Python will read each statement whether the former were True or False. Such as:

if 0 in {x, y, z}:
    mylist.append("c")
if 1 in {x, y, z}:
    mylist.append("d")
if 2 in {x, y, z}:
    mylist.append("e")
...

This will work, but if you are comfortable using dictionaries (see what I did there), you can clean this up by making an initial dictionary mapping the numbers to the letters you want, then just using a for-loop:

num_to_letters = {0: "c", 1: "d", 2: "e", 3: "f"}
for number in num_to_letters:
    if number in {x, y, z}:
        mylist.append(num_to_letters[number])
Georgy
  • 12,464
  • 7
  • 65
  • 73
ThatGuyRussell
  • 1,361
  • 9
  • 18
  • 1
    @VisioN You mean `for number in num_to_letters`? You don't need `.keys()`, dicts iterate over keys by default. Regarding using a string, you mean something like this, right? `for i, c in enumerate('cdef'): if i in {x, y, z}: mylist.append(c)` Agreed, that would be simpler. Or better yet, `s = 'cdef'; mylist = [s[i] for i in [x, y, z]]` – wjandrea Nov 28 '20 at 22:03
  • @wjandrea Yes, you are right, it's my mistake! I completely forgot about the default behaviour. Unfortunately, I cannot edit my comment, so I have deleted it since you have highlighted the better approach in your comment. – VisioN Nov 29 '20 at 11:09
55

The direct way to write x or y or z == 0 is

if any(map((lambda value: value == 0), (x,y,z))):
    pass # write your logic.

But I dont think, you like it. :) And this way is ugly.

The other way (a better) is:

0 in (x, y, z)

BTW lots of ifs could be written as something like this

my_cases = {
    0: Mylist.append("c"),
    1: Mylist.append("d")
    # ..
}

for key in my_cases:
    if key in (x,y,z):
        my_cases[key]()
        break
akaRem
  • 7,326
  • 4
  • 29
  • 43
  • 9
    In your example of the `dict` instead of a key, you will get errors because the return value of `.append` is `None`, and calling `None` gives an `AttributeError`. In general I agree with this method, though. – SethMMorton Feb 08 '14 at 20:57
  • 4
    the dict instead of a key is wrong, you will get Mylist=['c', 'd'] when the dictionary get initialized even if you commented out "for..loop" part – Mahmoud Elshahat Apr 07 '19 at 01:41
  • 2
    In your first example `filter` would be better than `map`, as it will return only instances where lambda evaluates to true – Alex Sep 16 '19 at 05:17
  • 4
    A comprehension is much simpler than a map of a lambda: `any(v == 0 for v in (x, y, z))` – wjandrea Feb 08 '20 at 05:21
36

If you ARE very very lazy, you can put the values inside an array. Such as

list = []
list.append(x)
list.append(y)
list.append(z)
nums = [add numbers here]
letters = [add corresponding letters here]
for index in range(len(nums)):
    for obj in list:
        if obj == num[index]:
            MyList.append(letters[index])
            break

You can also put the numbers and letters in a dictionary and do it, but this is probably a LOT more complicated than simply if statements. That's what you get for trying to be extra lazy :)

One more thing, your

if x or y or z == 0:

will compile, but not in the way you want it to. When you simply put a variable in an if statement (example)

if b

the program will check if the variable is not null. Another way to write the above statement (which makes more sense) is

if bool(b)

Bool is an inbuilt function in python which basically does the command of verifying a boolean statement (If you don't know what that is, it is what you are trying to make in your if statement right now :))

Another lazy way I found is :

if any([x==0, y==0, z==0])
rassa45
  • 3,482
  • 1
  • 29
  • 43
  • 8
    -1 There's a lot of bad practice here. `list` is a Python builtin; use another name instead, like `xyz` for example. Why do you construct the list in four steps when you can do one, i.e. `xyz = [x, y, z]`? Don't use parallel lists, use a dict instead. All in all, this solution is much more convoluted than [ThatGuyRussell's](https://stackoverflow.com/a/32085628/4518341). Also for the last part, why not do a comprehension, i.e. `any(v == 0 for v in (x, y, z))`? Also [arrays](https://docs.python.org/3/library/array.html) are something else in Python. – wjandrea Feb 08 '20 at 05:36
35

To check if a value is contained within a set of variables you can use the inbuilt modules itertools and operator.

For example:

Imports:

from itertools import repeat
from operator import contains

Declare variables:

x = 0
y = 1
z = 3

Create mapping of values (in the order you want to check):

check_values = (0, 1, 3)

Use itertools to allow repetition of the variables:

check_vars = repeat((x, y, z))

Finally, use the map function to create an iterator:

checker = map(contains, check_vars, check_values)

Then, when checking for the values (in the original order), use next():

if next(checker)  # Checks for 0
    # Do something
    pass
elif next(checker)  # Checks for 1
    # Do something
    pass

etc...

This has an advantage over the lambda x: x in (variables) because operator is an inbuilt module and is faster and more efficient than using lambda which has to create a custom in-place function.

Another option for checking if there is a non-zero (or False) value in a list:

not (x and y and z)

Equivalent:

not all((x, y, z))
GuiltyDolphin
  • 768
  • 1
  • 7
  • 10
33

Set is the good approach here, because it orders the variables, what seems to be your goal here. {z,y,x} is {0,1,3} whatever the order of the parameters.

>>> ["cdef"[i] for i in {z,x,y}]
['c', 'd', 'f']

This way, the whole solution is O(n).

B. M.
  • 18,243
  • 2
  • 35
  • 54
32

I think this will handle it better:

my_dict = {0: "c", 1: "d", 2: "e", 3: "f"}

def validate(x, y, z):
    for ele in [x, y, z]:
        if ele in my_dict.keys():
            return my_dict[ele]

Output:

print validate(0, 8, 9)
c
print validate(9, 8, 9)
None
print validate(9, 8, 2)
e
shuttle87
  • 15,466
  • 11
  • 77
  • 106
Bhargav Boda
  • 331
  • 3
  • 3
32

If you want to use if, else statements following is another solution:

myList = []
aList = [0, 1, 3]

for l in aList:
    if l==0: myList.append('c')
    elif l==1: myList.append('d')
    elif l==2: myList.append('e')
    elif l==3: myList.append('f')

print(myList)
Vishvajit Pathak
  • 3,351
  • 1
  • 21
  • 16
hamid
  • 469
  • 4
  • 6
32

All of the excellent answers provided here concentrate on the specific requirement of the original poster and concentrate on the if 1 in {x,y,z} solution put forward by Martijn Pieters.
What they ignore is the broader implication of the question:
How do I test one variable against multiple values?
The solution provided will not work for partial hits if using strings for example:
Test if the string "Wild" is in multiple values

>>> x = "Wild things"
>>> y = "throttle it back"
>>> z = "in the beginning"
>>> if "Wild" in {x, y, z}: print (True)
... 

or

>>> x = "Wild things"
>>> y = "throttle it back"
>>> z = "in the beginning"
>>> if "Wild" in [x, y, z]: print (True)
... 

for this scenario it's easiest to convert to a string

>>> [x, y, z]
['Wild things', 'throttle it back', 'in the beginning']
>>> {x, y, z}
{'in the beginning', 'throttle it back', 'Wild things'}
>>> 

>>> if "Wild" in str([x, y, z]): print (True)
... 
True
>>> if "Wild" in str({x, y, z}): print (True)
... 
True

It should be noted however, as mentioned by @codeforester, that word boundries are lost with this method, as in:

>>> x=['Wild things', 'throttle it back', 'in the beginning']
>>> if "rot" in str(x): print(True)
... 
True

the 3 letters rot do exist in combination in the list but not as an individual word. Testing for " rot " would fail but if one of the list items were "rot in hell", that would fail as well.
The upshot being, be careful with your search criteria if using this method and be aware that it does have this limitation.

Vishvajit Pathak
  • 3,351
  • 1
  • 21
  • 16
Rolf of Saxony
  • 21,661
  • 5
  • 39
  • 60
28
d = {0:'c', 1:'d', 2:'e', 3: 'f'}
x, y, z = (0, 1, 3)
print [v for (k,v) in d.items() if x==k or y==k or z==k]
Saksham Varma
  • 2,122
  • 13
  • 15
28

This code may be helpful

L ={x, y, z}
T= ((0,"c"),(1,"d"),(2,"e"),(3,"f"),)
List2=[]
for t in T :
if t[0] in L :
    List2.append(t[1])
    break;
michael zxc858
  • 407
  • 5
  • 7
15

You can try the method shown below. In this method, you will have the freedom to specify/input the number of variables that you wish to enter.

mydict = {0:"c", 1:"d", 2:"e", 3:"f"}
mylist= []

num_var = int(raw_input("How many variables? ")) #Enter 3 when asked for input.

for i in range(num_var): 
    ''' Enter 0 as first input, 1 as second input and 3 as third input.'''
    globals()['var'+str('i').zfill(3)] = int(raw_input("Enter an integer between 0 and 3 "))
    mylist += mydict[globals()['var'+str('i').zfill(3)]]

print mylist
>>> ['c', 'd', 'f']
Siddharth Satpathy
  • 2,737
  • 4
  • 29
  • 52
12

One line solution:

mylist = [{0: 'c', 1: 'd', 2: 'e', 3: 'f'}[i] for i in [0, 1, 2, 3] if i in (x, y, z)]

Or:

mylist = ['cdef'[i] for i in range(4) if i in (x, y, z)]
Vinayak Kaniyarakkal
  • 1,110
  • 17
  • 23
10

The most pythonic way of representing your pseudo-code in Python would be:

x = 0
y = 1
z = 3
mylist = []

if any(v == 0 for v in (x, y, z)):
    mylist.append("c")
if any(v == 1 for v in (x, y, z)):
    mylist.append("d")
if any(v == 2 for v in (x, y, z)):
    mylist.append("e")
if any(v == 3 for v in (x, y, z)):
    mylist.append("f")
rsalmei
  • 3,085
  • 1
  • 14
  • 15
  • 2
    This approach is more universal than ` if 2 in (x, y, z): mylist.append('e')` because allows arbitrary comparisons (e.g. `if any(v >= 42 for v in (x, y, z)):` ). And performance of all 3 methods (`2 in {x,y,z}`, `2 in (x,y,z)`, `any(_v == 2 for _v in (x,y,z))`) seems to be almost the same in CPython3.6 (see [Gist](https://gist.github.com/imposeren/06eb52771d1b779e64d9bef525da87d8)) – imposeren May 04 '19 at 04:47
10

Maybe you need direct formula for output bits set.

x=0 or y=0 or z=0   is equivalent to x*y*z = 0

x=1 or y=1 or z=1   is equivalent to (x-1)*(y-1)*(z-1)=0

x=2 or y=2 or z=2   is equivalent to (x-2)*(y-2)*(z-2)=0

Let's map to bits: 'c':1 'd':0xb10 'e':0xb100 'f':0xb1000

Relation of isc (is 'c'):

if xyz=0 then isc=1 else isc=0

Use math if formula https://youtu.be/KAdKCgBGK0k?list=PLnI9xbPdZUAmUL8htSl6vToPQRRN3hhFp&t=315

[c]: (xyz=0 and isc=1) or (((xyz=0 and isc=1) or (isc=0)) and (isc=0))

[d]: ((x-1)(y-1)(z-1)=0 and isc=2) or (((xyz=0 and isd=2) or (isc=0)) and (isc=0))

...

Connect these formulas by following logic:

  • logic and is the sum of squares of equations
  • logic or is the product of equations

and you'll have a total equation express sum and you have total formula of sum

then sum&1 is c, sum&2 is d, sum&4 is e, sum&5 is f

After this you may form predefined array where index of string elements would correspond to ready string.

array[sum] gives you the string.

tripleee
  • 175,061
  • 34
  • 275
  • 318
Sergei Krivonos
  • 4,217
  • 3
  • 39
  • 54
8

It can be done easily as

for value in [var1,var2,var3]:
     li.append("targetValue")
Seeni
  • 1,518
  • 1
  • 14
  • 22
7

To test multiple variables with one single value: if 1 in {a,b,c}:

To test multiple values with one variable: if a in {1, 2, 3}:

alamin
  • 2,377
  • 1
  • 26
  • 31
5

Looks like you're building some kind of Caesar cipher.

A much more generalized approach is this:

input_values = (0, 1, 3)
origo = ord('c')
[chr(val + origo) for val in inputs]

outputs

['c', 'd', 'f']

Not sure if it's a desired side effect of your code, but the order of your output will always be sorted.

If this is what you want, the final line can be changed to:

sorted([chr(val + origo) for val in inputs])
firelynx
  • 30,616
  • 9
  • 91
  • 101
4

You can use dictionary :

x = 0
y = 1
z = 3
list=[]
dict = {0: 'c', 1: 'd', 2: 'e', 3: 'f'}
if x in dict:
    list.append(dict[x])
else:
    pass

if y in dict:
    list.append(dict[y])
else:
    pass
if z in dict:
    list.append(dict[z])
else:
    pass

print list
Rohit Gawas
  • 267
  • 3
  • 8
4

Without dict, try this solution:

x, y, z = 0, 1, 3    
offset = ord('c')
[chr(i + offset) for i in (x,y,z)]

and gives:

['c', 'd', 'f']
Massifox
  • 4,369
  • 11
  • 31
3

This will help you.

def test_fun(val):
    x = 0
    y = 1
    z = 2
    myList = []
    if val in (x, y, z) and val == 0:
        myList.append("C")
    if val in (x, y, z) and val == 1:
        myList.append("D")
    if val in (x, y, z) and val == 2:
        myList.append("E")

test_fun(2);
Karan Shah
  • 111
  • 11
Mayur
  • 4,345
  • 3
  • 26
  • 40
3

You can unite this

x = 0
y = 1
z = 3

in one variable.

In [1]: xyz = (0,1,3,) 
In [2]: mylist = []

Change our conditions as:

In [3]: if 0 in xyz: 
    ...:     mylist.append("c") 
    ...: if 1 in xyz: 
    ...:     mylist.append("d") 
    ...: if 2 in xyz: 
    ...:     mylist.append("e") 
    ...: if 3 in xyz:  
    ...:     mylist.append("f") 

Output:

In [21]: mylist                                                                                
Out[21]: ['c', 'd', 'f']
Serhii
  • 1,367
  • 3
  • 13
  • 31
3

you can develop it through two ways

    def compareVariables(x,y,z):
        mylist = []
        if x==0 or y==0 or z==0:
            mylist.append('c')
        if  x==1 or y==1 or z==1:
            mylist.append('d')
        if  x==2 or y==2 or z==2:
            mylist.append('e')
        if  x==3 or y==3 or z==3:
            mylist.append('f')
        else:
            print("wrong input value!")
        print('first:',mylist)

        compareVariables(1, 3, 2)

Or

    def compareVariables(x,y,z):
        mylist = []
        if 0 in (x,y,z):
             mylist.append('c')
        if 1 in (x,y,z):
             mylist.append('d')
        if 2 in (x,y,z):
             mylist.append('e')
        if 3 in (x,y,z):
             mylist.append('f')
        else:
             print("wrong input value!")
        print('second:',mylist)

        compareVariables(1, 3, 2)
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
behnaz.sheikhi
  • 624
  • 8
  • 6
3

The or does not work like that, as explained by this answer.

While the generic answer would be use

if 0 in (x, y, z):
    ...

this is not the best one for the specific problem. In your case you're doing repeated tests, therefore it is worthwhile to compose a set of these variables:

values = {x, y, z}

if 0 in values:
    mylist.append("c")

if 1 in values:
    mylist.append("d")

We can simplify this using a dictionary - this will result in the same values:

mappings = {0: "c", 1: "d", ...}
for k in mappings:
    if k in values:
        mylist.append(mappings[k])

Or if the ordering of the mylist is arbitrary, you can loop over the values instead and match them to the mappings:

mappings = {0: "c", 1: "d", ...}
for v in (x, y, z):
    if v in mappings:
        mylist.append(mappings[v])
1

Problem

While the pattern for testing multiple values

>>> 2 in {1, 2, 3}
True
>>> 5 in {1, 2, 3}
False

is very readable and is working in many situation, there is one pitfall:

>>> 0 in {True, False}
True

But we want to have

>>> (0 is True) or (0 is False)
False

Solution

One generalization of the previous expression is based on the answer from ytpillai:

>>> any([0 is True, 0 is False])
False

which can be written as

>>> any(0 is item for item in (True, False))
False

While this expression returns the right result it is not as readable as the first expression :-(

fhgd
  • 402
  • 2
  • 10
1

To test multiple variables against a single value:

Wrap the variables in a set object, e.g. {a, b, c}. Use the in operator to test if the value is stored in any of the variables. The in operator will return True if the value is stored in at least one of the variables.

# ✅ test multiple variables against single value using tuple

if 'a' in (a, b, c):
    print('value is stored in at least one of the variables')

# ---------------------------------------------------------

# ✅ test multiple variables against single value using tuple

if 'a' in {a, b, c}:
    print('value is stored in at least one of the variables')

# ---------------------------------------------------------


# ✅ test multiple variables against single value (OR operator chaining)
if a == 'a' or b == 'a' or c == 'a':
    print('value is stored in at least one of the variables')
My Car
  • 4,198
  • 5
  • 17
  • 50
0

Here is one more way to do it:

x = 0
y = 1
z = 3
mylist = []

if any(i in [0] for i in[x,y,z]):
    mylist.append("c")
if any(i in [1] for i in[x,y,z]):
    mylist.append("d")
if any(i in [2] for i in[x,y,z]):
    mylist.append("e")
if any(i in [3] for i in[x,y,z]):
    mylist.append("f")

It is a mix of list comprehension and any keyword.

  • Why `i in [0]` instead of just `i == 0`? – Tomerikoo Apr 12 '21 at 17:08
  • 1
    For a single comparison like in this question, you can use "==" but if you want multiple comparisons with multiple variables, then you can use the "in" operator like: if any(i in [0,5,4,9,7] for i in[x,y,z] ) – Abhishek Kumar Saw Apr 13 '21 at 01:21
0

usage without if example:

x,y,z = 0,1,3
values = {0:"c",1:"d",2:"e",3:"f"} # => as if usage
my_list = [values[i] for i in (x,y,z)]

print(my_list)
SimoN SavioR
  • 614
  • 4
  • 6
0

FIRST, A CORRECTION TO THE OR CONDITIONAL:

You need to say:

if x == 0 or y == 0 or z == 0:

The reason is that "or" splits up the condition into separate logical parts. The way your original statement was written, those parts were:

x
y
z == 0   // or 1, 2, 3 depending on the if statement

The last part was fine --- checking to see if z == 0, for instance --- but the first two parts just said essentially if x and if y. Since integers always evaluate to True unless they're 0, that means the first part of your condition was always True when x or y didn't equal 0 (which in the case of y was always, since you had y = 1, causing your whole condition (because of how OR works) to always be True.

To avoid that, you need to make sure all parts of your condition (each side of the OR) make sense on their own (you can do that by pretending that the other side(s) of the OR statement doesn't exist). That's how you can confirm whether or not your OR condition is correctly defined.

You would write the statements individually like so:

if x == 0
if y == 0
if z == 0

which means the correct mergin with the OR keyword would be:

if x == 0 or y == 0 or z == 0

SECOND, HOW TO SOLVE THE PROBLEM:

You're basically wanting to check to see if any of the variables match a given integer and if so, assign it a letter that matches it in a one-to-one mapping. You want to do that for a certain list of integers so that the output is a list of letters. You'd do that like this:

def func(x, y, z):

    result = []

    for integer, letter in zip([0, 1, 2, 3], ['c', 'd', 'e', 'f']):
        if x == integer or y == integer or z == integer:
            result.append(letter)
            
    return result
        

Similarly, you could use LIST COMPREHENSION to achieve the same result faster:

def func(x, y, z):

    return [ 
                letter 
                for integer, letter in zip([0, 1, 2, 3], ['c', 'd', 'e', 'f'])
                if x == integer or y == integer or z == integer
           ]
    
    
Elliptica
  • 3,928
  • 3
  • 37
  • 68
-1
#selection
: a=np.array([0,1,3])                                                                                                                                                 

#options
: np.diag(['c','d','e','f']) 
array([['c', '', '', ''],
       ['', 'd', '', ''],
       ['', '', 'e', ''],
       ['', '', '', 'f']], dtype='<U1')

now we can use a as [row,col] selector, which acts as if any(...) condition :

#list of options[sel,sel]
: np.diag(['c','d','e','f'])[a,a]                                                                                                                                     

 array(['c', 'd', 'f'], dtype='<U1')
sten
  • 7,028
  • 9
  • 41
  • 63