We often encounter some inexplicable errors in Python programming. In fact, this is not a problem with the language itself, but caused by us ignoring some characteristics of the language itself. Today we will take a look at 3 incredible errors caused by using Python variables. Error, please pay more attention in future programming.
This seems right? You write a small function that, say, searches for a link on the current page and optionally appends it to another provided list.
def search_for_links(page, add_to=[]): new_links = page.search_for_links() add_to.extend(new_links) return add_to
On the surface, this looks like very normal Python code. In fact, it is, and it can run. However, there is a problem. If we provide a list to the add_to parameter, it will work as we expect. But if we let it use the default value, something magical happens.
Try the following code:
def fn(var1, var2=[]): var2.append(var1) print(var2) fn(3) fn(4) fn(5)
Maybe you think we will see:
[3] [4] [5]
But in fact, what we see is:
[3] [3,4] [3,4,5]
why? As you can see, the same list is used every time. Why is the output like this? In Python, when we write such a function, this list is instantiated as part of the function definition. When a function runs, it is not instantiated every time. This means that this function will always use the exact same list object unless we provide a new object:
fn(3,[4]) [4,3]
The answer is exactly what we thought. To get this result, the correct way is:
def fn(var1, var2=None): ifnot var2: var2 =[] var2.append(var1)
Or in the first example:
def search_for_links(page, add_to=None): ifnot add_to: add_to =[] new_links = page.search_for_links() add_to.extend(new_links) return add_to
This will remove the instantiated content when the module is loaded so that every List instantiation occurs every time the function is run. Please note that for immutable data types, such as tuples, strings, and integers, this situation does not need to be considered. This means that code like the following is very feasible:
def func(message="my message"): print(message)
This is very similar to the last error mentioned above. Consider the following code:
class URLCatcher(object): urls =[] def add_url(self, url): self.urls.append(url)
This code looks perfectly normal. We have an object that stores URLs. When we call the add_url method, it adds a given URL to the storage. Looks pretty right right? Let’s see what it actually looks like:
a =URLCatcher() a.add_url('http://www.google.com') b =URLCatcher() b.add_url('http://www.pythontab.com') print(b.urls) print(a.urls)
Result:
['http://www.google.com','http://www.pythontab.com'] ['http://www.google.com','http://www.pythontab.com']
Wait, what’s going on? ! That's not what we thought. We instantiate two separate objects a and b. Give one URL to a and another to b. How come these two objects have these two URLs?
This is the same problem as the first error example. When the class definition is created, the URL list is instantiated. All instances of this class use the same list. There are times when this is useful, but most of the time you don't want to do it. You want a separate storage for each object. To do this, we modify the code to:
class URLCatcher(object): def __init__(self): self.urls =[] def add_url(self, url): self.urls.append(url)
Now, when the object is created, the URL list is instantiated. When we instantiate two separate objects, they will each use two separate lists.
This problem has troubled me for a while. Let's make some changes and use another mutable data type - a dictionary.
a ={'1':"one",'2':'two'}
Now, suppose we want to use this dictionary elsewhere and keep its original data intact.
b = a b['3']='three'
Simple, right?
Now, let’s look at the original dictionary a that we don’t want to change:
{'1':"one",'2':'two','3':'three'}
Wow wait a minute, let’s look at b?
{'1':"one",'2':'two','3':'three'}
Wait, what? A bit messy... Let's back up and see what happens in this case with other immutable types, such as a tuple:
c =(2,3) d = c d =(4,5)
Now c is (2, 3) and d is (4 , 5).
The result of this function is as we expected. So, what exactly happened in the previous example? When using a mutable type, it behaves somewhat like a pointer in C. In the above code, we let b = a, what we really mean is: b becomes a reference of a. They all point to the same object in Python memory. Sound familiar? That's because this question is similar to the previous one.
Does the same thing happen with lists? Yes. So how do we solve it? This must be done very carefully. If we really need to copy a list for processing, we can do this:
b = a[:]
This will loop through and copy the reference of each object in the list and put it in a new list. But be careful: if every object in the list is mutable, we will get a reference to them again, not a complete copy.
Suppose you make a list on a piece of paper. In the original example, it is equivalent to A and B looking at the same piece of paper. If one person modifies this listing, both people will see the same changes. When we copy the references, everyone now has their own list. However, we assume that this list includes places to find food. If "refrigerator" is first in the list, even if it is copied, the entries in both lists will point to the same refrigerator. Therefore, if the refrigerator is modified by A and eats the big cake inside, B will also see the disappearance of the cake. There's no easy way around it. Just remember it and write your code in a way that doesn't cause this problem.
字典以相同的方式工作,并且你可以通过以下方式创建一个昂贵副本:
b = a.copy()
再次说明,这只会创建一个新的字典,指向原来存在的相同的条目。因此,如果我们有两个相同的列表,并且我们修改字典 a 的一个键指向的可变对象,那么在字典 b 中也将看到这些变化。
可变数据类型的麻烦也是它们强大的地方。以上都不是实际中的问题;它们是一些要注意防止出现的问题。在第三个项目中使用昂贵复制操作作为解决方案在 99% 的时候是没有必要的。
The above is the detailed content of Introduction to common mistakes in using Python variables. For more information, please follow other related articles on the PHP Chinese website!