Python is a clear and concise language, if you do not understand some of the details, you will fall into the bottomless "pit", below, I will summarize some of the common pit in Python.
List creation and referencing
Creation of Nested Lists
Use the * sign to create a nested list: the
li = [[]] * 3 print(li) # Out: [[], [], []]
With this method, you can get a nested list containing 3 lists, let's add an element to the first list:
li[0].append(1) print(li) # Out: [[1], [1], [1]]
As you can see by the output at first, we only added elements to the first element, and as a result all three lists added an element. This is because [[]]*3 does not create three different lists, but creates three objects pointing to the same list, so when we manipulate the first element, the other two elements will also change the content of the reason. The effect is equivalent to this code below:
li = [] element = [[]] li = element + element + element print(li) # Out: [[], [], []] (1) print(li) # Out: [[1], [1], [1]]
We can print out the memory address of the element to find out:
li = [[]] * 3 print([id(inner_list) for inner_list in li]) # Out: [6830760, 6830760, 6830760]
At this point we can understand why. So how to solve it? It can be like this:
li = [[] for _ in range(3)]
In this way we have created three different list objects
print([id(inner_list) for inner_list in li]) # Out: [6331048, 6331528, 6331488]
References to list elements
Don't use indexing methods to traverse the list, for example:
for i in range(len(tab)): print(tab[i])
A better way to do it is:
for elem in tab: print(elem)
The for statement automatically generates an iterator. If you need to index positions and elements, use the enumerate function:
for i, elem in enumerate(tab): print((i, elem))
Note the use of the == symbol
if (var == True): # if condition holds when var is: True, 1, 1.0, 1L if (var != True): # if condition holds when var is not True and 1 if (var == False): # When var is False or 0 (or 0.0, 0L, 0j) if condition holds if (var == None): # var is None if condition holds if var: # When var non-empty (None or size 0) objects string/list/dictionary/tuple, non-0, etc. if condition holds if not var: # When var empty (None or size 0) object string/list/dictionary/tuple, non-0, etc. if conditions hold if var is True: # if condition holds only if var is True 1 doesn't work either if var is False: # If condition holds only when var is False 0 doesn't work either if var is None: # cap (a poem)var == None concordance
Catching exceptions due to advance checking
Not elegant enough code:
if (file_path): file = open(file_path) else: # do something
A better practice:
try: file = open(file_path) except OSError as e: # do something
It could be more concise inside python 2.6+:
with open(file_path) as file:
The reason why so used, is so write more general, such as file_path to pass you a None on the blind, but also have to judge whether it is None, if not, then have to catch exceptions, judgment, then, the code has to write a lot more.
Class Variable Initialization
Don't initialize class attributes outside of an object's init function, there are two main problems with this
- If the class attribute changes, the initial value changes.
- If you set the mutable object to its default value, you will get the same object shared across instances.
Demonstration of errors (unless you want static variables)
``` class Car(object): color = "red" wheels = [Wheel(), Wheel(), Wheel(), Wheel()] ```
The right thing to do:
``` class Car(object): def __init__(self): = "red" = [Wheel(), Wheel(), Wheel(), Wheel()] ``` **Function Default Arguments** ``` def foo(li=[]): (1) print(li) foo([2]) # Out: [2, 1] foo([3]) # Out: [3, 1] ```
The code behaves as expected, but what if we don't pass parameters?
``` foo() # Out: [1] As expected... foo() # Out: [1, 1] Not as expected... ```
This is because the function parameter type is defined to be confirmed rather than runtime, so li points to the same list object in both function calls, if you want to solve this problem, you can do this:
``` def foo(li=None): if not li: li = [] (1) print(li) foo() # Out: [1] foo() # Out: [1] ```
This solves the above problem, but, with some other objects, such as zero-length strings, the output is not what we want.
``` x = [] foo(li=x) # Out: [1] foo(li="") # Out: [1] foo(li=0) # Out: [1] ```
The most common approach is to check if the parameter is None
``` def foo(li=None): if li is None: li = [] (1) print(li) foo() # Out: [1] ``` **Modify while traversing**
The for statement generates an iterator when traversing an object, which can produce unexpected results if you modify the object during traversal:
alist = [0, 1, 2] for index, value in enumerate(alist): (index) print(alist) # Out: [1]
The second element is not deleted because the iteration traverses the indexes in order. The above loop is traversed twice with the following result:
# Iteration #1 index = 0 alist = [0, 1, 2] (0) # removes '0' # Iteration #2 index = 1 alist = [1, 2] (1) # removes '2' # loop terminates, but alist is not empty: alist = [1]
If you want to avoid this problem, you can create another list
alist = [1,2,3,4,5,6,7] for index, item in reversed(list(enumerate(alist))): # delete all even items if item % 2 == 0: (index) print(alist) # Out: [1, 3, 5, 7]
Integer and String Definitions
Python pre-caches an interval of integers to minimize memory manipulation, but that's exactly what happens, and sometimes very peculiar errors occur, for example:
>>> -8 is (-7 - 1) False >>> -3 is (-2 - 1) True
Another example.
>>> (255 + 1) is (255 + 1) True >>> (256 + 1) is (256 + 1) False
With constant testing, you will find that integers in the interval (-3,256) return True, some even (-8,257). By default, [-5,256] is created and cached when the interpreter first starts, hence the strange behavior above. This is a very common but easily overlooked pitfall. The solution is to always compare values using the equality (==) operator instead of the identity (is) operator.
Python also retains references to commonly used strings and can produce similar obfuscation behavior when comparing the identity (i.e., use) of is strings.
>>> 'python' is 'py' + 'thon' True
The python strings are cached, all python strings are references to that object, and for uncommon strings, the comparison identity will fail even if the strings are equal.
>>> 'this is not a common string' is 'this is not' + ' a common string' False >>> 'this is not a common string' == 'this is not' + ' a common string' True
So, just like the integer rule, always use the equal (==) operator instead of the identity (is) operator to compare string values.
List derivation and variable leakage in loops
Here's an example:
i = 0 a = [i for i in range(3)] print(i) # Outputs 2
List derivation in python2 changed the value of the i variable, and python3 fixes this:
i = 0 a = [i for i in range(3)] print(i) # Outputs 0
Similarly, for loops do not have private scopes for their iteration variables
i = 0 for i in range(3): pass print(i) # Outputs 2
This behavior occurs in Python 2 and Python 3.
To avoid problems with leaking variables, use new variables in list derivations and for loops.
or operator (computing)
for example
if a == 3 or b == 3 or c == 3:
This one is easy, but, look at another one:
if a or b or c == 3: # Wrong
This is because or has a lower priority than ==, so the expression will be evaluated if (a) or (b) or (c == 3):. The correct approach is to explicitly check all conditions:
if a == 3 or b == 3 or c == 3: # Right Way
Alternatively, the built-in function any() can be used instead of the link-or operator:
if any([a == 3, b == 3, c == 3]): # Right
Or, to make it more efficient:
if any(x == 3 for x in (a, b, c)): # Right
Shorter write-up:
if 3 in (a, b, c): # Right
summarize
The above is the entire content of this article, I hope that the content of this article for your study or work has a certain reference learning value, thank you for your support.