python: trap Listing 2

Posted by corillo181 on Wed, 09 Mar 2022 11:09:40 +0100

Although Python does not have C/C + + pointers and various data type conversions, it does not mean that it does not have a smooth path. For beginners, when they lament the simplicity and power of python, they may fall into a trap accidentally. In order to warn the latecomers, various traps of Python are summarized to achieve the effect of "covering the front car and learning from the rear".

Trap 1: the default parameter of the function is initialized only once


For the default parameters of Python functions, it is executed only once when defined. For immutable data types, such as string type, there may be no impact. But for list, dictionary and instance objects of most classes, the difference is very big. As follows:

def func1(a, b=[]):
    b.append(a)
    print b
 
func1(1)
func1(2)
func1(3)
 
class A(object):
    def __init__(self):
        print("Init A")
        
def func2(a, b=A()):
    print(a)
func2(10)
func2(20)
func2(30)


 Output result:

[1]
[1, 2]
[1, 2, 3]
Init A
10
20
30

Trap 2: the default parameter expression of the function evaluates within the defined range


The default parameter expression of Python function is evaluated at the function definition point and within its definition range, rather than when the function is called. The code is as follows:

index = 100
def func(a = index):
    print("a = %d"%(a))
 
index = 200
func()

Output result:

a = 100

Trap 3: do not dynamically modify the size of the loop set in the loop


In the Python loop body, if the size of the loop collection is dynamically modified (such as adding or deleting elements of lists and dictionaries), unpredictable results will be caused. As shown below

>>> a = [3, 2, 2, 4]
>>> for i in a:
    if i % 2 == 0:
        a.remove(i)
 
>>> a
[3, 2]

It is generally thought that the output result is [3]. In fact, when deleting the second element "2", the internal iterator indicates the position of 1. After deleting "2", it enters the next cycle, and the iterator increases 1 to indicate 2 (the element in this position has changed to 4 at this time), resulting in the skipping of the processing of the second "2" (the position of "2" is 1 at this time).

In order to avoid the above problems, it is necessary to ensure that the length of the loop set is not dynamically modified in the loop body. You can make a copy, or use the slice of the list. This method is very simple and practical, but it is only limited to the loop of the list.

The code is as follows. From the results, we can see that our expected purpose has been achieved.

>>> a = [3, 2, 2, 4]
>>> for i in a[:]:
    if i % 2 == 0:
        a.remove(i)
        
>>> a
[3]
>>> 

Trap 4: immutable types are not immutable


As we all know, Python's string, tuple and other types are immutable. However, immutability here means that their elements will not change (that is, the element ID in the container is fixed), but the value of the element is variable (provided that the element type is variable). As follows:

>>> a = [1, 2]
>>> b = ["Hello", "World"]
>>> id(a)
19999120
>>> id(b)
19998120
>>> T = (a, b)
>>> T
([1, 2], ['Hello', 'World'])
>>> id(T[0])
19999120
>>> id(T[1])
19998120
>>> 
>>> a.append(3)
>>> b.append("!!!")
>>> T
([1, 2, 3], ['Hello', 'World', '!!!'])
>>> id(T[0])
19999120
>>> id(T[1])
19998120
>>> 

Where t is a tuple and is an immutable type, but elements a and b are lists and are variable. If we change the values of a and b, the values of the elements contained in t change accordingly. The only constant of T is that the elements it contains are the original elements a and b (their ID s are the same).

Trap 5: embedded function complex string construction does not allow spaces


The complex ([real [, imag]) function allows you to pass in a string in the form of "real+imagJ", but it does not allow spaces on both sides of the "+" sign. This is more deceptive. As follows:

>>> complex("1+3j")
(1+3j)
>>> complex("1 + 3j")


 
Traceback (most recent call last):<pre name="code" class="python"><span style="font-family: Arial, Helvetica, sans-serif;">  File "<pyshell#1>", line 1, in <module></span>
complex("1 + 3j")ValueError: complex() arg is a malformed string>>>
Operators other than the not and: Boolean operators have lower precedence than the not and: Boolean operators
Readers familiar with C/C + + and other languages know expressions! a == b is equivalent to (! a) == b. But for Python, not a == b is equivalent to not (a == b). As follows:

>>> (not 1.5) == 1.0
False
>>> not 1.5 == 1.0
True
>>> 

Trap 7: the "multiplicative copy" of sequences (str, unicode, list, tuple, bytearray, buffer, xrange, etc.) is a shallow copy


Python's sequence types support operations like S = subs * n, and the final result of S is the connection of N subs. In particular, it should be noted that if the operand sub is of variable type (such as list and dictionary), if you do not understand that the "multiplication copy" connection operation is a shallow copy, it will cause unpredictable problems and be more difficult to troubleshoot in complex programs. Let's start with an example:

>>> s = [[0], {"name": "csdn"}]
>>> s
[[0], {'name': 'csdn'}]
>>>
>>> t = s * 3
>>> t
[[0], {'name': 'csdn'}, [0], {'name': 'csdn'}, [0], {'name': 'csdn'}]
>>>
>>> s[0].append(1)
>>> s
[[0, 1], {'name': 'csdn'}]
>>> t
[[0, 1], {'name': 'csdn'}, [0, 1], {'name': 'csdn'}, [0, 1], {'name': 'csdn'}]
>>>
>>> s[1]["name"] = "google"
>>> s
[[0, 1], {'name': 'google'}]
>>> t
[[0, 1], {'name': 'google'}, [0, 1], {'name': 'google'}, [0, 1], {'name': 'google'}]
>>>
>>> t[0].remove(1)
>>> t
[[0], {'name': 'google'}, [0], {'name': 'google'}, [0], {'name': 'google'}]
>>> s
[[0], {'name': 'google'}]


From the above example, we can see that for the list T, it only makes a shallow copy of S, and t refers to the nested elements in S (a list and a dictionary). Therefore, the modification of the elements in s or T will cause the changes of the corresponding elements in t or S.

Trap 8: the flush function does not necessarily write the contents to disk immediately


When using Python to operate a file, in order to update the contents to the disk file in real time, you need to use the flush function to empty the cached data and force it to be written to the disk, but the fact often goes against our expectations. The following code does not guarantee that it will be written to disk immediately:

f = open("test.txt", "w")
for i in range(1000):
    f.write("a" * i)
    f.write("\n")
    f.flush()
f.close()

To understand writing to disk, you need to call os. after flush. Fsync() operation. As follows:

import os
f = open("test.txt", "w")
for i in range(1000):
    f.write("a" * i)
    f.write("\n")
    f.flush()
    os.fsync(f.fileno())
f.close()

This "trap" is not unique to Python, but related to the underlying file system. This potential problem is also highlighted in the Python help document when the flush function is introduced.


Trap 9: the small integer pool object of the internal mechanism causes inconsistent object judgment


Python will put the objects with small integers (- 5-256) into a small integer pool object. All integers in this range point to the objects in the object pool; Beyond this range, i.e. greater than 256 or less than - 5, objects are allocated respectively. It may not be easy to describe in language. Let's take the following example:

>>> a = 257
>>> b = 257
>>> a is b
False
>>>
>>> a = 256
>>> b = 256
>>> a is b
True
>>>
>>> a = -5
>>> b = -5
>>> a is b
True
>>> a = -6
>>> b = -6
>>> a is b
False
>>>

 

reference resources:

https://blog.csdn.net/thomashtq/article/details/39087367

 

Topics: Python