Introduction of Python

Python is an object-oriented interpreted high-level programming language with dynamic semantics. With its built-in advanced data structures and support for dynamic typing and dynamic binding, Python is convenient for rapid application development. As well as being a scripting language, it is compatible with some existing components and services. Python also supports extensions to modules and various libraries, helping to achieve modular programming and code reuse.

About this article

Newcomers to the language may be uncomfortable with Python’s compact, flexible syntax or underestimate its power. With that in mind, here are 10 common mistakes Python developers make that even experienced programmers can’t avoid.



This article is for advanced Python developers. Python geeks can refer to the following article:

http://www.onlamp.com/pub/a/python/2004/02/05/learn_python.html

Common mistake # 1: Abusing expressions as default values for function arguments

Python allows developers to specify default values for function arguments, which is a great feature of Python, but can be confusing when the default values are variable. For example, the following functions are defined:

>>> 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 barCopy the code

See the bug? That is, a variable parameter is not assigned before each call to the function and is assumed to be the default value. For example, in the code above, one might expect to return ‘baz’ when foo() is called repeatedly, thinking that bar will have the value [], an empty list, every time foo() is called.

However, let’s take a look at the code result:

Copy the code
>>> foo()

["baz"]

>>> foo()

["baz", "baz"]      

>>> foo()

["baz", "baz", "baz"]Copy the code

Huh? Why is “baz” constantly being added to an existing list every time foo() is called instead of creating a new list? The answer is that the default values of function parameters are executed only once when the function is defined. Thus, bar is initialized to the default (that is, an empty list) only the first time foo() is defined, and thereafter, each time foo() is called, the argument bar is the list that was generated the first time it was initialized.

Common solutions:

>>> def foo(bar=None):

... if bar is None: # or if not bar:

... bar = []

... bar.append("baz")

... return bar

...

>>> foo()

["baz"]

>>> foo()

["baz"]

>>> foo()

["baz"]Copy the code

Common mistake # 2: Using class variables incorrectly

Code examples:

Copy the code
>>> class A(object):

... x = 1

...

>>> class B(A):

... pass

...

>>> class C(A):

... pass

...

>>> print A.x, B.x, C.x

1 1 1Copy the code

The results were fine.

>>> B.x = 2

>>> print A.x, B.x, C.x

1 2 1Copy the code

And it turns out to be correct.

>>> A.x = 3

>>> print A.x, B.x, C.x

3 2 3Copy the code

What the hell? We only changed A.x. Why did C.x change too?

In Python, class variables are handled internally in dictionary form, following Method Resolution Order (MRO). So, in the code above, because the attribute X is not found in class C, it looks for the value of x from the parent class (although Python supports multiple inheritance, only one parent class A exists in the code above). In other words, C has no x of its own that is independent of class A. So, C.x actually refers to A.x. Unless handled properly, this can lead to Python errors.

To learn more about Python’s class features, please click:

https://www.toptal.com/python/python-class-attributes-an-overly-thorough-guide

Common mistake 3: The error specifies the parameters of the exception code block

Suppose you have code like this:

>>> 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 rangeCopy the code

The problem here is that an except statement does not accept a list of exceptions specified in this way. In Python2.x, the variable e in the except Exception statement can be used to bind Exception information to a second optional parameter for further viewing the Exception. Therefore, in the above code, the except statement does not catch an IndexError exception; Instead, the exception that occurs is bound to the parameter IndexError.

The correct way to catch multiple exceptions simultaneously in an except statement is to specify the first parameter as a tuple and write the types of exceptions to be caught into that tuple. For convenience, use the AS keyword, which is supported by both Python 2 and Python 3:

>>> try:

... l = ["a", "b"]

... int(l[2])

... except (ValueError, IndexError) as e: 

... pass

...

>>>Copy the code

Common mistake # 4: Misunderstanding the scope of variables in Python

Python variable scopes follow the LEGB rule. If you want additional recommendations on this. LEGB stands for Local, enclosed, Global, and Builtin. In fact, Python works this way in a unique way that can lead to programming errors such as:

>>> 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 assignmentCopy the code

What’s the problem?

The above error is caused by the fact that when assigning a value to a variable within a scope, Python automatically treats that variable as a local variable to that scope and shields externally defined variables with the same name. Therefore, the correct code adds an assignment statement inside a function and accidentally receives an UnboundLocalError message.

UnboundLocalError UnboundLocalError

https://docs.python.org/2/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value

Python programmers are more likely to fall into such traps when using lists, such as:

>>> 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 assignmentCopy the code

Why is foo1 working and foo2 crashing?

The reason is similar to what happened in the previous case, but the error here is more subtle. Foo1 does not assign to LST, while foo2 does.

First, LST += [5] is short for LST = LST + [5], and an attempt is made to assign the variable LST in the function foo2 (Python defaults the variable LST to a locally scoped variable). Foo2: LST += [5] : LST = foo2: LST = foo2: LST = foo2: LST = foo2: LST = foo2: LST = foo2

Common mistake 5: Modifying a list while iterating through it

The error in the following code is obvious:

>>> 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 rangeCopy the code

Experienced programmers know that you should never delete elements from a list or array when iterating through it in Python. While the error in the code above is obvious, it is common for experienced programmers to make such mistakes when writing complex code.

Fortunately, Python incorporates a number of classic programming paradigms that, when used properly, can greatly simplify code and make programming more efficient. Simple code reduces the chance of these bugs. List comprehensions is one of the tools that will perfectly avoid the above bugs. The solution is as follows:

>>> 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]Copy the code

More about detailed content list analytical formula, please stamp: https://docs.python.org/2/tutorial/datastructures.html#tut-listcomps

Common mistake 6: Not understanding variable bindings in Python closures

Code examples:

Copy the code

>>> def create_multipliers():

... return [lambda x : i * x for i in range(5)]

>>> for multiplier in create_multipliers():

... print multiplier(2)

...
Copy the code

You would expect the result to be:

0, 2, 4, 6, 8Copy the code

But the actual output is: 8

8

8

8

8Copy the code

Surprise!

This is due to Python’s late binding mechanism, which means that the value of a variable in a closure is searched only when an internal function is called. So in the code above, every time the return function in create_multipliers() is called, the value of variable I is queried in a nearby scope. (At this point, the loop in return has ended, so I is 4).

Common solutions:

Copy the code

>>> 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, 8Copy the code

That’s right! We use the default arguments of the anonymous function lambda to generate the result sequence. Some will find this usage concise, some will call it clever, and some will find it hard to understand. If you are a Python developer, it is important that you have a good understanding of the above syntax.

Common mistake 7: Cyclic dependencies between modules

Suppose you have two files, a.py and b.py, which import to each other as follows:

Code in a.py module:

import b

def f():

return b.x

print f()Copy the code

Code in b. Py module:

import a

x = 1

def g():

print a.f()Copy the code

First, we try to import a.py:

>>> import a

1Copy the code

Running result is correct! This seems a bit surprising, since we are doing a loop import here and should report an error!

The answer is that in Python, if there is only one circular import, the program will not report an error. If a module has already been imported, Python automatically recognizes it and does not import it again. But if each module tries to access functions or variables in different positions of other modules, then Error and double 叒 yi appears.

Going back to the example above, when importing the A.py module, the program can import the B.py module normally because the B.py module does not access any variables or functions defined in a.py at this point. The b.py module only references the a.f() function in the a.py module. The called a.f() function is subordinate to the g() function, which is not called in the a.py or B.py modules. So the program does not report an error.

But what if we import the B.py module before importing the A.py module?

>>> 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 b.x

AttributeError: 'module' object has no attribute 'x'Copy the code

An error! The problem is that in the process of importing B.py, it tries to import the A.py module, which calls the f() function, which tries to access the b.x variable. But at this point, the variable b.x has not been defined, so an AttributeError exception occurs.

A slight modification of b.py, that is, importing a.py inside the g() function, solves the above problem.

Revised B.py:

x = 1

def g():


import a # This will be evaluated only when g() is called

print a.f()Copy the code

Now we import the b.py module again, there will be no error!

>>> import b >>> b.g() 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'Copy the code

Common error 8: File names conflict with Python standard library module names

One of Python’s strengths is its integration with a rich standard library. Because of this, you can accidentally name your files with the same names as Python’s own standard library modules. For example, if you have a module in your code called email.py, it happens to be the same as the module email.py in the Python standard library.

The above problems are complicated. For example, when importing module A, if module A tries to import module B from the Python standard library, but you have already defined A module B with the same name, module A will import your custom module B by mistake instead of module B from the Python standard library. This kind of error is bad because it’s hard for programmers to know it’s due to a naming conflict.

Python programmers should therefore be careful to avoid naming conflicts with Python standard library modules. After all, it’s much easier to change the name of your module than it is to change the name of the standard library! Of course, you could write a Python Enhancement Proposal (PEP) proposing to change the name of the standard library.

Common mistake # 9: Not familiar with the difference between Python2 and Python3

Let’s start with the code in 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()Copy the code

In Python 2, the above code works fine

$ python foo.py 1

key error

1

$ python foo.py 2

value error

2Copy the code

But when run 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 assignmentCopy the code

What’s going on? It turns out that in Python 3, exception objects cannot be accessed outside the scope of the except code block. (The reason is that Python 3 keeps circular references in the memory stack until they are cleaned up in memory after the garbage collector runs.)

For more information please click:

https://docs.python.org/3/reference/compound_stmts.html#except

One solution is to add a reference to the exception object outside the scope of the except code block to access the exception object normally. Here is the processed code, which runs the same in Python2 and Python3:

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()Copy the code

Run the code in Python3 again:

$ python3 foo.py 1

key error

1

$ python3 foo.py 2

value error

2Copy the code

Problem solved!

For more information on the differences between Python2 and Python3, please click:

https://www.toptal.com/python#hiring-guide

Common mistake # 10: Misuse of the _del_ method

Suppose a file named mod.py has the following code:

import foo

class Bar(object):

...

def __del__(self):

foo.cleanup(self.myhandle)Copy the code

Then, you want to do the following in the another_mod.py file:

import mod

mybar = mod.Bar()Copy the code

If you try to run another_mod.py, an AttributeError will be raised.

Why is that? This is because the module’s global variables are set to None when the Python interpreter is turned off. Thus, in the example above, foo is already None when the __del__ function is called.

For more on the Python interpreter, please click:

https://mail.python.org/pipermail/python-bugs-list/2009-January/069209.html

Calling the atexit.register() function solves the above high-level Python programming problem. After calling atexit.register(), the registration handler will run before the interpreter closes when your code finishes running (that is, when you normally exit the program).

Using the above method, the modified mod.py file looks like this:

import foo

import atexit

def cleanup(handle):

foo.cleanup(handle)

class Bar(object):

def __init__(self):

...

atexit.register(cleanup, self.myhandle)Copy the code

This method is a convenient way to invoke the program’s cleanup function when the program terminates normally. In the example above, the foo.cleanup function decides what to do with the object bound to self.myHandle, but calling atexit.register () lets you decide when to do the cleanup.

conclusion

Python is a powerful and flexible programming language that provides many programming mechanisms and paradigms that can greatly improve our productivity. But no matter what software tool or programming language you use, developers should have a thorough understanding of Python’s syntax rules and programming specifications, or they will be trapped in a state of “half-knowledge”.

Learning the syntax rules of Python, especially those mentioned in this article, will help reduce error rates and improve Python programming efficiency.



Big Data Abstracts
Big Data Abstracts