Python 3 chapter 3: digital date and time

Posted by doozerdc on Sat, 22 Jan 2022 19:44:43 +0100

Chapter 3: digital date and time

It is very simple to perform mathematical operations of integers and floating-point numbers in Python. However, if you need to perform fractions, arrays, or date and time operations, you have to do more work. This chapter focuses on these topics.

3.1 rounding of figures

problem

You want to round a floating-point number to the specified precision.

Solution

For simple rounding operations, use the built-in round(value, ndigits) function. For example:

>>> round(1.23, 1)
1.2
>>> round(1.27, 1)
1.3
>>> round(-1.27, 1)
-1.3
>>> round(1.25361,3)
1.254
>>>

When a value is just in the middle of two boundaries, the round function returns the nearest even number. In other words, the rounding operation of 1.5 or 2.5 will get 2.

The ndigits parameter passed to the round() function can be negative. In this case, the rounding operation will act on the tens, hundreds, thousands, etc. For example:

>>> a = 1627731
>>> round(a, -1)
1627730
>>> round(a, -2)
1627700
>>> round(a, -3)
1628000
>>>

discuss

Don't confuse rounding with formatted output. If your goal is to simply output numbers of a certain width, you don't need to use the round() function. You only need to specify the precision when formatting. For example:

>>> x = 1.23456
>>> format(x, '0.2f')
'1.23'
>>> format(x, '0.3f')
'1.235'
>>> 'value is {:0.3f}'.format(x)
'value is 1.235'
>>>

Also, don't try to "fix" problems that appear to be correct by rounding floating-point values. For example, you might be inclined to do this:

>>> a = 2.1
>>> b = 4.2
>>> c = a + b
>>> c
6.300000000000001
>>> c = round(c, 2) # "Fix" result (???)
>>> c
6.3
>>>

This is not necessary or recommended for most programs that use floating point. Although there will be a little small error in the calculation, these small errors can be understood and tolerated. If you can't allow such small errors (such as involving the financial field), you have to consider using the decimal module, which will be discussed in detail in the next section.

3.2 perform precise floating point operations

problem

You need to perform accurate calculations on floating-point numbers and don't want any small errors.

Solution

A common problem with floating point numbers is that they do not accurately represent decimal numbers. Moreover, even the simplest mathematical operation will produce small errors, such as:

>>> a = 4.2
>>> b = 2.1
>>> a + b
6.300000000000001
>>> (a + b) == 6.3
False
>>>

These errors are characteristic when the underlying CPU and IEEE 754 standard perform arithmetic through their own floating-point units. Since Python's floating-point data type uses the underlying representation to store data, you can't avoid such errors.

If you want to be more precise (and tolerate a certain performance loss), you can use the decimal module:

>>> from decimal import Decimal
>>> a = Decimal('4.2')
>>> b = Decimal('2.1')
>>> a + b
Decimal('6.3')
>>> print(a + b)
6.3
>>> (a + b) == Decimal('6.3')
True

At first glance, the above code seems a little strange. For example, we use strings to represent numbers. However, Decimal objects work like ordinary floating-point numbers (supporting all common mathematical operations). If you print them or use them in string formatting functions, they look like ordinary numbers.

A major feature of the decimal module is that it allows you to control every aspect of the calculation, including the number of digits and rounding operations. To do this, you must first create a local context and change its settings, such as:

>>> from decimal import localcontext
>>> a = Decimal('1.3')
>>> b = Decimal('1.7')
>>> print(a / b)
0.7647058823529411764705882353
>>> with localcontext() as ctx:
...     ctx.prec = 3
...     print(a / b)
...
0.765
>>> with localcontext() as ctx:
...     ctx.prec = 50
...     print(a / b)
...
0.76470588235294117647058823529411764705882352941176
>>>

discuss

The decimal module implements IBM's "general decimal operation specification". Needless to say, there are many configuration options that are not mentioned in this book.

Novices to Python tend to use the decimal module to handle the precise operation of floating-point numbers. However, it is important to understand the purpose of your application first. If you are doing scientific computing or engineering computing, computer graphics, or most operations in science, it is common to use ordinary floating-point types. One reason is that in the real world, it is rarely required to be accurate to the 17 bit accuracy that ordinary floating-point numbers can provide. Therefore, a little error in the calculation process is allowed. The second point is that native floating-point numbers are much faster - sometimes when you perform a lot of operations, speed is also very important.

Even so, you can't completely ignore the error. Mathematicians spend a lot of time studying all kinds of algorithms. Some processing errors will be better than other methods. You should also pay attention to the effects of subtraction deletion and addition of large numbers and decimals. For example:

>>> nums = [1.23e+18, 1, -1.23e+18]
>>> sum(nums) # Notice how 1 disappears
0.0
>>>

The above error can take advantage of math Fsum () provides more accurate computing power to solve:

>>> import math>>> math.fsum(nums)1.0>>>

However, for other algorithms, you should study it carefully and understand its error source.

In general, the decimal module is mainly used in the fields related to finance. In such programs, even a small error is not allowed to spread in the calculation process. Therefore, the decimal module provides a method to solve this kind of problem. When Python deals with databases, it usually encounters decimal objects, and it is usually also when dealing with financial data.

3.3 formatted output of numbers

problem

You need to format the number and output it, and control the number of digits, alignment, thousand separator and other details.

Solution

When formatting and outputting a single number, you can use the built-in format() function, such as:

>>> x = 1234.56789>>> # Two decimal places of accuracy>>> format(x, '0.2f')'1234.57'>>> # Right justified in 10 chars, one-digit accuracy>>> format(x, '>10.1f')'    1234.6'>>> # Left justified>>> format(x, '<10.1f')'1234.6    '>>> # Centered>>> format(x, '^10.1f')'  1234.6  '>>> # Inclusion of thousands separator>>> format(x, ',')'1,234.56789'>>> format(x, '0,.1f')'1,234.6'>>>

If you want to use exponential notation, change f to e or E (depending on the case of the exponential output). For example:

>>> format(x, 'e')'1.234568e+03'>>> format(x, '0.2E')'1.23E+03'>>>

The general form of specifying both width and precision is' [< > ^]? width[,]? (.digits)?' , Where width and digits are integers,? Represents an optional part. The same format is used in the format() method of the string. For example:

>>> 'The value is {:0,.2f}'.format(x)'The value is 1,234.57'>>>

discuss

Digital formatted output is usually relatively simple. The technique demonstrated above applies to both floating-point numbers and decimal digital objects in the decimal module.

When the number of digits is specified, the result value will be returned after rounding according to the same rules as the round() function. For example:

>>> x1234.56789>>> format(x, '0.1f')'1234.6'>>> format(-x, '0.1f')'-1234.6'>>>

Formatting with thousands has nothing to do with localization. If you need to display thousands by region, you need to investigate the functions in the locale module yourself. You can also use the translate() method of the string to exchange thousands of characters. For example:

>>> swap_separators = { ord('.'):',', ord(','):'.' }>>> format(x, ',').translate(swap_separators)'1.234,56789'>>>

You can see in many Python codes that% is used to format numbers, such as:

>>> '%0.2f' % x'1234.57'>>> '%10.1f' % x'    1234.6'>>> '%-10.1f' % x'1234.6    '>>>

This formatting method is also feasible, but it is a little worse than the more advanced format(). For example, when using the% operator to format numbers, some features (adding thousands) are not supported.

3.4 binary integer

problem

You need to convert or output integers expressed in binary, octal or hexadecimal.

Solution

To convert an integer to a binary, octal, or hexadecimal text string, you can use the bin(), oct(), or hex() functions, respectively:

>>> x = 1234>>> bin(x)'0b10011010010'>>> oct(x)'0o2322'>>> hex(x)'0x4d2'>>>

In addition, if you don't want to output the prefix of 0b, 0o or 0x, you can use the format() function. For example:

>>> format(x, 'b')'10011010010'>>> format(x, 'o')'2322'>>> format(x, 'x')'4d2'>>>

Integers are signed, so if you are dealing with negative numbers, the output will contain a negative sign. For example:

>>> x = -1234>>> format(x, 'b')'-10011010010'>>> format(x, 'x')'-4d2'>>>

If you want to generate an unsigned value, you need to increase a value indicating the maximum bit length. For example, to display 32-bit values, you can write as follows:

>>> x = -1234>>> format(2**32 + x, 'b')'11111111111111111111101100101110'>>> format(2**32 + x, 'x')'fffffb2e'>>>

In order to convert an integer string in different base numbers, simply use the int() function with base numbers:

>>> int('4d2', 16)1234>>> int('10011010010', 2)1234>>>

discuss

In most cases, dealing with binary, octal, and hexadecimal integers is simple. Just remember that these conversions are conversions between integers and their corresponding text representations. There is always only one integer type.

Finally, programmers who use octal need to pay attention to one thing. Python's syntax for specifying octal numbers is slightly different from other languages. For example, if you specify octal as follows, there will be a syntax error:

>>> import os>>> os.chmod('script.py', 0755)    File "<stdin>", line 1        os.chmod('script.py', 0755)                            ^SyntaxError: invalid token>>>

Make sure that the octal number is prefixed with 0o, as follows:

>>> os.chmod('script.py', 0o755)>>>

Packing and unpacking from 3.5 bytes to large integers

problem

You have a byte string and want to decompress it into an integer. Or, you need to convert a large integer to a byte string.

Solution

Suppose your program needs to process a 128 bit long 16 element byte string. For example:

data = b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'

To parse bytes into integers, use int.from_bytes() method and specify the byte order as follows:

>>> len(data)16>>> int.from_bytes(data, 'little')69120565665751139577663547927094891008>>> int.from_bytes(data, 'big')94522842520747284487117727783387188>>>

To convert a large integer to a byte string, use int.to_bytes() method, and specify the number of bytes and byte order as follows:

>>> x = 94522842520747284487117727783387188>>> x.to_bytes(16, 'big')b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'>>> x.to_bytes(16, 'little')b'4\x00#\x00\x01\xef\xcd\x00\xab\x90x\x00V4\x12\x00'>>>

discuss

Conversion between large integers and byte strings is not common. However, it sometimes appears in some application fields, such as cryptography or network. For example, IPv6 network addresses are represented by a 128 bit integer. If you want to extract such a value from a data record, you will face such a problem.

As an alternative, you may want to use the struct module described in section 6.11 to decompress bytes. This also works, but using the struct module to decompress is limited to the size of integers. Therefore, you may want to decompress multiple byte strings and merge the results into the final result, as follows:

>>> datab'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'>>> import struct>>> hi, lo = struct.unpack('>QQ', data)>>> (hi << 64) + lo94522842520747284487117727783387188>>>

The byte order rule (little or big) only specifies the low and high order of bytes when building integers. We can easily see from the following carefully constructed representation of hexadecimal numbers:

>>> x = 0x01020304>>> x.to_bytes(4, 'big')b'\x01\x02\x03\x04'>>> x.to_bytes(4, 'little')b'\x04\x03\x02\x01'>>>

If you try to package an integer as a byte string, it is not appropriate and you will get an error. If necessary, you can use int.bit_ The length () method to determine how many bytes are required to store this value.

>>> x = 523 ** 23>>> x335381300113661875107536852714019056160355655333978849017944067>>> x.to_bytes(16, 'little')Traceback (most recent call last):File "<stdin>", line 1, in <module>OverflowError: int too big to convert>>> x.bit_length()208>>> nbytes, rem = divmod(x.bit_length(), 8)>>> if rem:... nbytes += 1...>>>>>> x.to_bytes(nbytes, 'little')b'\x03X\xf1\x82iT\x96\xac\xc7c\x16\xf3\xb9\xcf...\xd0'>>>

3.6 mathematical operation of complex numbers

problem

The latest network authentication scheme code you wrote encountered a problem, and your only solution is to use complex space. Or you just need to use complex numbers to perform some calculations.

Solution

Complex numbers can be specified using the function complex(real, imag) or floating-point numbers with suffix j. For example:

>>> a = complex(2, 4)>>> b = 3 - 5j>>> a(2+4j)>>> b(3-5j)>>>

The corresponding real part, imaginary part and conjugate complex number can be easily obtained. As follows:

>>> a.real2.0>>> a.imag4.0>>> a.conjugate()(2-4j)>>>

In addition, all common mathematical operations can work:

>>> a + b(5-1j)>>> a * b(26+2j)>>> a / b(-0.4117647058823529+0.6470588235294118j)>>> abs(a)4.47213595499958>>>

If you want to perform other complex functions such as sine, cosine or square root, use the cmath module:

>>> import cmath>>> cmath.sin(a)(24.83130584894638-11.356612711218174j)>>> cmath.cos(a)(-11.36423470640106-24.814651485634187j)>>> cmath.exp(a)(-4.829809383269385-5.5920560936409816j)>>>

discuss

Most math related modules in Python can handle complex numbers. For example, if you use numpy, you can easily construct a complex array and perform various operations on the array:

>>> import numpy as np>>> a = np.array([2+3j, 4+5j, 6-7j, 8+9j])>>> aarray([ 2.+3.j, 4.+5.j, 6.-7.j, 8.+9.j])>>> a + 2array([ 4.+3.j, 6.+5.j, 8.-7.j, 10.+9.j])>>> np.sin(a)array([ 9.15449915 -4.16890696j, -56.16227422 -48.50245524j,        -153.20827755-526.47684926j, 4008.42651446-589.49948373j])>>>

Python's standard mathematical functions do not produce complex values, so complex return values are unlikely to appear in your code. For example:

>>> import math>>> math.sqrt(-1)Traceback (most recent call last):    File "<stdin>", line 1, in <module>ValueError: math domain error>>>

If you want to generate a complex number return result, you must use the cmath Module shown, or declare the use of complex types in a library that supports complex numbers. For example:

>>> import cmath>>> cmath.sqrt(-1)1j>>>

3.7 infinity and NaN

problem

You want to create or test positive infinity, negative infinity, or Nan (non numeric) floating-point numbers.

Solution

Python has no special syntax to represent these special floating-point values, but you can use float() to create them. For example:

>>> a = float('inf')>>> b = float('-inf')>>> c = float('nan')>>> ainf>>> b-inf>>> cnan>>>

To test the existence of these values, use math Isinf () and math Isnan() function. For example:

>>> math.isinf(a)True>>> math.isnan(c)True>>>

discuss

For more information on these special floating-point values, refer to the IEEE 754 specification. However, there are some things you should pay special attention to, especially when it comes to comparison and operators.

Infinite numbers propagate when performing mathematical calculations, such as:

>>> a = float('inf')>>> a + 45inf>>> a * 10inf>>> 10 / a0.0>>>

However, some operations are undefined and return a NaN result. For example:

>>> a = float('inf')>>> a/anan>>> b = float('-inf')>>> a + bnan>>>

NaN values are propagated in all operations without exceptions. For example:

>>> c = float('nan')>>> c + 23nan>>> c / 2nan>>> c * 2nan>>> math.sqrt(c)nan>>>

In a particular place of NaN values, the comparison operation between them always returns False. For example:

>>> c = float('nan')>>> d = float('nan')>>> c == dFalse>>> c is dFalse>>>

For this reason, the only safe way to test a NaN is to use math IsNaN (), as demonstrated above.

Sometimes programmers want to change Python's default behavior and throw exceptions in operations that return infinite or NaN results. The fpectl module can be used to change this behavior, but it is not enabled in standard Python builds. It is platform related and targeted at expert programmers. You can refer to the online Python documentation for more details.

3.8 fractional operation

problem

You enter the time machine and suddenly find that you are doing your primary school homework and involve the problem of score calculation. Or you may need to write code to calculate the measurements in your woodworking factory.

Solution

The fractions module can be used to perform mathematical operations containing fractions. For example:

>>> from fractions import Fraction>>> a = Fraction(5, 4)>>> b = Fraction(7, 16)>>> print(a + b)27/16>>> print(a * b)35/64>>> # Getting numerator/denominator>>> c = a * b>>> c.numerator35>>> c.denominator64>>> # Converting to a float>>> float(c)0.546875>>> # Limiting the denominator of a value>>> print(c.limit_denominator(8))4/7>>> # Converting a float to a fraction>>> x = 3.75>>> y = Fraction(*x.as_integer_ratio())>>> yFraction(15, 4)>>>

discuss

In most programs, the calculation of scores generally does not occur, but it is sometimes needed. For example, in a program that allows to accept test units in the form of fractions and perform operations in the form of fractions, the direct use of fractions can reduce the manual conversion to decimal or floating-point numbers.

3.9 large array operation

problem

You need to perform calculations on large data sets, such as arrays or grids.

Solution

The NumPy library can be used for heavyweight operations involving arrays. A major feature of NumPy is that it provides python with an array object, which is more suitable for mathematical operations than the standard Python list. The following is a simple small example to show you the difference between standard list objects and NumPy array objects:

>>> # Python lists>>> x = [1, 2, 3, 4]>>> y = [5, 6, 7, 8]>>> x * 2[1, 2, 3, 4, 1, 2, 3, 4]>>> x + 10Traceback (most recent call last):    File "<stdin>", line 1, in <module>TypeError: can only concatenate list (not "int") to list>>> x + y[1, 2, 3, 4, 5, 6, 7, 8]>>> # Numpy arrays>>> import numpy as np>>> ax = np.array([1, 2, 3, 4])>>> ay = np.array([5, 6, 7, 8])>>> ax * 2array([2, 4, 6, 8])>>> ax + 10array([11, 12, 13, 14])>>> ax + ayarray([ 6, 8, 10, 12])>>> ax * ayarray([ 5, 12, 21, 32])>>>

As you can see, the basic mathematical results of arrays in the two schemes are not the same. In particular, scalar operations in NumPy (such as ax * 2 or ax + 10) act on each element. In addition, when both operands are arrays, the element peer position calculation is performed, and finally a new array is generated.

Performing mathematical operations on all elements of the whole array at the same time can make the function operation on the whole array simple and fast. For example, if you want to calculate the value of a polynomial, you can do this:

>>> def f(x):... return 3*x**2 - 2*x + 7...>>> f(ax)array([ 8, 15, 28, 47])>>>

NumPy also provides a large number of general functions for array operations, which can be used as an alternative to similar functions in the math module. For example:

>>> np.sqrt(ax)array([ 1. , 1.41421356, 1.73205081, 2. ])>>> np.cos(ax)array([ 0.54030231, -0.41614684, -0.9899925 , -0.65364362])>>>

Using these general-purpose functions is much faster than looping through arrays and using functions in the math module. Therefore, if possible, try to choose NumPy's array scheme.

In the underlying implementation, NumPy array uses the mechanism of C or Fortran language to allocate memory. In other words, they are a very large continuous memory area composed of the same type of data. So you can construct an array that is much larger than an ordinary Python list. For example, if you want to construct a floating-point two-dimensional grid of 10000 * 10000, it is easy:

>>> grid = np.zeros(shape=(10000,10000), dtype=float)>>> grid    array([[ 0., 0., 0., ..., 0., 0., 0.],    [ 0., 0., 0., ..., 0., 0., 0.],    [ 0., 0., 0., ..., 0., 0., 0.],    ...,    [ 0., 0., 0., ..., 0., 0., 0.],    [ 0., 0., 0., ..., 0., 0., 0.],    [ 0., 0., 0., ..., 0., 0., 0.]])>>>

All normal operations still work on all elements at the same time:

>>> grid += 10>>> gridarray([[ 10., 10., 10., ..., 10., 10., 10.],    [ 10., 10., 10., ..., 10., 10., 10.],    [ 10., 10., 10., ..., 10., 10., 10.],    ...,    [ 10., 10., 10., ..., 10., 10., 10.],    [ 10., 10., 10., ..., 10., 10., 10.],    [ 10., 10., 10., ..., 10., 10., 10.]])>>> np.sin(grid)array([[-0.54402111, -0.54402111, -0.54402111, ..., -0.54402111,        -0.54402111, -0.54402111],    [-0.54402111, -0.54402111, -0.54402111, ..., -0.54402111,        -0.54402111, -0.54402111],    [-0.54402111, -0.54402111, -0.54402111, ..., -0.54402111,        -0.54402111, -0.54402111],    ...,    [-0.54402111, -0.54402111, -0.54402111, ..., -0.54402111,        -0.54402111, -0.54402111],    [-0.54402111, -0.54402111, -0.54402111, ..., -0.54402111,        -0.54402111, -0.54402111],    [-0.54402111, -0.54402111, -0.54402111, ..., -0.54402111,        -0.54402111, -0.54402111]])>>>

One particular idea about NumPy is that it extends the indexing function of Python lists - especially for multi-dimensional arrays. To illustrate clearly, first construct a simple two-dimensional array and try some experiments:

>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])>>> aarray([[ 1, 2, 3, 4],[ 5, 6, 7, 8],[ 9, 10, 11, 12]])>>> # Select row 1>>> a[1]array([5, 6, 7, 8])>>> # Select column 1>>> a[:,1]array([ 2, 6, 10])>>> # Select a subregion and change it>>> a[1:3, 1:3]array([[ 6, 7],        [10, 11]])>>> a[1:3, 1:3] += 10>>> aarray([[ 1, 2, 3, 4],        [ 5, 16, 17, 8],        [ 9, 20, 21, 12]])>>> # Broadcast a row vector across an operation on all rows>>> a + [100, 101, 102, 103]array([[101, 103, 105, 107],        [105, 117, 119, 111],        [109, 121, 123, 115]])>>> aarray([[ 1, 2, 3, 4],        [ 5, 16, 17, 8],        [ 9, 20, 21, 12]])>>> # Conditional assignment on an array>>> np.where(a < 10, a, 10)array([[ 1, 2, 3, 4],        [ 5, 10, 10, 8],        [ 9, 10, 10, 10]])>>>

discuss

NumPy is the basis of many scientific and engineering libraries in the Python field, and it is also the largest and most complex module widely used. Even so, some simple examples and toy programs can help us accomplish some interesting things at the beginning.

Usually, when importing numpy module, we will use the statement import numpy as np. In this way, you don't have to type numpy in your program again and again. You just need to enter np, which saves a lot of time.

If you want to get more information, of course you have to go to NumPy's official website at: http://www.numpy.org

3.10 matrix and linear algebraic operation

problem

You need to perform matrix and linear algebraic operations, such as matrix multiplication, finding determinants, solving linear equations, etc.

Solution

The NumPy library has a matrix object that can be used to solve this problem.

The matrix is similar to the array object in section 3.9, but follows the calculation rules of linear algebra. The following example shows some basic characteristics of the matrix:

>>> import numpy as np>>> m = np.matrix([[1,-2,3],[0,4,5],[7,8,-9]])>>> mmatrix([[ 1, -2, 3],        [ 0, 4, 5],        [ 7, 8, -9]])>>> # Return transpose>>> m.Tmatrix([[ 1, 0, 7],        [-2, 4, 8],        [ 3, 5, -9]])>>> # Return inverse>>> m.Imatrix([[ 0.33043478, -0.02608696, 0.09565217],        [-0.15217391, 0.13043478, 0.02173913],        [ 0.12173913, 0.09565217, -0.0173913 ]])>>> # Create a vector and multiply>>> v = np.matrix([[2],[3],[4]])>>> vmatrix([[2],        [3],        [4]])>>> m * vmatrix([[ 8],        [32],        [ 2]])>>>

Can be found in numpy Find more operation functions in the linalg sub package, such as:

>>> import numpy.linalg>>> # Determinant>>> numpy.linalg.det(m)-229.99999999999983>>> # Eigenvalues>>> numpy.linalg.eigvals(m)array([-13.11474312, 2.75956154, 6.35518158])>>> # Solve for x in mx = v>>> x = numpy.linalg.solve(m, v)>>> xmatrix([[ 0.96521739],        [ 0.17391304],        [ 0.46086957]])>>> m * xmatrix([[ 2.],        [ 3.],        [ 4.]])>>> vmatrix([[2],        [3],        [4]])>>>

discuss

Obviously, linear algebra is a very big topic, which is beyond the scope of this book. However, NumPy is a good entry point if you need to manipulate arrays and vectors. You can visit NumPy's official website http://www.numpy.org For more information.

3.11 random selection

problem

You want to randomly extract several elements from a sequence, or you want to generate several random numbers.

Solution

The random module has a large number of functions to generate random numbers and randomly select elements. For example, to randomly extract an element from a sequence, you can use random choice() :

>>> import random>>> values = [1, 2, 3, 4, 5, 6]>>> random.choice(values)2>>> random.choice(values)3>>> random.choice(values)1>>> random.choice(values)4>>> random.choice(values)6>>>

In order to extract samples of N different elements for further operations, you can use random sample() :

>>> random.sample(values, 2)[6, 2]>>> random.sample(values, 2)[4, 3]>>> random.sample(values, 3)[4, 3, 1]>>> random.sample(values, 3)[5, 4, 1]>>>

If you just want to disrupt the order of the elements in the sequence, you can use random shuffle() :

>>> random.shuffle(values)>>> values[2, 4, 6, 5, 3, 1]>>> random.shuffle(values)>>> values[3, 5, 2, 1, 6, 4]>>>

To generate random integers, use random randint() :

>>> random.randint(0,10)2>>> random.randint(0,10)5>>> random.randint(0,10)0>>> random.randint(0,10)7>>> random.randint(0,10)10>>> random.randint(0,10)3>>>

To generate floating-point numbers that are evenly distributed from 0 to 1, use random random() :

>>> random.random()0.9406677561675867>>> random.random()0.133129581343897>>> random.random()0.4144991136919316>>>

If you want to get an integer with N random bits (binary), use random getrandbits() :

>>> random.getrandbits(200)335837000776573622800628485064121869519521710558559406913275>>>

discuss

The random module uses the Mersenne Twister algorithm to calculate and generate random numbers. This is a deterministic algorithm, but you can use random The seed() function modifies the initialization seed. For example:

random.seed() # Seed based on system time or os.urandom()random.seed(12345) # Seed based on integer givenrandom.seed(b'bytedata') # Seed based on byte data

In addition to the functions described above, the random module also includes random number generation functions based on uniform distribution, Gaussian distribution and other distributions. For example, random Uniform() calculates a uniformly distributed random number, random Gauss () calculates the normal distribution random number. For other distributions, please refer to the online documentation.

Functions in the random module should not be used in cryptography related programs. If you really need similar functions, you can use the corresponding functions in the ssl module. For example, ssl RAND_ Bytes () can be used to generate a secure sequence of random bytes.

3.12 basic date and time conversion

problem

You need to perform simple time conversion, such as day to second, hour to minute and so on.

Solution

To perform the conversion and calculation of different time units, use the datetime module. For example, to represent a time period, you can create a timedelta instance, as follows:

>>> from datetime import timedelta>>> a = timedelta(days=2, hours=6)>>> b = timedelta(hours=4.5)>>> c = a + b>>> c.days2>>> c.seconds37800>>> c.seconds / 360010.5>>> c.total_seconds() / 360058.5>>>

If you want to represent the specified date and time, first create a datetime instance, and then use standard mathematical operations to manipulate them. For example:

>>> from datetime import datetime>>> a = datetime(2012, 9, 23)>>> print(a + timedelta(days=10))2012-10-03 00:00:00>>>>>> b = datetime(2012, 12, 21)>>> d = b - a>>> d.days89>>> now = datetime.today()>>> print(now)2012-12-21 14:54:43.094063>>> print(now + timedelta(minutes=10))2012-12-21 15:04:43.094063>>>

When calculating, it should be noted that datetime will automatically process leap years. For example:

>>> a = datetime(2012, 3, 1)>>> b = datetime(2012, 2, 28)>>> a - bdatetime.timedelta(2)>>> (a - b).days2>>> c = datetime(2013, 3, 1)>>> d = datetime(2013, 2, 28)>>> (c - d).days1>>>

discuss

For most basic date and time processing problems, the datetime module is sufficient. If you need to perform more complex date operations, such as processing time zone, fuzzy time range, holiday calculation, etc., you can consider using dateutil module

Many similar time calculations can use dateutil Instead, use the relativedelta() function. However, one thing to note is that it fills the gap when dealing with months (and their day gap). See the example most clearly:

>>> a = datetime(2012, 9, 23)>>> a + timedelta(months=1)Traceback (most recent call last):File "<stdin>", line 1, in <module>TypeError: 'months' is an invalid keyword argument for this function>>>>>> from dateutil.relativedelta import relativedelta>>> a + relativedelta(months=+1)datetime.datetime(2012, 10, 23, 0, 0)>>> a + relativedelta(months=+4)datetime.datetime(2013, 1, 23, 0, 0)>>>>>> # Time between two dates>>> b = datetime(2012, 12, 21)>>> d = b - a>>> ddatetime.timedelta(89)>>> d = relativedelta(b, a)>>> drelativedelta(months=+2, days=+28)>>> d.months2>>> d.days28>>>

3.13 calculate the date of the last Friday

problem

You need a general method to calculate the date of one day of the week, such as the date of the last Friday.

Solution

Python's datetime module has tool functions and classes to help you perform such calculations. Here is a general solution to problems like this:

#!/usr/bin/env python# -*-Encoding: UTF-8 - * - "" topic: last Friday desc: "" from datetime, import datetime, timedeltaweekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] def get_ previous_ byday(dayname, start_date=None):    if start_ date is None:        start_ date = datetime. today()    day_ num = start_ date. weekday()    day_ num_ target = weekdays. index(dayname)    days_ ago = (7 + day_num - day_num_target) % 7    if days_ ago == 0:        days_ ago = 7    target_ date = start_ date - timedelta(days=days_ago)    return target_ date

Use the following in the interactive interpreter:

>>> datetime.today() # For referencedatetime.datetime(2012, 8, 28, 22, 4, 30, 263076)>>> get_previous_byday('Monday')datetime.datetime(2012, 8, 27, 22, 3, 57, 29045)>>> get_previous_byday('Tuesday') # Previous week, not todaydatetime.datetime(2012, 8, 21, 22, 4, 12, 629771)>>> get_previous_byday('Friday')datetime.datetime(2012, 8, 24, 22, 5, 9, 911393)>>>

Optional start_ The date parameter can be provided by another datetime instance. For example:

>>> get_previous_byday('Sunday', datetime(2012, 12, 21))datetime.datetime(2012, 12, 16, 0, 0)>>>

discuss

The above algorithm principle is as follows: first map the start date and target date to the position of the week array (Monday index is 0), and then calculate how many days it takes for the target date to reach the start date through modular operation. Then subtract the time difference from the start date to get the result date.

If you want to perform a lot of date calculations like this, you'd better install the third-party package Python dateutil instead. For example, the following is to perform the same calculation using the relativedelta() function in the dateutil module:

>>> from datetime import datetime>>> from dateutil.relativedelta import relativedelta>>> from dateutil.rrule import *>>> d = datetime.now()>>> print(d)2012-12-23 16:31:52.718111>>> # Next Friday>>> print(d + relativedelta(weekday=FR))2012-12-28 16:31:52.718111>>>>>> # Last Friday>>> print(d + relativedelta(weekday=FR(-1)))2012-12-21 16:31:52.718111>>>

3.14 calculate the date range of the current month

problem

Your code needs to cycle each day in the current month to find an efficient way to calculate this date range.

Solution

Loop over such dates and need to construct a list of all dates in advance. You can calculate the start date and end date first, and then use datetime when you step The timedelta object increments the date variable.

The following is a tuple object that accepts any datetime object and returns a tuple composed of the start date of the current month and the start date of the next month.

from datetime import datetime, date, timedeltaimport calendardef get_month_range(start_date=None):    if start_date is None:        start_date = date.today().replace(day=1)    _, days_in_month = calendar.monthrange(start_date.year, start_date.month)    end_date = start_date + timedelta(days=days_in_month)    return (start_date, end_date)

With this, you can easily cycle through the returned date range:

>>> a_day = timedelta(days=1)>>> first_day, last_day = get_month_range()>>> while first_day < last_day:...     print(first_day)...     first_day += a_day...2012-08-012012-08-022012-08-032012-08-042012-08-052012-08-062012-08-072012-08-082012-08-09#... and so on...

discuss

The above code first calculates the date corresponding to the first day of the month. A quick way is to simply set the days property to 1 by using the replace() method of the date or datetime object. One advantage of the replace() method is that it creates an object of the same type as the object you started passing in. Therefore, if the input parameter is a date instance, the result is also a date instance. Similarly, if the input is a datetime instance, you get a datetime instance.

Then, use calendar The montrange () function to find the total number of days in the month. Whenever you want to get calendar information, the calendar module is very useful. The montrange () function returns a tuple containing the week and the number of days in the month.

Once the number of days of the month is known, the end date can be obtained by adding this number of days to the start date. It should be noted that the end date is not included in this date range (in fact, it is the start date of the next month). This is consistent with Python's slice and range operation behavior, and also does not contain an end.

In order to cycle through the date range, standard mathematical and comparison operations are used. For example, you can use the timede lt a instance to increment the date. The less than sign < is used to check whether a date is before the end date.

Ideally, it would be nice to create a function for the date iteration that is the same as the built-in range() function. Fortunately, this goal can be easily achieved using a generator:

def date_range(start, stop, step):    while start < stop:        yield start        start += step

The following is an example of using this generator:

>>> for d in date_range(datetime(2012, 9, 1), datetime(2012,10,1),                        timedelta(hours=6)):...     print(d)...2012-09-01 00:00:002012-09-01 06:00:002012-09-01 12:00:002012-09-01 18:00:002012-09-02 00:00:002012-09-02 06:00:00...>>>

The simplicity of this implementation is also due to the fact that the date and time in Python can be calculated using standard mathematical and comparison operators.

3.15 converting string to date

problem

Your application accepts input in string format, but you want to convert them to datetime objects to perform non string operations on them.

Solution

Using Python's standard module datetime can easily solve this problem. For example:

>>> from datetime import datetime>>> text = '2012-09-20'>>> y = datetime.strptime(text, '%Y-%m-%d')>>> z = datetime.now()>>> diff = z - y>>> diffdatetime.timedelta(3, 77824, 177393)>>>

discuss

datetime. The strptime () method supports many formatting codes, such as% Y for a 4-digit year and% m for a 2-digit month. It is also worth noting that these formatting placeholders can also be used in reverse to output the date as a specified format string.

For example, suppose a datetime object is generated in your code, and you want to format it into a beautiful and easy to read form and put it at the top of the automatically generated letter or report:

>>> zdatetime.datetime(2012, 9, 23, 21, 37, 4, 177393)>>> nice_z = datetime.strftime(z, '%A %B %d, %Y')>>> nice_z'Sunday September 23, 2012'>>>

It should also be noted that the performance of strptime() is much worse than you think, because it is implemented in pure Python and must deal with all system local settings. If you need to parse a large number of dates in the code and already know the exact format of date string, you can implement a set of parsing scheme to obtain better performance. For example, if you already know that the date format is YYYY-MM-DD, you can implement an parsing function as follows:

from datetime import datetimedef parse_ymd(s):    year_s, mon_s, day_s = s.split('-')    return datetime(int(year_s), int(mon_s), int(day_s))

In actual tests, this function is better than datetime Strptime() is more than 7 times faster. If you want to deal with a lot of date related data, you'd better consider this scheme!

3.16 date operation combined with time zone

problem

You have a conference call scheduled for 9:30 a.m. on December 21, 2012 in Chicago. While your friend is in Bangalore, India, what time should he attend the meeting?

Solution

You should use the pytz module for almost all time zone related issues. This package provides Olson time zone database, which is the de facto standard of time zone information, which can be found in many languages and operating systems.

One of the main purposes of the pytz module is to localize simple date objects created by the datetime library. For example, the following is an example of how to represent Chicago time:

>>> from datetime import datetime>>> from pytz import timezone>>> d = datetime(2012, 12, 21, 9, 30, 0)>>> print(d)2012-12-21 09:30:00>>>>>> # Localize the date for Chicago>>> central = timezone('US/Central')>>> loc_d = central.localize(d)>>> print(loc_d)2012-12-21 09:30:00-06:00>>>

Once the date is localized, it can be converted to time in other time zones. In order to get the time corresponding to Bangalore, you can do this:

>>> # Convert to Bangalore time>>> bang_d = loc_d.astimezone(timezone('Asia/Kolkata'))>>> print(bang_d)2012-12-21 21:00:00+05:30>>>

If you plan to perform calculations on localized dates, you need to pay special attention to daylight saving time conversion and other details. For example, in 2013, US standard daylight saving time began at 2:00 a.m. local time on March 13 (at that time, the time skipped one hour forward). If you are performing a local calculation, you will get an error. For example:

>>> d = datetime(2013, 3, 10, 1, 45)>>> loc_d = central.localize(d)>>> print(loc_d)2013-03-10 01:45:00-06:00>>> later = loc_d + timedelta(minutes=30)>>> print(later)2013-03-10 02:15:00-06:00 # WRONG! WRONG!>>>

The result is wrong because it does not consider a one hour jump in local time. To fix this, you can use the normalize() method of the time zone object. For example:

>>> from datetime import timedelta>>> later = central.normalize(loc_d + timedelta(minutes=30))>>> print(later)2013-03-10 03:15:00-05:00>>>

discuss

In order not to confuse you with these things, the usual strategy for dealing with localized dates first converts all dates to UTC time and uses it to perform all intermediate storage and operations. For example:

>>> print(loc_d)2013-03-10 01:45:00-06:00>>> utc_d = loc_d.astimezone(pytz.utc)>>> print(utc_d)2013-03-10 07:45:00+00:00>>>

Once converted to UTC, you don't have to worry about daylight saving time. Therefore, you can safely perform common date calculations as before. When you want to change the output to local time, use the appropriate time zone to convert it. For example:

>>> later_utc = utc_d + timedelta(minutes=30)>>> print(later_utc.astimezone(central))2013-03-10 03:15:00-05:00>>>

When it comes to time zone operation, the problem is how to get the name of the time zone. For example, in this example, how do we know that "Asia/Kolkata" is the time zone name corresponding to India? To find, you can use the ISO 3166 country code as a keyword to refer to the dictionary pytz country_ timezones . For example:

>>> pytz.country_timezones['IN']['Asia/Kolkata']>>>

Note: when you read here, it is possible that the pytz module is no longer recommended because PEP431 provides more advanced time zone support. However, many of the issues discussed here are of reference value (such as the suggestion of using UTC date, etc.).

Topics: Python