Understanding Python's Half-Open Interval Pattern
Understanding Python's Half-Open Interval Pattern
In mathematics, the set of all points on a line between two fixed points A and B can be represented in four ways:
- Open interval: Excludes A and B, denoted as (A,B)
- Closed interval: Includes both A and B, denoted as [A,B]
- Left-closed, right-open: Includes A but excludes B, denoted as [A,B)
- Left-open, right-closed: Excludes A but includes B, denoted as (A,B]
These interval types can be summarized as follows:
| Interval Type | Inclusion of A and B | Notation |
|---|---|---|
| Open interval | Excludes A and B | (A,B) |
| Closed interval | Includes A and B | [A,B] |
| Left-closed, right-open | Includes A, excludes B | [A,B) |
| Left-open, right-closed | Excludes A, includes B | (A,B] |
Python frequently employs the left-closed, right-open pattern in various functions and operations.
1. The range() Function
The range() function creates sequences that include the start value but exclude the end value:
seq = range(10)
print(seq)
for item in seq:
print(item, end=' ')
Output:
range(0, 10)
0 1 2 3 4 5 6 7 8 9
As shown, while 10 is the upper bound, iteration stops at 9, confirming that the end value is not included.
2. Slicing Operations
String slicing follows the same pattern, where the start index is included but the end index is excluded. In the following example, slicing from index 1 to 6 returns elements at positions 1 through 5:
text = '0123456789'
sliced_text = text[1:6]
print(sliced_text)
Output:
12345
3. NumPy Arrays
Many NumPy array generation methods also follow this convention.
The np.arange() function creates arrays with values in the interval [0,x), including 0 but excluding x:
import numpy as np
arr = np.arange(6) # Creates a one-dimensional array
print(type(arr))
print('arr =', arr)
Output:
<class 'numpy.ndarray'>
arr = [0 1 2 3 4 5]
For np.random.randint(), when only the low parameter is provided, it returns random integers in [0,low). When both low and high are specified, it returns integers in [low,high). In the following example, array 'a' contains values from 0 to 5 (excluding 6), while array 'b' contains values from 6 to 9 (excluding 10):
import numpy as np
a = np.random.randint(6, size=(3,7))
print(type(a))
print('a =', a)
b = np.random.randint(6, 10, size=(3,7))
print(type(b))
print('b =', b)
Output:
<class 'numpy.ndarray'>
a = [[5 0 2 2 2 5 4]
[0 3 1 2 1 1 5]
[5 1 3 2 4 3 0]]
<class 'numpy.ndarray'>
b = [[9 9 9 7 7 9 8]
[9 8 6 9 7 7 8]
[9 8 6 9 8 7 7]]
4. Pseudorandom Number Generation
The random.random() function generates pseudorandom numbers in the interval [0.0,1.0), including 0.0 but excluding 1.0:
import random
for i in range(5):
num = random.random()
print(num)
Output:
0.9759375788175713
0.623926154386807
0.5665190757083962
0.5264106376758246
0.5817898105732811
The random.randrange() function also follows this pattern: - With one parameter: randrange(stop) generates random numbers in [0,stop) - With two parameters: randrange(start,stop) generates random numbers in [start,stop) - With three parameters: randrange(start,stop,step) generates random numbers in [start,stop) with a specified step size
import random
print('\nrandrange with one parameter:')
for i in range(5):
num = random.randrange(100)
print(num, end=' ')
print('\nrandrange with two parameters:')
for i in range(5):
num = random.randrange(90, 100)
print(num, end=' ')
print('\nrandrange with three parameters:')
for i in range(5):
num = random.randrange(0, 100, 10)
print(num, end=' ')
Output:
randrange with one parameter:
25 53 72 70 13
randrange with two parameters:
97 97 95 91 98
randrange with three parameters:
40 20 60 10 30
Exceptions to the Pattern
While the left-closed, right-open pattern is common in Python, not all functions follow it. For example, numpy.linspace() may operate on a closed interval. numpy.linspace(1,10,20) creates a one-dimensional array that includes both the start value 1 and the end value 10.
Similarly, random.randint(start,stop) generates random numbers in a closed interval [start,stop]. However, examining the source code reveals that randint() is actually a wrapper around randrange(), calling randrange(start,stop+1) internally to achieve the closed interval behavior.