Python File
Python File
5.1
Introduction
78
79
5.2
Operators
In this section we will examine details of all the Python operators introduced
in Table 2.3. We classify these operators, both symbols and identifiers, into
four categories and examine them separately: Arithmetic (+ - * / // % **),
Relational: (== != < > <= >= is in), Logical (and not or), and finally Bit
wise (& | ~ ^ << >>).
When describing these operators we will uses a header-like notation called
a prototype. A prototype includes the symbol/name of the operator, the
type(s) of its operand(s), and the type of result it returns; unlike headers, prototypes do not specify parameter names nor default argument values. As with
headers, if a symbol/name has multiple prototypes we say what it is overloaded.
Most operators are overloaded.
For example, the prototype < (int, int) -> bool specifies that one of the
overloaded prototypes of the lessthan operator has two int operands and returns a boolean result. Semantically, this operator returns a result of True
when its left operand is strictly less than its right operand; otherwise it returns
a result of False: so 1 < 3 returns a result of True and 3 < 1 returns a result
of False.
We categorize prototypes by the number of operands they specify. Pythons
operator prototypes specify either one or two operands: operators with one
operand are called unary operators (think uni-cycle) and operators with two
operands are called binary operators (think bi-cycle). We write unary operators in prefix form (meaning before their single operand) and we write binary
operators in infix form (meaning inbetween their two operands): the first
type in a prototype specifies the left operand and the second specifies the right
operand.
5.2.1
Arithmetic Operators
This section explains the prototypes (syntax) and semantics of all the arithmetic
operators in Python, using its three numeric types: int, float and complex.
Some are familiar operators from mathematics, but others are common only
in computer programming. The end of this section discusses how Pythons
arithmetic operators apply to bool values and how Python interprets operands
of mixed types (e.g., 3 + 5.0)
80
Addition: The + operator in Python can be used in both the binary and
unary form. The binary form means add, returning a result that is the standard
arithmetic sum of its operands. The unary form means identity, returning the
same value as its operand.
Prototype
+ (int,int) -> int
+ (float,float) -> float
+ (complex,complex) -> complex
+ (int) -> int
+ (float) -> float
+ (complex) -> complex
Example
3 + 5 returns the result 8
3.0 + 5.0 returns the result 8.0
3j + 5j returns the result 8j
+3 returns the result 3
+3.0 returns the result 3.0
+3j returns the result 3j
Subtraction: The - operator in Python can be used in both the binary and
unary form. The binary form means subtract, returning a result that is the standard arithmetic difference of its operands: left operand minus right operand.
The unary form means negate, returning the negated value as its operand: zero
to zero, positive to negative, and negative to positive.
Prototype
- (int,int) -> int
- (float,float) -> float
- (complex,complex) -> complex
- (int) -> int
- (float) -> float
- (complex) -> complex
Note that we will write negative values
even though they are not literals.
Example
3 - 5 returns the result -2
3.0 - 5.0 returns the result -2.0
3j - 5j returns the result -2j
-3 returns the result -3
-3.0 returns the result -3.0
-3j returns the result -3j
like -2 as the result of computations,
Example
3 * 5 returns the result 15
3.0 * 5.0 returns the result 15.0
3j * 5j returns the result (-15+0j)
Complex numbers, which have real (r) and imaginary (i) parts display in
Python as (r + ij); the product of two purely imaginary numbers has a real
part that is the negative product of the imaginary parts with zero as their imaginary part: 3j * 5j returns the result (-15+0j), which is stored and displayed
as a complex number (see the prototyp), even though its imaginary part is 0.
Division: The / operator (slash) in Python can be used only in the binary
form, which means division, returning a result that is the standard arithmetic
quotient of its operands: left operand divided by right operand.
Prototype
/ (int,int) -> float
/ (float,float) -> float
/ (complex,complex) -> complex
Example
3 / 5 returns the result 0.6
3.0 / 5.0 returns the result 0.6
3j/5j returns the result (.06+0j)
Notice here that dividing two int values always returns a float result (unlike addition, subtraction, and multiplication): so even 4 / 2 which has an
81
Example
3 // 5 returns the result 0
3.0 // 5.0 returns the result 0.0
Example
8 % 3 returns the result 2
8.0 % 3.0 returns the result 2.0
In Python, the sign of the returned result is the same as the sign of the divisor
and the magnitude of the resulted result is always less than the divisor: so 17%5
returns a result of 2 because when 17 is divided by 5 the quotient is 3 and the
remainder is 2; but 17%-5 returns a result of -2 because the divisor is negative.
Mathematically a%b = a - b*(a//b) for both int and float operands. Most
uses of the % operator in programming have two positive operands.
Why is the modulo division operator useful? If we want to make change for
84 cents, we can use modulo to determine how much change is left to make after
giving quarters: using the formula above, the result is 84 - 25*(84//25) where
we subtract from 84 the product of 25 times the number of quarters returned
as change, which computes the amount of change given by 3 quarters.. So, in
the problem of making change, both the floor division (integer quotient) and
modulo (remainder after division) operators are useful.
Power: The ** operator in Python can be used only in the binary form, which
means power returning a result that is the left operand raised to the power of
the right operand.
Prototype
** (int,int) -> int
** (float,float) -> float
** (complex,complex) -> complex
Example
3 ** 5 returns the result 243
3.0 ** 5.0 returns the result 243.0
3j ** 5j returns the result (0.00027320084764143374-0.00027579525809376897j
Here are some general observations about arithmetic operators. For most operators (+ - * // % ** but not /), their result types match their operand types.
82
5.2.2
Before finishing our discussion of arithmetic operators in Python, there are two
topics we must cover: arithmetic on boolean operands and arithmetic on mixed
type operands. Both involve the general concept of implicit typeconversion:
Python arithmetic operators implicitly convert values of the type bool to int,
int to float, float to complex when necessary. Think about these types as
a hierarchy with complex at the top and bool at the bottom: any value from
a lower type can be converted to an equivalent value from an upper type: e.g,
True converts to 1 converts to 1.0 converts to (1+0j). Note that conversion
the other way might not be equivalent: e.g., there is no way to convert 1.5 to
an equivalent integer.
Arithmetic on Boolean Values: To perform any arithmetic operators on
boolean values, first they are promoted (up the hierarchy) to integers: False
is promoted to 0 and True is promoted to 1: so, in True * False Python
promotes the boolean values to 1 * 0, which returns the result 0. The term
promotion implies movement up the numeric type hierarchy.
Arithmetic on Mixed Types: To perform any arithmetic operator on two
operands that do not have the same type, the lower type operand is promoted
to the type of the higher type operand: so, in True * 1.5 the boolean value
True is promoted to the integer value 1, which is promoted to the floatingpoint
value 1.0, which is then satisfying one of the prototypes for * multiplied
by 1.5, which returns the result 1.5.
Conversions in these two cases are implicit: they are performed automatically
by Python, to be able to satisfy the prototypes of the operators. But we can
also explicitly convert between numeric types, both up and down the numeric
type hierarchy. In fact, we can use bool, str, and each numeric type name
(int, float, and complex) as the name of a function that converts values to
that type from other types. We have already seen some examples of explict
conversion in Section 4.5.3, which discussed converting strings input from the
user to values of the type int and float.
The table below summarizes the prototype and semantics of each conversion
function. Note that when any conversion function is passed an argument of its
own type, it returns a reference to its argument: e.g., int(3) returns 3. See
Section 4.5.3, for how int and float convert strings to their types; complex is
similar: e.g., complex((3+5j)) returns (3+5j).
Prototype
str (T) -> str
int (T) -> int
float (T) -> float
complex (T) -> complex
bool (T) -> bool
Semantics
returns string showing literal (possible signed for numeric types)
returns 0 for False, 1 for True; truncated float; truncated realpart of complex
returns 0.0 for False, 1.0 for True; equivalent for int; realpart for complex
returns (0+0j) for False, (1+0j) for True; equivalent int and float
False for empty string and zero value, True for nonempty string and nonzero value
83
We have seen that when converting upward in the numeric type hierarchy, no
information is lost. But, when converting downward, information can be lost:
converting from complex to float the imaginary part is lost; converting from
float to int the decimal part is lost; converting from int to bool zero (or the
empty string) converts to False and any nonzero value (or any nonempty
string) converts to True.
A surprising result of these semantics is that bool(False) returns a result
of True; check it. In Section ?? we will learn how to convert False to False
and True to True correctly.
Experiment with the Python interpreter to explore the arithmetic operators
and conversion functions.
5.2.3
String Operators
5.2.4
Relational Operators
Relational operators always return a boolean result that indicates whether some
relationship holds between their operands. Most relational operators are symbols (== != < > <= >= but two are identifiers (is in) and one is a compound
identfier (not in). The table below lists the prototypes and the relational operators to which they apply. All their prototypes are similar: same-type operands
returning a boolean result.
Prototype Form
r-op (int,int) -> bool
r-op (float,float) -> bool
r-op (complex,complex) -> bool
r-op (bool,bool) -> bool
r-op (str,str) -> bool
Operators
==
!=
==
!=
==
!=
==
!=
==
!=
is
is
is
is
in
not in
84
Another way to classify these operators is equality operators (== !=), ordered
comparison (< > <= >=), identity (is), and inclusion (in and also not in).
The table belows lists the relational operators, their names, and their semantics.
r-op
==
!=
<
>
<=
>=
is
in
Name
equals
not equals
less than
greater than
less than or equals
greater than or equals
object identity
inclusion
Semantics
True when
True when
True when
True when
True when
True when
True when
True when
the
the
the
the
the
the
the
the
Equality and Identity: The equality operators (== !=) are the easiest to
understand: they have prototypes for each type in Python and return a result
based on the whether the two operands store references to objects storing
the same value. The identity operator (is) is similar but more restrictive: it
also has prototypes for each type, but returns a result based on whether the
two operands store references to the same object. Read over these last two
sentences carefully, because the difference is subtle. Figure 5.1 illustrates the
difference between == and is: note that there are two str objects that each
store the same value: abc. This picture is the result of Python executing the
following script.
1
2
3
a = abc
b = a
c = input ( Enter String : )
There are a few cases where Python creates two different objects that both store
the same value: input of strings from the console; writing integers literals with
many digits; writing floatingpoint and complex literals. But in most programs
when we compare values we want to check equality not identity, so we dont
care whether Python creates multiple objects. But, in some advanced programs
we will want to check identity, and now we know how. Finally, note that if the
is operator returns True the == operator must return True: if two references
refer to the same object, then the value of that object must be the same as
itself. Experiment with these concepts in the interpreter: try >>> a = 1.5
>>> b = 1.5 >>> a == b >>> a is b.
Ordered Comparison: The ordered comparsion operators (< > <= >=) are
simple to understand for the numeric types (int and float; there is no prototype for complex) and bool: they have the same meanings as mathematics,
with False considered to be less than True which reinforces the idea False
promoting to 0 and True promoting to 1. If we try an ordered comparison
85
of complex values, Python will raise the TypeError exception, describing this
type as unorderable.
Lets now learn the algorithm for comparing strings, which are sequences
of zero or more characters. First, we must learn to compare the individual
characters in strings: we do so according to their decimal values, illustrated in
the ASCII table below. Although Python uses unicode characters, we will still
stick to the ASCII subset, and use the following table to compare characters.
Figure 5.2: Character Values in ASCII
To answer the question, How does the character a compare to the character
1? we translate it into the question, How does the decimal value of the
character a compare to the decimal value of the character 1? According to the
ASCII table, the decimal value of the character a is 97 and the decimal value
of the character 1 is 49, so a is greater than 1.
Dont memorize the ASCII table, but know that the digit characters are less
than the uppercase characters, which are less than the lowercase characters;
and within each of these character groupings, decimal values increase: e.g., A
is less than B is less than ... is less than tt Z. In fact, the builtins module
defines the ord(str) -> int function which returns the ASCII value of any
single character: ord(a) returns the result 97; if the argument to ord is not
a single character, it raises the TypeError exception.
86
Given this information, the algorithm for comparing strings is follows. This is
called dictionary or lexicographic ordering.
Step 1 Start at the leftmost character of each string; skip all characters that
are pairwise the same, trying to find a character that is different.
Step 2 Based on the outcome, compare the strings as follows:
Step 2A Found a different character: the strings compare according
to how the different characters compare.
Step 2B Found no different characters (e.g., all characters in one
string appear in the other): the strings compare according to
their number of characters.
For example, to compare anteaters to ants Python finds the first three
characters (ant) are the same but the fourth characters are different: because e (from anteaters) is less than s (rom ants) Python determines
anteaters is less than ants. Similarly, to compare anteater to ant
Python finds that all the characters in ant are in anteater): because
anteater has 8 characters and ant has 3 characters, Python determines
anteater is greater than ant. Finally, notice that to compare ant to
ant Python finds that all the characters in ant are in ant: because both
ant strings have 3, Python determines ant is equal to ant.
The functions builtins.min and builtins.max both take one or more arguments each of their headers contains (*args) so long as they are types
on which Python can perform ordered comparisions. They return a reference
to the minimum/maximum argument respectively: e.g., min(1,-2,4) returns a
result of -2 and min(aunteater,aunt,ant) returns a result of ant. If
any two arguments cannot be compared, Python raises a TypeError exception,
including a comment about unorderable types: Typing >>> min(1,a) in
the Python interpreter displays TypeError: unorderable types: int() <
str() in the console.
Relations on MixedTypes: To perform any relational operator on two
operands that do not have the same type, the lower type operand is promoted
to the type of the higher type operand (as for arithmetic operators): so, in 3
< 5.0 the integer value 3 is promoted to the floatingpoint value 3.0, which is
then satisfying one of the prototypes for < compared to 5.0, which returns
the result True. Note that there is no implicit conversion to or from the string
type.
Inclusion: The inclusion operators (in, and not in) work on any sequence
type in Python. They are opposites: when one returns True the other returns
False. For now, the only sequence type we know is str (which are sequences of
characters), so that is how we will explore these operators. We will generalize
these operators to other sequence types when we learn them.
The in operator determines if all the characters in its left operand appear
in the same order, adjacent, in it right operand. In the simplest case, the left
operand has just one character. So, assuming s is bound to a str object, a in
s returns whether or not the character a appears anywhere in s. Likewise, acg
in s returns whether or not the character sequence cgt appears anywhere in
s: it returns True if s is bound to acgtta, but returns False if s is bound
to attgca.
87
5.2.5
Logical Operators
b
False
True
False
True
a and b
False
False
False
True
a or b
False
True
True
True
not b
True
False
a != b (exclusive or)
False
True
True
False
These rows would duplicate the top two rows, where b has the same values.
Programmers must memorize these truthtables to be able to analyze complicated expressions that use logical operators. Here are two short-cuts. The
result returned by the and operator is True only when both of its operands are
True; more intuitively, if I say to my son, You may play if your homework is
done and your room is clean the only time he can play is is both are True.
The result returned by the or operator is False only when both of its operands
are False; more intuitively, if I say to my son, You may play if your homework
is done or your room is clean he cannot play if both are False, but can play
either or both are True: this is called inclusive or. The exclusive or is True
if one operands is True and the other operands is False: there is no exclusive or
88
operator, but != on booleans has the same semantics (shown in the last column
of the table above).
In fact, the semantics of these these operators are a bit more complicated,
because we can apply them to operands of any types, not just booleans. Recall
the semantics of the bool conversion function discussed in Section 5.2.2.
When evaluating the and operator, Python first evaluates its left operand: if
calling the bool conversion function with this value is False, the result returned
is the value of the left operand; otherwise, the result returned is the value of the
right operand. Notice that for boolean left and right operands, these semantics
match the semantics of the table shown above; check all four examples.
When evaluating the or operator, Python first evaluates its left operand: if
calling the bool conversion function with this value is True, the result returned
is the value of the left operand; otherwise, the result returned is the value of
the right operand. Again, for boolean left and right operands, these semantics
match the semantics of the table shown above; check all four examples.
Experiment with the Python interpreter to explore these logical operators for
both boolean and nonboolean operands.
5.2.6
Bitwise Operators
Bitwise operations (& | ~ ^ << >>) compute their results on the individual
bits in the binary representations of their integer operands. We can illustrate
all four possible combinations of left and right bits using the operands 3 (the
binary literal 0b0011) and 3 (the binary literal 0b0101).
Bitwise
and
inclusive or
not
exclusive or
shift left
shift right
Prototype
& (int,int) -> int
| (int,int) -> int
~ (int) -> int
^ (int,int) -> int
<< (int,int) -> int
>> (int,int) -> int
The last two operators shift the left binary operand either left or right by the
number of bits specified by the right operand. For shifting left, 0s are added
on the right; for shifting right, bits are removed from the right. Shifting positive numbers left/right is like multiplying/dividing by 2; this is similar to how
decimal numbers are shifted left/right when they are multiplied/divided by
10. Because of the way negative binary numbers are typically stored (twos
complement), shifting negative numbers right does not work as expected: e.g.,
-1>>2 returns a result of -1!
Note that the function builtins.bin(x : int) -> str returns a result
that is the binary string equivalent of x: e.g., bin(5) returns a result of 0b101.
So bin(3 ^
5 ) returns the result 0b110. For any name x bound to an integer
int(bin(x),base=0) returns the same result as x; where base=0 means to
interpret the first argument as string specifying a integer literal (which might
start with 0 and a base indicator.
In most simple programs we will not use bitwise operators. But, in some
89
5.2.7
Syntax
Semantics
Returns a onecharacter string at index int if int len(s); otherwise
index
s[int1 ] -> str
Python raises the IndexError exception
Returns a substring with those characters in s at indexes i such that int1
i < int2 ; if int1 is omitted/>len(s), its value is 0/len(s); if int2 is
slice
s[int1 :int2 ] -> str
omitted/>len(s), its value is len(s); negative values for either int specify
an index that is relative to the end of the string, and equivalent to len(s)
+ int: e.g., -1 for an int is equivalent to len(s) - 1
Returns a result similar to slice, but with the first character from index
int1 , the second from index int1 +int3 , the third from index int1 +2*int3 ,
slice/step s[int1 :int2 :int3 ] -> str ... until the index reaches or exceeds int2 ; for negative step values, if
int1 is omitted/len(s) its value is len(s) - 1 and if int2 is omitted its
value is 0; if int3 is omitted, its value is 1, and if it is 0 Python raises the
ValueError exception
To understand these semantics better, lets explore some examples of each form.
When analyzing sequence operators, it is useful to illustrate the strings they
operate on as follows (here for the string acgtta):
string
index
character
acgtta
0
1
a c
2
g
3
t
4
t
5
a
Now, here are the examples that illustrate most of the interesting semantics
of these operators using string s. Unfortunately, it is confusing when the first
character in this string is at index 0 and last character is at index len(s)-1;
programmers must learn to overcome this confusion. Experiment in the Python
interpreter with other strings and integer operands.
Operation
s[0]
s[1]
s[-1]
s[6]
s[0:6]
s[:]
s[1:3]
s[-4:10]
s[::2]
s[1:5:2]
s[::-2]
int1
0
1
5 (6-1)
6
0
0
1
2 (6-4)
0
1
5
int2
NA
NA
NA
NA
6
6
3
6 (len(s))
6
5
0
int3
NA
NA
NA
NA
1
1
1
1
2
2
-2
used
0
1
5
6
0, 1,
0, 1,
1, 2
3, 4,
0, 2,
1, 3
5, 3,
indexes
2, 3, 4, 5
2, 3, 4, 5
5
4
1
90
Result
a
c
a
NA
acgtta
acgtta
cg
gtta
agt
ct
atc
Comments
First character
Second character
Last character
raises IndexError exception
All characters
All characters
Second and third characters
Fourth from last to the end
Every other character forward from index 0
2 characters, every other forward from index 1
Every other character backward from last index
problems: standard operators: no conversion; e.g., 1 + a TypeError: unsupported operand type(s) for +: int and str boolean mixed /2**N bool(False):
strange; see eval 0*4 is 0; what string when replicated 4 ties is the same string
11 5 vs str(11) str(5), how does compare
5.3
Expressions
Structure Evaluation
5.3.1
Oval Diagrams
5.3.2
Operator Precedence
Dictionary/Set + their Comprehensions [] List + its Comprehension () Tuple
+ its Comprehension x.attr Attribute reference x(...) Call: function, method
x[i:j:k] Slicing x[i] Indexing x**y Power (exponentiation) x Bitwise not -x,+x
Negation, Identity * + - Add/catenation, subtract/set-difference xy xy
5.3.3
91
Common Mistakes
5.4
5.5
eval(False)
5.6