Python Language
4. Loops, Conditional Branching, and Operations on Sequences

4.1 More on Looping
4.2 Conditional Branching
4.3 More on Sequence Types
4.4 More on scipy.constants Module

Objectives

4.1 More on Looping

The scripts discussed here are available in the directory Applications_Library/GettingStarted/python/py_basics. The code examples that must be entered at the interactive prompt are also available in this Sentaurus Workbench project.

Click to view the primary file python_pyt.py.

Section 2.6 Introduction to Looping introduced the concept of looping.

This section introduces the in operator, the range object, the enumerate function, and iterating over two sequences.

4.1.1 Membership Testing

The for statement uses the Boolean in operator. Both the in and not in Boolean operators test for membership in a sequence, that is, whether the value is an element of the sequence (see The Python Language Reference: Membership test operations). For example:

>>> numbers = [11, 22, 33, 43]
>>> 22 in numbers
True
>>> 1 not in numbers
True
s

4.1.2 Range Object

The built-in range function has the syntax: range(start, stop, end)

It takes the integer values start, stop, and step as arguments and returns a series of integers in arithmetic progression. For a positive step, the progression is given by:
r[i] = start + i * step < stop, where i0. The step should not be zero. For example:

>>> range(1, 6, 2)
range(1, 6, 2)
The range function creates objects of type range:
>>> type(range(1, 6, 2))
<class 'range'>

These objects can be used anywhere a sequence of integers is used. For example, they can be used in for loops:

>>> for i in range(1, 6, 2):
...     print(i)
...
1
3
5

The output shows that the range function generated the arithmetic progression of integers 1, 3, 5. If start < stop and step is positive, then an increasing progression is generated. The given value of stop is never part of the progression.

You can use the list constructor to create a list of integers in arithmetic progression from the range function. This is useful for checking the progression generated by the range function:

>>> integers = list(range(1, 6, 2))
>>> integers
[1, 3, 5]

You can generate a decreasing sequence of integers by specifying start > stop and a negative step:

>>> list(range(6, 1, -2))
[6, 4, 2]

As discussed in The Python Standard Library: Ranges, start and step are optional and have the default values of 0 and 1, respectively. If only one argument is passed to the range function, then that argument is assumed to be stop, and the default values of start = 0 and step = 1 are used. If two arguments are passed, then the default value of step = 1 is used. For example, both range(0, 4) and range(4) produce the integers 0, 1, 2, 3:

>>> list(range(0, 4))  # start=0, stop=4, step=1 (default)
[0, 1, 2, 3]
>>> list(range(4))     # start=0 (default), stop=4, step=1 (default)
[0, 1, 2, 3]

You can use the range function and for loops to convert the unit of a progression of temperatures 0, 20, 40, 60, 80 from degree Fahrenheit to degree Celsius:

# Script: tempf2c_for.py 
print(f'Fahren Celsius')
for fahren in range(0, 100, 20):
    celsius = (5 / 9) * (fahren - 32)
    print(f'{fahren:6.0f} {celsius:7.1f}') 
> gpythonsh tempf2c_for.py    
Fahren Celsius
     0   -17.8
    20    -6.7
    40     4.4
    60    15.6
    80    26.7

Here, format specifiers are used to print values in tabular format, so that the values in each column are right justified (see Section 2.4.3.1 Formatted Strings (f-Strings) and Format Specification).

You can iterate over the indices of a sequence by combining the range and len functions:

# Script: for_rangelen.py 
greeting = 'Hello'
for i in range(len(greeting)):
    print(f'Element {i} is {greeting[i]}') 
> gpythonsh for_rangelen.py  
Element 0 is H
Element 1 is e
Element 2 is l
Element 3 is l
Element 4 is o

Here, you explicitly access the indices as well as the characters in the string greeting. Whenever you want to access both the indices and the elements of a sequence, it is more convenient to use the built-in enumerate function as discussed in Section 4.1.3 enumerate Function.

Often, the name of the variable to which the current element of a sequence is assigned during execution of the for statement is not important. For example, in the following code, the for statement is used to print a string three times and the variable is not accessed in the loop body:

>>> for _ in range(3):
...     print("Hello, World")
Hello, World
Hello, World
Hello, World

In such cases, it is common to use an underscore (_) as the variable name.

4.1.3 enumerate Function

The enumerate function accepts a sequence and returns the tuple (index, element) during each iteration of the for loop:

>>> greeting = 'Hello'
>>> for i_elem in enumerate(greeting):
...     print(i_elem)
...
(0, 'H')
(1, 'e')
(2, 'l')
(3, 'l')
(4, 'o')

You can use tuple unpacking to conveniently access both the indices and the elements:

# Script: for_enumerate.py 
greeting = 'Hello'
for i, char in enumerate(greeting):
    print(f'Element {i} is {char}') 
> gpythonsh for_enumerate.py   
Element 0 is H
Element 1 is e
Element 2 is l
Element 3 is l
Element 4 is o

4.1.4 Iterating Over Two Sequences

You can use the built-in zip function to combine two sequences. When it is used in a for statement, during each iteration, it returns a tuple consisting of elements from the first sequence and the second sequence. The two sequences need not have the same length. For example:

>>> # Iterating over two lists of unequal length
>>> numbers = list(range(1, 7))
>>> chars = 'abcde'
>>> for num_char in zip(numbers, chars):
...     print(num_char)
...
(1, 'a')
(2, 'b')
(3, 'c')
(4, 'd')
(5, 'e')

For sequences of unequal lengths, iterations are performed until the smaller sequence is exhausted.

You can use tuple unpacking to access the individual elements of the two sequences:

# Script: list_zip.py 
numbers = list(range(1, 7))
chars = 'abcde'
for num, char in zip(numbers, chars):
    print(f'The {num} letter of the alphabet is {char}')
> gpythonsh list_zip.py 
The 1 letter of the alphabet is a
The 2 letter of the alphabet is b
The 3 letter of the alphabet is c
The 4 letter of the alphabet is d
The 5 letter of the alphabet is e

4.2 Conditional Branching

Executing a code block only when a particular condition is true is called conditional execution. You can perform conditional execution in Python using the if statement. The condition is defined using Boolean and comparison operators.

Boolean expressions are expressions that evaluate to a Boolean value. They can be created using comparison and Boolean operators.

4.2.1 Comparison Operators

You can compare two values in Python using the comparison operators listed in Table 1 (see The Python Standard Library: Comparisons).

Table 1. Comparison operators.
Operation Operator
Strictly less than <
Less than or equal to <=
Strictly greater than >
Equal ==
Not equal !=

The syntax for using comparison operators is:

operand operator operand

Here, the operands are values. The result of a comparison is a Boolean value. If the operands meet the condition, then the result is True:

>>> 5 == 5  # Test whether 5 is equal to 5
True

Otherwise, the result is False:

>>> 5.5 < 2.0  # Test whether 5.5 is strictly less than 2.0
False

All the comparison operators have the same priority. Their priority is higher than that of Boolean operators.

Comparison operators have a lower priority than that of arithmetic operators. Python first evaluates the expressions on either side of the comparison operator and then performs the comparison:

>>> 2 * 5 < 6 / 2
False

4.2.2 Boolean Operators

Boolean operators operate on Boolean operands and result in Boolean values. Table 2 lists the Python Boolean operators in increasing order of precedence (see The Python Standard Library: Boolean Operations — and, or, not).

Table 2. Boolean operators.
Operation Operator Result
Logical or x or y True if either or both x and y are True
Logical and x and y True if both x and y are True
Logical not not x True if x is False

The syntax for using the not operator is: operand operator

For example:

>>> not True
False
>>> not False
True

The syntax for using the other two Boolean operators is: operand operator operand

For example:

>>> True and False
False
>>> True or False
True

Usually, Boolean operators are used to combine the results of comparison operators to build more complex conditions:

>>> 3 > 2 and 1 > 2
False

The and and or operators are short-circuit operators. Python starts evaluating the individual operands sequentially from left to right. It stops evaluating the right operand as soon as it can determine the answer. Python evaluates:

Consider the following code:

>>> x = 0 
>>> y = 5
>>> x != 0 and y / x 
False

If Python were to evaluate both operands, then evaluating y / x would have resulted in an error because of the division by zero. However, Python does not evaluate the second operand since the result of the first operand is False and it can determine the result (both False and True and False and False result in False).

4.2.3 Conditional Statements

The if statement is used for conditional execution of code blocks. As discussed in Composing Programs: Conditional Statements, the if statement has the following general syntax:

if <condition>:
    code block
elif <condition>:
    code block
else:
    code block

The if statement consists of a series of headers and code blocks. The if clause is required. It is followed by an optional sequence of elif clauses, which is followed by an optional else clause.

As discussed in The Python Language Reference: The if statement, Python executes a conditional statement by evaluating the conditions in order until one evaluates to True. It then executes only that code block. If all conditions are False, then:

The if statement has two simpler forms. The most simple form has only the if clause:

if <condition>:
    code block

There is only one condition to test. Python executes the code block only if the condition is True. For example, the following code determines whether a number is positive:

# Script: if_demo.py
value = 1
if value > 0:
   print('value is positive')

print('End of if-statement')
> gpythonsh if_demo.py
value is positive
End of if-statement

The second form of the if statement does not contain the elif clause:

if <condition>:
    code block
else:
    code block

Python executes the code block of the if clause if the condition is True. Otherwise, it executes the code block of the else clause. For example, the following code determines the sign of a number:

# Script: if_else.py
value = -1
if value > 0:
   print('value is positive')
else:
   print('value is negative or zero')

print('End of if-statement')
> gpythonsh if_else.py
value is negative or zero
End of if-statement

If the code blocks consist of a single statement, then the first two forms of the if statement can be written more concisely:

# Script: if_concise.py
value = 1
if value > 0: print('value is positive')
> gpythonsh if_concise.py
value is negative or zero
value is positive
# Script: if_else_concise.py
value = -1
if value > 0: print('value is positive')
else: print('value is negative or zero')
> gpythonsh if_else_concise.py
value is negative or zero

The most general form of the if statement consists of the if clause, one or more elif clauses, and one else clause. The following code determines whether a number is zero, positive, or negative:

# Script: if_elif_else.py
value = -1
if value == 0:
    print('value is 0')
elif value > 0:
   print('value is positive')
else:
   print('value is negative')

print('End of if-statement')

> gpythonsh if_elif_else.py
value is negative
End of if-statement

Here, Python first evaluates the condition value == 0 in the if clause. Since this condition is False, it evaluates the condition value > 0 in the next clause, which is the elif clause. Even this condition evaluates to False. Finally, Python executes the code block in the else clause.

4.2.4 Chained Comparisons

The Python comparison operators can be chained. For example, x < y < z is equivalent to x < y and y < z.

The following code determines whether a value lies between 2 and 5:

# Script: comparison_chaining.py
value = 4
if 2 < value < 5:
    print('value is between 2 and 5')
else:
    print('value is outside 2 and 5')
> gpythonsh comparison_chaining.py
value is between 2 and 5

4.3 More on Sequence Types

Sequences were introduced in Section 2.2.5 Introduction to Sequence Types, which discussed how to create sequences and some of the operations on them, such as:

This section discusses how to create some special types of sequence as well as additional operations on sequence types.

4.3.1 Creating Special Sequences

Empty sequences have no elements. When working with lists, it is common to first create an empty list and then append elements to the list inside a for statement (see Section 3.1.4.2 Scalar-Oriented Versus Vectorized Code). You can create an empty list, tuple, or string by not including any values inside brackets, parentheses, or quotation marks. For example:

>>> empty_list = []
>>> empty_list, len(empty_list)
([], 0)
>>> empty_str = ''
>>> empty_str, len(empty_str)
('', 0)

You can create singleton sequences (sequences with a single element) by enclosing a single element:

>>> singleton_list = [1]
>>> singleton_list, len(singleton_list)
([1], 1)

You cannot create a singleton tuple by enclosing a single element inside parentheses. Since Python uses parentheses for grouping expressions, using a single element inside parentheses results in a number:

>>> (1)
1

The expression (1) is the same as the number 1, as can be verified using the equality operator:

>>> # Use equality operator to test whether two values are same
>>> (1) == 1 
True

To create a singleton tuple, use a comma after the element:

>>> singleton_tuple = (1, )
>>> type(singleton_tuple)
<class 'tuple'>
>>> singleton_tuple 
(1,)

Strings are not like integers, floats, and Booleans. A string is a sequence. It is an ordered collection of other values, that is, characters. There is no separate character type in Python; a character is simply a string of size one:

>>> greeting = 'Hello, World!'
>>> letter = greeting[0]  # Extract single character
>>> letter, type(letter)
('H', str)

4.3.1.1 Creating Special Arrays

You can create an empty 1D NumPy array by passing the argument 0 to the numpy.empty function:

>>> empty_arr = np.empty(0)
>>> empty_arr
array([], dtype=float64)

You have already seen how to create arrays prefilled with specific values such as 0 or 1 in Section 2.3.3 NumPy Array Operations. NumPy also has functions for creating linearly spaced values (see numpy.linspace) or logarithmically spaced values (see numpy.logspace).

The function call numpy.linspace(start, stop, num) creates an array of num evenly spaced numbers in the interval [start, stop]. By default, the right endpoint is included. This is often useful for generating a domain of values that can be used for evaluating a function using vectorized operations.

In Section 4.1.2 Range Object, you saw how to use a for loop to convert an arithmetic progression of temperature values 0, 20, 40, 60, 80 from degree Fahrenheit to degree Celsius. Since these values are linearly spaced, you can create an array containing these values using numpy.linspace. Here is the the vectorized version of the same code:

# Script: tempf2c_vec.py
import numpy as np

# Set the precision for printing floating-point numbers in NumPy objects
np.set_printoptions(precision=4)

fahren = np.linspace(0, 80, 5)
print(f'{fahren= }')
celsius = (5 / 9) * (fahren - 32)
print(f'{celsius= }')
> gpythonsh tempf2c_vec.py
fahren= array([ 0., 20., 40., 60., 80.])
celsius= array([-17.7778,  -6.6667,   4.4444,  15.5556,  26.6667])

Here, the numpy.set_printoptions function is used to set the precision to 4 for printing floating-point numbers in NumPy objects.

You can use the zip function to print the output of the above program in tabular format (see Section 4.1.4 Iterating Over Two Sequences):

# Script: tempf2c_vec_tab.py
import numpy as np

fahren = np.linspace(0, 80, 5)
celsius = (5 / 9) * (fahren - 32)

# Print temperatures in tabular format
print(f'Fahren Celsius')
for fahren, celsius in zip(fahren, celsius):
    print(f'{fahren:6.0f} {celsius:7.1f}')
> gpythonsh tempf2c_vec_tab.py
Fahren Celsius
     0   -17.8
    20    -6.7
    40     4.4
    60    15.6
    80    26.7

The function call numpy.logspace(start, stop, num) creates an array of num logarithmically spaced numbers betweem decades \( 10^{start} \) and \( 10^{stop} \).

>>> freq = np.logspace(1, 5, 5)
>>> freq
array([1.e+01, 1.e+02, 1.e+03, 1.e+04, 1.e+05])

The numpy.arange function is roughly equivalent to the Python range function. Instead of returning a range object, it returns a 1D NumPy array. Similar to the range function, it takes the integer values start, stop, and step as arguments. Both start and step are optional and have default values, and the given value of stop is never part of the array. Like the range function, it can generate an array of integers:

>>> integer_arr = np.arange(1, 5)  # Creates an array of ints
>>> integer_arr
array([1, 2, 3, 4])

However, unlike the range function, the numpy.arange function can also generate an array of floating-point numbers if you set at least one of its arguments to a floating-point value:

>>> float_arr = np.arange(1, 5.0)  # Creates an array of floats
>>> float_arr
array([1., 2., 3., 4.])

Since arrays are homogeneous, all numbers in an array are of the same data type. You can check the data type of an array by using its dtype data attribute:

>>> integer_arr.dtype, float_arr.dtype
(dtype('int64'), dtype('float64'))

If you pass a list containing integers and floats to the numpy.array function, then the integers are converted to floats:

>>> numbers = np.array([1, 2.0, 3, 4])
>>> numbers, numbers.dtype
(array([1., 2., 3., 4.]), dtype('float64'))

Similarly, passing a list of integers, floats, and complex numbers converts the integers and floats to complex numbers:

>>> numbers = np.array([1, 2.0, 3+4j, 4])
>>> numbers, numbers.dtype
(array([1.+0.j, 2.+0.j, 3.+4.j, 4.+0.j]), dtype('complex128'))

When creating arrays using the np.array function, you can explicitly specify the data type by using the dtype argument:

>>> numbers = np.array([0, 1, 2], dtype='float64')  # Creates an array of floats
>>> numbers, numbers.dtype
(array([0., 1., 2.]), dtype('float64'))

4.3.2 Slicing

As discussed in Section 2.2.5.4 Indexing Sequences, sequence indexing is used to extract individual elements of a sequence:

>>> # Indexing
>>> [10, 20, 30, 40][0]  # first element or element in position 0
10

Sequence slicing is used to extract a portion of the sequence. For example, it can be used to create sublists or subarrays. The syntax is:

<sequence>[start:stop:step]

Here, start and stop are indices. Slicing returns a sequence that includes the element with index start, but the element with index stop is excluded. This behavior of slicing is similar to that of the range function. You can optionally specify a step size using step. The default values of these options are:

For example:

>>> import numpy as np
>>> my_lst = [0, 10, 20, 30, 40]
>>> my_arr = np.array(my_lst)  

>>> # Every other element from position 1 (included) to 4 (excluded)
>>> my_lst[0:4:2]  # Slicing a list  
[0, 20]

>>> # Every other element from position 1 (included) to 4 (excluded)
>>> my_arr[0:4:2]  # Slicing an array
array([ 0, 20])

>>> # Slicing using default value of step = 1
>>> my_arr[1:4]  # elements from position 1 (included) to 4 (excluded)
array([10, 20, 30])

If the start index is omitted, then Python uses the default value of 0:

>>> # Omitting start 
>>> my_arr[:4]  # elements from position 0 (included) to 4 (excluded)
array([ 0, 10, 20, 30])

Omitting the stop index creates a slice starting with the element at index start and includes all the remaining elements from the sequence:

>>> # Omitting stop 
>>> my_arr[2:]  # elements from position 1 (included) to end of list
array([20, 30, 40])

Omitting both start and stop returns a copy of the sequence:

>>> # Create copy of list
>>> my_lst[:]  # elements from position 0 (included) to end of list
[0, 10, 20, 30, 40]

If the step value is negative, then the defaults for start and end are interchanged. A negative step value can be used to reverse a sequence:

>>> my_arr[::-1]  # Reverse array
array([40, 30, 20, 10,  0])

Slicing always returns a sequence, even when the slice results in a single element:

>>> my_lst[2:3]  # elements from position 1 (included) to 3 (excluded)
[20]

>>> my_arr[2:3]  # elements from position 1 (included) to 3 (excluded)
array([20])

A slice of a list is a new list; whereas, a slice of a NumPy array is a view of the original array. A view of an array is another way of viewing the elements of an array. Slicing a list copies the data from the original list to the new list; whereas, slicing a NumPy array does not copy the data. It returns the elements stored at the memory location of the original array. For example:

>>> my_arr = np.array([0, 10, 20, 30, 40])  
>>> # Create array slice and assign to my_arr_view
>>> my_arr_view = my_arr[:3]
array([ 0, 10, 20])
>>> my_arr_view[0] = 10  # Update my_arr_view
>>> my_arr               # my_arr is also updated
array([10, 10, 20, 30, 40])

As discussed in Section 2.5.5.4 Copying Objects, you must use the numpy.ndarray.copy() method to copy arrays. You can also use this method to create a new 1D array containing a copy of a subarray:

>>> my_arr = np.array([0, 10, 20, 30, 40])  
>>> # Create new array containing copy of subarray
>>> my_arr_copy = np.copy(my_arr[:3]) 
>>> my_arr_copy
array([ 0, 10, 20])
>>> my_arr_copy[0] = 10   # Update my_arr_copy
>>> my_arr                # my_arr does not change
array([0, 10, 20, 30, 40])

4.3.3 Working With NumPy Arrays

It is common to compute a quantity as a function of values stored in arrays and then to store the computed values in a new array (here, called the results array). For example, consider the problem of numerically computing the derivative of y-values with respect to x-values using the forward difference approximation:

\[ {Δy} / {Δx} = {y(x_{i} + Δx) - y(x_{i})} / {Δx} \]

The x- and y-values are used to compute the derivative, which is then stored in the results array. You can compute the derivative using the following approaches:

4.3.3.1 numpy.append Function

The first approach utilizes two NumPy functions numpy.empty (see Section 4.3.1.1 Creating Special Arrays) and numpy.append. The numpy.append function appends a value to the end of an array.

You first create an empty array dydx that will be used to store the computed derivatives. Next, you use a for loop to repeatedly compute the derivative and append it to the dydx array. As a result, the variable dydx is reassigned repeatedly:

# Script: diff_append.py
import numpy as np

x = np.array([1.0, 2.0, 3.0, 4.0])
y = 2 * x

# Create empty derivatives array
dydx = np.empty(0)    # memory allocation  
print(f'{id(dydx)= }')
for i in range(3):
    der = (y[i+1] - y[i]) / (x[i+1] - x[i])
    # Compute derivative and append to derivatives array
    dydx = np.append(dydx, der)  # memory allocation 
    print(f'{id(dydx)= }')

print(f'{dydx= }')    

> gpythonsh diff_append.py
id(dydx)= 46912901690832
id(dydx)= 46912901691120
id(dydx)= 46912901690928
id(dydx)= 46912901690832
dydx= array([2., 2., 2.])

It is common to use this approach when working with lists (see Section 4.3.4 List Methods) and also with NumPy arrays whenever the length or size of the results array is unknown. However, this is not the recommended approach because it is slower compared to the array preallocation approach.

NumPy arrays are stored in contiguous blocks of memory, that is, in consecutive memory locations. Whenever the size of an array needs to be increased, for example, as a result of reassignment, NumPy creates a new array by allocating a contiguous block of memory at a new location, copies the data from the old array to the new array, and deallocates the old array. This is further verified by printing the memory address of the dydx array in the script diff_append.py. The output shows that NumPy creates a new array during each iteration.

This approach is slow because NumPy is repeatedly extending the array and the variable dydx is being reassigned to the new array.

4.3.3.2 Array Preallocation

If the size of the results array is known in advance, then NumPy can be used to allocate the required memory before filling it up with the computed values. This approach of working with NumPy arrays is faster compared to the previous approach. Reserving or allocating memory before it is actually needed is called preallocation. Preallocation can be performed by using the special NumPy function numpy.zeros (discussed in Section 2.3.3 NumPy Array Operations). For example, in the following code, you preallocate the results array dydx by creating an array with the expected size and filled with zeros. The array is later filled with the computed derivatives during each iteration of the for loop:

# Script: diff_preallocate.py
import numpy as np

x = np.array([1.0, 2.0, 3.0, 4.0])
y = 2 * x

# Preallocate dydx: Create derivative array filled with zeros
dydx = np.zeros(len(x)-1)  # preallocate memory
print(f'{id(dydx)= }')
for i in range(3):
    # Compute derivative and update derivative array
    dydx[i] = (y[i+1] - y[i]) / (x[i+1] - x[i])  # no memory allocation
    print(f'{id(dydx)= }')
    
print(f'{dydx= }')     
> gpythonsh diff_preallocate.py
id(dydx)= 46913519231792
id(dydx)= 46913519231792
id(dydx)= 46913519231792
id(dydx)= 46913519231792
dydx= array([2., 2., 2.])

The output shows that, since the memory address of dydx does not change during each iteration, NumPy creates this array only once.

4.3.3.3 Vectorized Code

The first two approaches use a for loop to compute the derivative. Wherever possible, it is recommended to create a vectorized version of the code (see Section 3.1.4.2 Scalar-Oriented Versus Vectorized Code for the advantages) or to use a vectorized function already available in NumPy. For example, here is the vectorized version of the code that uses the numpy.diff function to compute the derivative using the forward difference approximation:

# Script: diff_vectorize.py
import numpy as np

x = np.array([1.0, 2.0, 3.0, 4.0])
y = 2 * x

dydx = np.diff(y)/np.diff(x)   
print(f'{dydx= }') 
> gpythonsh diff_vectorize.py
dydx= array([2., 2., 2.])

4.3.4 List Methods

Python has several methods for processing lists. Since lists are mutable, most methods for processing lists also modify the list argument. As a result, they change the state of the object and, therefore, are called mutators or mutator methods. The list methods append(), sort(), insert(), pop(), and extend() are mutators (see The Python Tutorial: More on Lists and The Python Standard Library: Lists). These methods return the value None.

The list.append() method adds an element to the end of a list. You have already seen an example of using this method in Section 3.1.4.2 Scalar-Oriented Versus Vectorized Code.

The following example computes the absolute value of a list of numbers using the append() method:

# Script: list_abs.py
numbers = [-4, 2, 6, -8.9]
abs_numbers = []

for num in numbers:
    abs_numbers.append(abs(num))
    
print(f'{abs_numbers= }') 
> gpythonsh list_abs.py
abs_numbers= [4, 2, 6, 8.9]

Sorting is another common operation on lists and can be done by using either the list.sort() method or the built-in sorted function.

The list.sort() method is a mutator method and modifies the list to be sorted:

# Script: list_sort.py
numbers = [-4, 2, 6, -8.9]
print(f'Before sorting: {numbers= }')
numbers.sort()  # Mutates the numbers list
print(f'After sorting : {numbers= }')

> gpythonsh list_sort.py
Before sorting: numbers= [-4, 2, 6, -8.9]
After sorting : numbers= [-8.9, -4, 2, 6]

Unlike the list.sort() method, the sorted function returns a new sorted list:

# Script: list_sorted.py
numbers = [-4, 2, 6, -8.9]
print(f'{numbers=        }')
# Assign list returned by sorted to numbers_sorted 
numbers_sorted = sorted(numbers)  
print(f'{numbers_sorted= }')

> gpythonsh list_sorted.py
numbers=        [-4, 2, 6, -8.9]
numbers_sorted= [-8.9, -4, 2, 6]

4.3.5 String Methods

Python has several methods for processing strings. Python has string methods for creating transformed strings, finding patterns in a string, and splitting and joining strings. For details about these methods, see The Python Standard Library: String Methods.

Since strings are immutable, methods for processing strings return a new string instead of modifying the string argument. For example, the str.upper() method takes a string and returns a new string with all uppercase letters:

>>> 'Hello World'.upper()
'HELLO WORLD'
>>> greeting = 'Hello World' 
>>> print(f'Before call to upper: {greeting= }')
>>> greeting.upper()
>>> print(f'After call to upper : {greeting= }')
Before call to upper: greeting= 'Hello World'
After call to upper : greeting= 'Hello World'

This output shows that calling the str.upper() method on the variable greeting does not change its value. To change the value of the variable greeting, you must assign the value returned by str.upper back to greeting:

# Script: str_upper.py
greeting = 'Hello World'
print(f'Before call to upper: {greeting= }')
greeting = greeting.upper()
print(f'After call to upper : {greeting= }')

> gpythonsh str_upper.py
Before call to upper: greeting= 'Hello World'
After call to upper : greeting= 'HELLO WORLD'

You can use the Boolean operator in to determine whether a string contains a substring (see Section 4.1.1 Membership Testing):

>>> greeting = 'Hello World' 
>>> 'ello' in greeting
True
>>> 'hell' in greeting
False

4.4 More on scipy.constants Module

In addition to the constants discussed in Section 3.1.4 Physical Constants: scipy.constants Module, the scipy.constants module contains information (value, unit, and precision) on many more physical constants recommended by CODATA. This information is available in the form of a dictionary of physical constants called physical_constants, whose format is:

scipy.constants.physical_constants[key] = (value, unit, uncertainty)

You can access this information for each constant by using a unique key for the physical constant.

You can either look up the key at Constants (scipy.constants) – Constants database, or you can find the key for a particular constant by using the scipy.constants.find function. To search for the key, pass a substring containing part of the name of the physical constant. For example, to find the key corresponding to the speed of light in vacuum, you can use any substring that could be part of the key such as vacuum or even cuu:

>>> import scipy.constants as const 
>>> # Search for the key for speed of light using the string 'cuu'
>>> const.find('cuu')
['speed of light in vacuum', 'vacuum electric permittivity', 
'vacuum mag. permeability']

The function returns a list of all keys containing the string cuu. Now, you can retrieve information about the speed of light using the key 'speed of light in vacuum':

>>> const.physical_constants['speed of light in vacuum'] 
(299792458.0, 'm s^-1', 0.0)

To retrieve the value, unit, and uncertainty individually, you can use either indexing or the functions scipy.constants.value, scipy.constants.unit, and scipy.constants.precision, respectively:

>>> const.value('speed of light in vacuum')
299792458.0

>>> const.unit('speed of light in vacuum')
'm s^-1'

4.4.1 Miscellaneous Functions

The scipy.constants module also contains the following functions:

main menu    |   module menu    |   << previous section    |   next section >>