↑  Follow + star, learn new Python skills every day

Backstage Reply [ Big Gift Package ] Send you a Python Self-Learning Gift Package

picture


Abstract: Python is an easy-to-learn programming language with concise and clear syntax, and a rich and powerful class library. In daily development, developers are prone to make some low-level mistakes. This article summarizes the 10 most common mistakes developers make.


Python is an easy-to-learn programming language with concise and clear syntax, and a rich and powerful class library. Unlike most other programming languages, which use curly braces, it uses indentation to define blocks of statements.


In normal work, Python developers are prone to make some small mistakes, and these mistakes are easy to avoid. This article summarizes the 10 most common mistakes made by Python developers. Let’s take a look at them. I wonder if you have been shot.



1. Abusing expressions as function parameter defaults


Python allows developers to specify a default value for function parameters. Although this is a feature of the language, it can easily lead to confusion when parameters are variable. For example, the following function definition:

>>> def foo(bar=[]): # bar is optional and defaults to [] if not specified 
... bar.append("baz") # but this line could be problematic, as we'll see.. . .
. return bar

In the above code, once the foo() function is called repeatedly (without specifying a bar parameter), it will always return 'bar', because no parameters are specified, then every time foo() is called, it will be assigned [] . Here's a look at the result of doing so:

>>> foo() 
["baz"]
>>> foo()
["baz", "baz"]
>>> foo()
["baz", "baz", "baz"]

solution:

>>> def foo(bar=None): 
... if bar is None: # or if not bar:
... bar = []
... bar.append("baz")
... return bar
.. .
>>> foo()
["baz"]
>>> foo()
["baz"]
>>> foo()
["baz"]

2. Incorrect use of class variables


Take a look at the following example:

>>> class A(object): 
... x = 1
...
>>> class B(A):
... pass
...
>>> class C(A):
... pass
...
>>> print Ax, Bx, Cx
1 1 1

This makes sense:

>>> Bx = 2 
>>> print Ax, Bx, Cx
1 2 1

do it again:

>>> Ax = 3 
>>> print Ax, Bx, Cx
3 2 3

Just changed Ax, why Cx also changed.


In Python, class variables are all handled internally as dictionaries and follow Method Resolution Order (MRO). In the code above, since the attribute x is not found in class C, it looks for its base class (in the above example only A, although Python supports multiple inheritance). In other words, C has no x attribute of its own and is independent of A, so referencing Cx is actually referencing Ax.


3. Specifying incorrect parameters for exceptions


Suppose the code has the following code:

>>> try: 
... l = ["a", "b"]
... int(l[2])
... except ValueError, IndexError: # To catch both exceptions, right?
... pass
. ..
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
IndexError: list index out of range

The problem here is that the except statement doesn't need this way to specify the list of exceptions. However, in Python 2.x, except Exception,e is usually used to bind the second parameter in the exception so that it can be checked further. Therefore, in the above code, the IndexError exception is not caught by the except statement, and the exception is finally bound to a parameter named IndexError.


The correct way to catch multiple exceptions in one exception statement is to specify the first parameter as a tuple containing all the exceptions that were caught. At the same time, use the as keyword for maximum portability, a syntax supported by both Python 2 and Python 3.

>>> try: 
... l = ["a", "b"]
... int(l[2])
... except (ValueError, IndexError) as e:
... pass
...
>> >

4. Misunderstanding Python Rule Scope


Python's scope resolution is based on LEGB rules, namely Local, Enclosing, Global, and Built-in. In fact, this parsing method also has some mysteries, see the following example:


>>> x = 10 
>>> def foo():
... x += 1
... print x
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment

Many people will be surprised that when they add a parameter statement to the body of a working function, an UnboundLocalError will be thrown in the code that was previously working (click here for a more detailed description).


It is very easy for developers to make this mistake when using lists. Take a look at the following example:

>>> lst = [1, 2, 3] 
>>> def foo1():
... lst.append(5) # This works ok...
...
>>> foo1()
>>> lst
[ 1, 2, 3, 5]

>>> lst = [1, 2, 3]
>>> def foo2():
... lst += [5] # ... but this bombs!
...
>> > foo2()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'lst' referenced before assignment

Why does foo2 fail while foo1 runs fine?


The answer is the same as in the previous example, but with some subtleties. foo1 is not assigned to lst, but foo2 is assigned. lst += [5] is actually lst = lst + [5], trying to assign a value to lst (thus, assuming Python is in local scope). However, the value we're looking to assign to lst is based on lst itself, which is not yet determined.


5. Modify the traversal list


The following code is obviously wrong:

>>> odd = lambda x : bool(x % 2) 
>>> numbers = [n for n in range(10)]
>>> for i in range(len(numbers)):
... if odd(numbers [i]):
... del numbers[i] # BAD: Deleting item from a list while iterating over it
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
IndexError: list index out of range

When traversing, it is a very low-level error to delete the list. Anyone with a little experience will not do it.


Modify the above code to execute correctly:

>>> odd = lambda x : bool(x % 2) 
>>> numbers = [n for n in range(10)]
>>> numbers[:] = [n for n in numbers if not odd(n)] # ahh, the beauty of it all
>>> numbers
[0, 2, 4, 6, 8]

6. How to bind variables in closures


Consider the following example:

>>> def create_multipliers(): 
... return [lambda x : i * x for i in range(5)]
>>> for multiplier in create_multipliers():
... print multiplier(2)
...

The result you expect is:

<code>0 
2
4
6
8</code>

Actually:

<code>8 
8
8
8
8</code>

Isn't it very surprising! This happens mainly because of Python's late binding behavior, the variable is used in the closure at the same time as the inner function is calling it.


solution:

>>> def create_multipliers(): 
... return [lambda x, i=i : i * x for i in range(5)]
...
>>> for multiplier in create_multipliers():
... print multiplier( 2)
...
0
2
4
6
8

7. Create circular module dependencies


Suppose there are two files, a.py and b.py, and import them separately, as follows:


In a.py:

import b 

def f():
return bx

print f()

In b.py:

import a 

x = 1

def g():
print af()

First, let's try to import a.py:

<code>>>> import a 
1</code>

can work well, maybe you will be surprised. After all, we do do a circular import here, shouldn't it be a problem?


The mere existence of a circular import is not a problem with Python itself, if a module is imported, Python will not try to re-import it. According to this, each module may encounter some problems at runtime when trying to access functions or variables.


What happens when we try to import b.py (a.py was not previously imported):

>>> import b 
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "b.py", line 1, in <module>
import a
File "a.py", line 6, in <module>
print f()
File "a.py", line 4, in f
return bx
AttributeError: 'module' object has no attribute 'x'

There is an error, the problem here is that in the process of importing b.py, it also tries to import a.py, so f() is called, and it tries to access bx. But bx is not defined.


It can be solved like this, just modify the g() function imported by b.py into a.py:

x = 1 
def g():
import a # This will be evaluated only when g() is called
print af()

Everything works fine whenever I import:

>>> import b 
>>> bg()
1 # Printed a first time since module 'a' calls 'print f()' at the end
1 # Printed a second time, this one is our call to 'g'

8. Conflicts with Python standard library module names


Python has a very rich module library and supports it "out of the box". Therefore, if not deliberately avoided, naming collision incidents can easily occur. For example, you might have an email.py module in your code that, because of the same name, is likely to conflict with the standard library module that comes with Python.


9. Differences between Python2.x and Python3.x are not handled as specified


Take a look at foo.py:

import sys 

def bar(i):
if i == 1:
raise KeyError(1)
if i == 2:
raise ValueError(2)

def bad():
e = None
try:
bar(int(sys.argv[1] ))
except KeyError as e:
print('key error')
except ValueError as e:
print('value error')
print(e)

bad()

This works fine in Python 2:

$ python foo.py 1 
key error
1
$ python foo.py 2
value error
2

But in Python 3:

$ python3 foo.py 1 
key error
Traceback (most recent call last):
File "foo.py", line 19, in <module>
bad()
File "foo.py", line 17, in bad
print(e)
UnboundLocalError : local variable 'e' referenced before assignment

solution:

import sys 

def bar(i):
if i == 1:
raise KeyError(1)
if i == 2:
raise ValueError(2)

def good():
exception = None
try:
bar(int(sys.argv[1] ))
except KeyError as e:
exception = e
print('key error')
except ValueError as e:
exception = e
print('value error')
print(exception)

good()

Running result in Py3k:

<code>$ python3 foo.py 1 
key error
1
$ python3 foo.py 2
value error
2</code>

There are many notes and discussions about Python 2 and Python 3 that need to be paid attention to when porting code in the Python Recruitment Guide, you can go and have a look.


10. Abusing the __del__ method


For example, here is a file called mod.py:

import foo 
class Bar(object):
...
def __del__(self):
foo.cleanup(self.myhandle)

Next, you do the following in the another_mod.py file:

import mod 
mybar = mod.Bar()

You will get an AttributeError exception.


As for why this exception occurs, click here for details. When the interpreter shuts down, the module's global variables are all set to None. So, in the above example, when __del__ is called, foo has all been set to None.


A good solution is to use atexit.register() instead. By the way, when the program execution finishes, the handler you registered stops working before the interpreter shuts down.


The code to fix the above problem:

import foo 
import atexit

def cleanup(handle):
foo.cleanup(handle)


class Bar(object):
def __init__(self):
...
atexit.register(cleanup, self.myhandle)

This implementation provides a neat and reliable way to call any function that needs cleanup, provided that the program terminates normally.


Summarize


Python is a powerful and flexible programming language and comes with many mechanisms and patterns to greatly increase productivity. As with any language or software tool, people will have a limited understanding or appreciation of its capabilities, some of which do more harm than good, and some times it will bring some traps. Getting to grips with the nuances of a language and understanding some common pitfalls will help you go further as a developer.

From: toptal, compiled by CSDN

Link: https://www.csdn.net/article/2014-05-12/2819716-Top-10-Mistakes-that-Python-Programmers-Make



picture


  1. YYDS! One line of Python code can realize data visualization on a large screen

  2. Will computer science become the next civil engineering?

  3. Why can't we make products like JetBrains in China?


picture

picture