Expressions in  programming, like in Mathematics, are used to compute results of combining some values. In programming, the expressions are  more advanced and can evaluate to a wide variety of values belonging to different types. 

Expressions are one of the building blocks of statements, and while an expression can stand by itself to form a complete statement, in most cases, they are combined with other entities, including loops, branches, functions, etc., to form statements

Operators, on the other hand,  are used to join the values which forms an expression. These values can be simple such as integers  and others can be more complex such as another expression or even the return value of a function. 

We will look on how Expressions are used  in  Python, from simple one value expressions, to how we can form more complex ones using the operators available.  

Operators and operands

Operators together with operands are the building blocks  of Python expressions. Operators by  themselves do not hold any value  and using them without necessary operands will  raise a SyntaxError. 

An Operand is an entity  in a Python expression whose value is well defined for example an integer, a string, a  list e.t.c.  Operators are used with one or more operands  in a systematic way, to get a fresh value from the operand(s).   

It is worth noting that  Python allows operator overloading  in objects, meaning that a user can define  the work of an operator in the context of a user defined object.  Operator overloading is beyond the scope of this article and we will only focus on the standard use of the operators as specified by Python.

Operators in Python can be grouped into four categories according to the type of operation  they are used  in, that is:

  • arithmetic operators
  • relational operators
  • logical  operators
  • bit-wise  operators

Another grouping mechanism which can be used on operators is by the number of operands that an operator specifies.  Grouping them this way we get:

  • unary operators
  • binary operators   

The word uni as you might already know means one  and the word bi means two. The unary operators as you might have deduced from the previous statement , specifies only one operand   while the binary operators  specifies two operands. We write unary operators in prefix form, meaning that the operator appears before the single operand and in binary operators, the operator is placed in-between the two operands , this is referred to as the infix form.  

Arithmetic Operators

Most  arithmetic operators are familiar from Mathematics, some of them are used just the way they are used in math while others may differ or have polymorphic usage when used in  programming. In this context, they are used with numeric types: int, float and complex.  The following table shows the 7 arithmetic operators available in Python.

Operator Operation
+ Addition
- Subtraction
* Multiplication
/ Division
// Floor Division
% Modulo
** Power
Addition

The  plus  sign ( + ) is used as the addition  operator, it    is one of the operators in Python that can be used  in the binary form as well as  the unary form. Used in binary form ( with two operands ), the operator is used to  get the standard arithmetic sum of its operands. In the unary form( one operand ) , it is used as an identity operator and it returns the same value as the  operand. The examples below demonstrates some of  the operator's usage:

Prototype Example
(int + int)  -> int  7 + 5 -> 12
(float + float ) -> float 3.0 + 6.0 -> 9.0
(complex + complex) -> complex 3j + 5j - >  8j
(float + int)  -> float 5.0 + 10 -> 15.0
+ (int) -> int  +(-9) -> -9
+ (float) -> float +(5.0) -> 5.0
+ (complex) -> complex +3j -> 3j
 
Subtraction

The  hyphen  ( - ) is used as the subtraction  operator , it can be used in both binary and unary form. The binary form is used for subtraction and returns the standard difference of it's operands while the unary form is used to negate the value of the operand.  Examples:

Prototype Example
(int - int)  -> int  7 - 5 ->  2
(float - float ) -> float 9.0 - 6.0 -> 3.0
(complex - complex) -> complex 9j -  5j - >  4j
(float - int)  -> float 5.0 - 10 -> -5.0
- (int) -> int  -(-9) -> 9
- (float) -> float -(5.0) -> -5.0
- (complex) -> complex -3j -> -3j
 
Multiplication

The asterisk ( * )   is used to evaluate the  standard arithmetic product of the operands, it only takes the binary form.  Some use cases are:

Prototype Example
(int * int)  -> int  7 * 5 ->  35
(float * float ) -> float 9.0 * 7.0 -> 63.0
(complex * complex) -> complex 3j * 5j ->  -12 + 0j
(float * int ) -> float 6 * 8.0 -> 48.0
 
Division

The forward slash( / ) is used for evaluating the arithmetic quotient of the operands, it  only takes the binary form.  Some use cases are:

You should always keep in mind that using these operator even with all operands being integers always return a float result.

Prototype Example
(int / int)  -> float  12 / 3  ->  4.0
(float / float ) -> float 30.0  /  5.0 -> 6.0
(complex / complex) -> complex 3j / 5j ->  .06 + 0j
Floor Division

The floor of a given value is the closest integral value lower than it for example the floor value of 2.9 is 2 , while the ceil value is the closest integral value upper of the number for example the ceil value of 2.9 will be 3 .The floor value for a number has a lot of use cases in real life  and Python provides an operator for evaluating it. 

Double forward slashes( // ) are used  to denote floor division operation. While the normal division in python i.e using a single slash, always returns a  floating result,  floor division returns an integer value if both operands are integers and a floating value if any of the operand is a float.  This operation only works with int and float  types and can only be used in the binary form. the following table shows some use cases:

Prototype Example
(int // int)  -> int  12 // 3  ->  4
(float // float ) -> int 3.0  //  5.0 -> 0.0
(float // int ) -> int 9.0 // 3 -> 3.0
 
Modulo Operation

The percentage sign ( % ) is used to perform modulo operation. The operator can only be used in the binary form and it is used to get the remainder value after dividing the left operand with the right operand. The return value i.e the remainder , always inherits the sign of the divisor( right operand ), so if the divisor is negative, the modulo results will be negative and if the divisor have a positive value, the results will be positive. The following table shows some use cases.

Prototype Example
(int %  int)  -> int  12 % 3  ->  0
(float % float ) -> float

3.0  % 5.0 -> 3.0

(float % int ) -> float 9.0 % -3 ->  -0.0 
 
Power

Double asterisks ( ** ) are used to perform power operation. It can only be used in the binary form , following table summarizes some use cases:

Prototype Example
(int **  int)  -> int  2 ** 3  ->  8
(float ** float ) -> float 3.0  ** 5.0 ->  243.0
(complex **  complex ) -> complex 2j  **  3j  -> (-0.00437 + 0.00784j)

 

As you might have observed, the arithmetic operations usually returns results that matches their operand types and if  the operands used are of different types the result is normally of the type with higher precedence than the other, for example using most operators with an int and a float operand will return a floating value.

 

Type conversion

The arithmetic operators have uses beyond what we discussed above, in the various examples, you have seen how we can use an operator with operands of differing types for example adding an integer to a floating value. Operations of that type requires implicit type conversions which we will discuss here.

The operator signs in Python converts operands given into necessary types before they are evaluated,  they achieve this by using a well established hierarchy with complex type on top and the bool type ot the bottom of the hierarchy as follows:

bool -> int -> float -> complex

Whenever operands of differing  types are encountered, the one at a lower level is promoted  to the type of the other operand . This is  the most logical approach since for example, there is  no integer equivalent to 5.5 but there is a float for any given integer for example for  5 it is 5.0 .

Arithmetic on boolean Values

To perform arithmetic operations on boolean values, the values are first promoted to integer type, the results are therefore of type int too. The boolean False is promoted to its integer value which is 0 and  True to it's integer value which is 1.

False + False
//0
True + True 
//2
False + True 
//1
True  + False
//1
Arithmetic operators on strings

Two arithmetic operators and are overloaded to support operations on strings. The operator is used for concatenating two strings  while the operator is used for replicating a string  number of times specified, the operator takes an integer and a string as it's operands.

print("Hello, " + "World!")

Hello, World!

 

print("Hello" * 3)

HelloHelloHello

 

Relational Operators

These operators always returns a boolean value i.e True or False , and are used to test whether a certain relation holds in it's operands.All of these operands operates in binary form meaning that each requires two operands.  The following table summarizes te 9 relational operators available in Python and the operations they are used to perform.

Operator Name Semantics
== equals True when the operands have equal values
!= not  equals True when  the operands have unequal values
< less than True when the left operand is less than the  right operand 
> greater than True when the left operand is greater than the right operand
<= less than or equals True when the left operand is less than or equal to the right operand
>= greater than or equals True when the left operand is greater than or equal to the right operand
is  object identity True when the left operand refers to the same object as the right operand. 
in inclusion True when the left operand appears in the right operand(which is an iterable)
not in negate inclusion  True when the left operand does not appear in the right operand(which is an iterable)

We can  group the relational operators according to the type of operations they perform:

  • Equality and identity ( ==, !=, is)
  • Ordered Comparison ( <, >, <=, >= )
  • inclusion ( in, not in )
5 > 10
//False
5 < 10
//True
5 == (2 + 3)
//True
10 != (5 * 2)
//False
12 >= (3 * 4)
//True
12 <= (4*5)
//False
2 in [5, 3, 4, 7, 9]
//False
"W" in "Hello, World!"
//True 
"r" not in "Hello, World!"
//False

Logical Operators

These operators are used to perform boolean logic ,they are mostly used with boolean operands. If the operand(s) are not of the bool type i.e True or False, their boolean values are evaluated in advance. These operators are summarized in the following table:

Operator Name Semantics
and Conjunction True if both operands have a boolean value of True
or Disjunction True if either or both operands have a boolean value of True
not Negation True if none of the operands have a boolean value of True
!= exclusive or True if either of the operand is True (returns False if both are True)

To perform complex operations with the logical operators, the programmers needs to understand the expected results of using these operators with varying operands. The following truth table summarizes the results of applying the operators on two values and each holding a given boolean value. 

a b and b or b not b != b
False False False False True False
False True False True False True
True False False True * True
True True True True * False

The not operator is a unary operator and it simply  checks whether an object has a boolean value of False, that is why it's column in the table above looks a little bit different 

Bit-wise Operators

These operators operates on it's operands which are normally integers at bit level. There are six bit-wise operators, their usage are shown on the table below.

Bit-wise Operator Examples
and & 0011 & 0101 -> 0001
inclusive or | 0011 | 0101 -> 0111
not ~ ~01 -> 10
exclusive or ^ 0011 ^ 0101 -> 0110
shift left << 101 << 2 -> 10100 
shift right >> 101 >> 2 -> 1