Summary of knowledge related to variables of Python foundation

  • 2021-11-13 08:03:21
  • OfStack

Variables are all references

Unlike other programming languages, Python's variables are not boxes, they don't store data, they're just references, like Tag 1, attached to objects.

For example:


>>> a = [1, 2, 3]
>>> b = a
>>> a.append(4)
>>> b
[1, 2, 3, 4]
>>> b is a
True

The a variable and the b variable refer to the same list [1, 2, 3] . b can be called an alias for a.

By comparison:


>>> a = [1, 2, 3]
>>> c = [1, 2, 3]
>>> c == a
True
>>> c is a
False

c refers to another list, which has the same value as the list referenced by a, but is a different object.

Shallow replication and deep replication

Shallow copy means copying only the outermost container, and the elements in the copy are references to the elements in the source container. If all elements are immutable, then there is no problem and content can be saved. However, if there are mutable elements, the results may be unexpected. Construct method or [:] What we do is shallow replication.

Example:


>>> x1 = [3, [66, 55, 44], (7, 8, 9)]
# x2 Yes x1 Shallow replication of 
>>> x2 = list(x1)

#  Immutable elements have no effect 
>>> x1.append(100)
>>> x1
[3, [66, 55, 44], (7, 8, 9), 100]
>>> x2
[3, [66, 55, 44], (7, 8, 9)]  

# x1[1] Is a list, and variable elements affect x2
#  Because they refer to the same 1 Objects 
>>> x1[1].remove(55)
>>> x1
[3, [66, 44], (7, 8, 9), 100]
>>> x2
[3, [66, 44], (7, 8, 9)]  

# x2[1] It will also affect in turn x1
>>> x2[1] += [33, 22]
>>> x1
[3, [66, 44, 33, 22], (7, 8, 9), 100]  
>>> x2
[3, [66, 44, 33, 22], (7, 8, 9)]

#  Immutable tuples have no effect 
# += Operator creates the 1 New tuples 
>>> x2[2] += (10, 11)
>>> x1
[3, [66, 44, 33, 22], (7, 8, 9), 100]  
>>> x2
[3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]

Deep replication refers to replication as we generally understand it. The replica does not share the reference of internal objects, but is a completely independent replica. This can be achieved with the help of copy. deepcopy.

Example:


>>> a = [10, 20]
>>> b = [a, 30]
>>> a.append(b)
>>> a
[10, 20, [[...], 30]]
>>> from copy import deepcopy
>>> c = deepcopy(a)
>>> c
[10, 20, [[...], 30]]

Even if there is a circular reference, it can be copied correctly.

Note that copy. copy () is shallow replication and copy. deepcopy () is deep replication.

Function parametric transmission

The only parameter passing mode supported by Python 1 is shared parameter passing, which means that each formal parameter of a function gets a copy of each reference in the argument. Because Python's variables are all references. There is no problem with immutable objects, but not with mutable objects.

Example:


>>> def f(a, b):
...     a += b
...     return a
... 

#  The numbers remain the same 
>>> x = 1
>>> y = 2
>>> f(x, y)
3
>>> x, y
(1, 2)

#  The list has changed 
>>> a = [1, 2]
>>> b = [3, 4]
>>> f(a, b)
[1, 2, 3, 4]
>>> a, b
([1, 2, 3, 4], [3, 4])

#  Tuple invariant 
>>> t = (10, 20)
>>> u = (30, 40)
>>> f(t, u)
(10, 20, 30, 40)
>>> t, u
((10, 20), (30, 40))

From this, we can draw a warning: try not to use variable parameters for function parameters, and if it is necessary, we should consider copying them inside the function.

Example:


class TwilightBus:
    """A bus model that makes passengers vanish"""

    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = passengers

    def pick(self, name):
        self.passengers.append(name)

    def drop(self, name):
        self.passengers.remove(name)

Under Test 1:


>>> basketball_team = ['Sue', 'Tina', 'Maya', 'Diana', 'Pat']
>>> bus = TwilightBus(basketball_team)
>>> bus.drop('Tina')
>>> bus.drop('Pat')
>>> basketball_team
['Sue', 'Maya', 'Diana']

The students who got off TwilightBus actually disappeared from basketball_team. This is because self. passengers refers to the same list object. The modification method is very simple, make a copy:


 def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = list(passengers)  #  Copy a copy using a constructor 

del and Garbage Collection

The del statement deletes references, not objects. However, del may cause objects to be unreferenced and then be garbage collected.

Example:


>>> import weakref
>>> s1 = {1, 2, 3}
# s2 And s1 Quote the same 1 Objects 
>>> s2 = s1
>>> def bye():
...     print("Gone")
...     
#  Monitor objects and call callbacks 
>>> ender = weakref.finalize(s1, bye)
>>> ender.alive
True
#  Delete s1 It still exists after s2 Quote 
>>> del s1
>>> ender.alive
True
# s2 Rebind causes {1, 2, 3} Reference to zero 
>>> s2 = "spam"
Gone
#  The object was destroyed 
>>> ender.alive
False

In CPython, the object is destroyed immediately after the number of references to the object returns to zero. If there are no references other than circular references, both objects will be destroyed.

Weak reference

In some cases, you may want to save a reference to an object, but not the object itself. For example, there is a class that wants to record all instances. This requirement can be implemented using weak references.

For example, weakref. finalize (s1, bye) in the above example, finalize holds {1, 2, 3} Although there are references, it will not affect the destruction of objects.

Other ways to use weak references are WeakDictionary, WeakValueDictionary, WeakSet.

Example:


class Cheese:

    def __init__(self, kind):
        self.kind = kind

    def __repr__(self):
        return 'Cheese(%r)' % self.kind
>>> import weakref
>>> stock = weakref.WeakValueDictionary()
>>> catalog = [Cheese('Red Leicester'), Cheese('Tilsit'),
...                 Cheese('Brie'), Cheese('Parmesan')]
...
>>> for cheese in catalog:
        #  Used as a cache 
        # key Yes cheese.kind
        # value Yes cheese Weak reference of 
...     stock[cheese.kind] = cheese
...
>>> sorted(stock.keys())
['Brie', 'Parmesan', 'Red Leicester', 'Tilsit']

#  Delete catalog Quote, stock Weak references do not affect garbage collection 
# WeakValueDictionary After the object referenced by the value of is destroyed, the corresponding key will be automatically deleted 
>>> del catalog
>>> sorted(stock.keys())  #  Still exist 1 A cheese A reference to a temporary variable 
['Parmesan']

#  Delete cheese A reference to a temporary variable, stock It's completely emptied 
>>> del cheese
>>> sorted(stock.keys())
[]

Note that not every Python object can be a target for weak references, such as the basic list and dict, but their subclasses do:


>>> a = [1, 2, 3]
>>> c = [1, 2, 3]
>>> c == a
True
>>> c is a
False
0

Summary

This article first illustrates the fact that Python variables are all references, which means that in Python, simple assignments do not create copies. If you want to create a copy, you can choose shallow copy and deep copy. Shallow copy uses construction methods, [:] Or copy.copy() For deep replication copy.deepcopy() . del deletes references, but causes objects to be garbage collected without references. Sometimes it is necessary to preserve references without preserving objects (such as caches), which is called weak references, and the weakref library provides the corresponding implementation.

References:

"Smooth Python"


Related articles: