# Python's equivalent of && (logical-and) in an if-statement

Python uses `and`

and `or`

conditionals.

i.e.

`if foo == 'abc' and bar == 'bac' or zoo == '123': # do something`

I'm getting an error in the IF conditional. What am I doing wrong?

There reason that you get a `SyntaxError`

is that there is no `&&`

operator in Python. Likewise `||`

and `!`

are **not valid** Python operators.

Some of the operators you may know from other languages have a different name in Python.The logical operators `&&`

and `||`

are actually called `and`

and `or`

.Likewise the logical negation operator `!`

is called `not`

.

So you could just write:

`if len(a) % 2 == 0 and len(b) % 2 == 0:`

or even:

`if not (len(a) % 2 or len(b) % 2):`

# Some additional information (that might come in handy):

I summarized the operator "equivalents" in this table:

`+------------------------------+---------------------+| Operator (other languages) | Operator (Python) |+==============================+=====================+| && | and |+------------------------------+---------------------+| || | or |+------------------------------+---------------------+| ! | not |+------------------------------+---------------------+`

See also Python documentation: 6.11. Boolean operations.

Besides the logical operators Python also has bitwise/binary operators:

`+--------------------+--------------------+| Logical operator | Bitwise operator |+====================+====================+| and | & |+--------------------+--------------------+| or | | |+--------------------+--------------------+`

There is no bitwise negation in Python (just the bitwise inverse operator `~`

- but that is **not** equivalent to `not`

).

See also 6.6. Unary arithmetic and bitwise/binary operations and 6.7. Binary arithmetic operations.

The logical operators (like in many other languages) have the advantage that these are short-circuited.That means if the first operand already defines the result, then the second operator isn't evaluated at all.

To show this I use a function that simply takes a value, prints it and returns it again. This is handy to see what is actuallyevaluated because of the print statements:

`>>> def print_and_return(value):... print(value)... return value>>> res = print_and_return(False) and print_and_return(True)False`

As you can see only one print statement is executed, so Python really didn't even look at the right operand.

This is not the case for the binary operators. Those always evaluate both operands:

`>>> res = print_and_return(False) & print_and_return(True);FalseTrue`

But if the first operand isn't enough then, of course, the second operator is evaluated:

`>>> res = print_and_return(True) and print_and_return(False);TrueFalse`

To summarize this here is another Table:

`+-----------------+-------------------------+| Expression | Right side evaluated? |+=================+=========================+| `True` and ... | Yes |+-----------------+-------------------------+| `False` and ... | No |+-----------------+-------------------------+| `True` or ... | No |+-----------------+-------------------------+| `False` or ... | Yes |+-----------------+-------------------------+`

The `True`

and `False`

represent what `bool(left-hand-side)`

returns, they don't have to be `True`

or `False`

, they just need to return `True`

or `False`

when `bool`

is called on them (1).

So in Pseudo-Code(!) the `and`

and `or`

functions work like these:

`def and(expr1, expr2): left = evaluate(expr1) if bool(left): return evaluate(expr2) else: return leftdef or(expr1, expr2): left = evaluate(expr1) if bool(left): return left else: return evaluate(expr2)`

Note that this is pseudo-code not Python code. In Python you cannot create functions called `and`

or `or`

because these are keywords. Also you should never use "evaluate" or `if bool(...)`

.

# Customizing the behavior of your own classes

This implicit `bool`

call can be used to customize how your classes behave with `and`

, `or`

and `not`

.

To show how this can be customized I use this class which again `print`

s something to track what is happening:

`class Test(object): def __init__(self, value): self.value = value def __bool__(self): print('__bool__ called on {!r}'.format(self)) return bool(self.value) __nonzero__ = __bool__ # Python 2 compatibility def __repr__(self): return "{self.__class__.__name__}({self.value})".format(self=self)`

So let's see what happens with that class in combination with these operators:

`>>> if Test(True) and Test(False):... pass__bool__ called on Test(True)__bool__ called on Test(False)>>> if Test(False) or Test(False):... pass__bool__ called on Test(False)__bool__ called on Test(False)>>> if not Test(True):... pass__bool__ called on Test(True)`

If you don't have a `__bool__`

method then Python also checks if the object has a `__len__`

method and if it returns a value greater than zero. That might be useful to know in case you create a sequence container.

See also 4.1. Truth Value Testing.

# NumPy arrays and subclasses

Probably a bit beyond the scope of the original question but in case you're dealing with NumPy arrays or subclasses (like Pandas Series or DataFrames) then the implicit `bool`

callwill raise the dreaded `ValueError`

:

`>>> import numpy as np>>> arr = np.array([1,2,3])>>> bool(arr)ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()>>> arr and arrValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()>>> import pandas as pd>>> s = pd.Series([1,2,3])>>> bool(s)ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().>>> s and sValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().`

In these cases you can use the logical and *function* from NumPy which performs an element-wise `and`

(or `or`

):

`>>> np.logical_and(np.array([False,False,True,True]), np.array([True, False, True, False]))array([False, False, True, False])>>> np.logical_or(np.array([False,False,True,True]), np.array([True, False, True, False]))array([ True, False, True, True])`

If you're dealing just with **boolean arrays** you could also use the binary operators with NumPy, these do perform element-wise (but also binary) comparisons:

`>>> np.array([False,False,True,True]) & np.array([True, False, True, False])array([False, False, True, False])>>> np.array([False,False,True,True]) | np.array([True, False, True, False])array([ True, False, True, True])`

(1)

That the `bool`

call on the operands has to return `True`

or `False`

isn't completely correct. It's just the first operand that needs to return a boolean in it's `__bool__`

method:

`class Test(object): def __init__(self, value): self.value = value def __bool__(self): return self.value __nonzero__ = __bool__ # Python 2 compatibility def __repr__(self): return "{self.__class__.__name__}({self.value})".format(self=self)>>> x = Test(10) and Test(10)TypeError: __bool__ should return bool, returned int>>> x1 = Test(True) and Test(10)>>> x2 = Test(False) and Test(10)`

That's because `and`

actually returns the first operand if the first operand evaluates to `False`

and if it evaluates to `True`

then it returns the second operand:

`>>> x1Test(10)>>> x2Test(False)`

Similarly for `or`

but just the other way around:

`>>> Test(True) or Test(10)Test(True)>>> Test(False) or Test(10)Test(10)`

However if you use them in an `if`

statement the `if`

will also implicitly call `bool`

on the result. So these finer points may not be relevant for you.