Fluent python Chapter 8 -- object reference variability garbage collection

Keywords: Python Back-end

Contents of this chapter:

Object reference:

Referential variable
 Object identifier,value,alias 

variability

Tuple properties
 Latent replication and deep replication
 References and function parameters

garbage collection

delete object
 Weak reference

Tip: the contents of this chapter are not understood clearly, and errors that are not easy to find may occur
8.1 variables are not boxes

#a is the identification of object [1,2,3], or referential variable 
a=[1,2,3]
#b is also the label or alias of the object [1,2,3]
b=a 
a.append(4)
print(b)

For referential variables, it is more reasonable to assign variables to objects

8.2 identification, equality and alias

charles={"name":"Charles L","born":1832}

#lewis is charles' pseudonym
lewis=charles 

lewis is charles

id(charles),id(lewis)


#At this time, there is an impostor:
alex={"name":"Charles L","born":1832}

#==Compare values
alex==charles 

#is compares the id value of the object
alex is not charles 

==Operator compares the values of two objects (the data stored in the object), and is compares the memory address of the object.

8.2.1 select between = = and is

Usually we use = = more because we usually care about the value of the object

Is is is most often used to check whether the value of variable binding is None. The following is the recommended wording:

x is None 

This is a faster call to the is operator. Because the is operator cannot be overloaded, Python does not need to find and call special methods

8.2.2 relative immutability of tuples
Tuples save references to objects. The immutability of tuples actually means that the physical content of tuple data structures (i.e. saved references) is immutable, independent of the referenced objects

That is, the immutable tuple is only the identification of the element, and the object pointed to is variable

t1=(1,2,[30,40])
t2=(1,2,[30,40])
t1==t2
id(t1[-1])
t1[-1].append(99)
id(t1)
t1==t2

Therefore, tuples only have relative immutability, which is the reason why tuples are not hashable
8.3 shallow copy by default

Shallow replication

l1=[3,[55,44],[7,8,9]]

#[:] the constructor does shallow copy by default
l2=l1[:]

l2 l1

l2==l1

l2 is l1

l1[1] is l2[1]

l3=la.copy()
#This method is also latent replication
l4=list(l1)

l5=list(l1)
#It is also a latent copy

Deep copy (that is, the copy does not share references to internal objects)

import copy 

l5=copy.deepcopy(l1)

Use the following example to see the difference between latent replication and deep replication

class Bus:
    def __init__(self,passengers=None):
        #Create a passenger list
        if passengers is None:
            self.passengers=[] 
        else:
            self.passengers=list(passengers)

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

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


import copy 

bus1=Bus(["Alice","bill","Charles","David"])
bus2=copy.copy(bus1)
#Use the copy module to copy variables directly. Here is latent copy
bus3=copy.deepcopy(bus1)
#Use the deep copy method in the copy module for deep copy
bus1.drop("Alice")

print(bus2.passengers)
print(bus3.passengers)

print(id(bus1.passengers)==id(bus2.passengers))
#bus2 is a shallow copy of bus1 and shares the same passenger list with bus
print(id(bus1.passengers)==id(bus3.passengers))

8.4 when the parameters of the function are used as references
The only parameter passing mode supported by Python is shared parameter passing

Shared parameters
: a formal parameter inside a function is an alias for an argument
The result of this parameter passing method: the function may modify the variable object passed in as a parameter

When the incoming parameters are immutable objects, such as integers and tuples, changing the formal parameters does not change the actual parameters.

def f(a,b):
	a+=b
	return a 
x=1
y=2
f(x,y)
print(x,y)

#tuple
t=(10,20)
u=(30,40)
f(t,u)
print(t,u)

Pay special attention when the parameter is a variable object, such as a list

x=[1,2]
y=[3,4]
f(x,y)
print(x,y)

8.4.1 do not use variable types as default values for parameters

In python functions, optional parameters can have default values.
This enables backward compatibility.
(backward compatibility: for example, when a program written on Python 2.0 can run perfectly on Python 3.0, we call this phenomenon backward compatibility or historical compatibility)

Using a variable object as the default value of a parameter may cause an error that is not easy to find.
See the following example:

class funnyBus:
    """People will disappear on the magic school bus"""
    def __init__(self,passengers=[]):
        #This sets a list (mutable objects) as the default parameter
        self.passengers=passengers

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

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

bus1=funnyBus(["uni","xiaoma","leo"])
#A list parameter is passed in. At this time, there is no problem
bus1.passengers
bus1.pick("alice")
bus2=funnyBus()
bus3=funnyBus()

At this time, the problem arises. Bus2 and bus2 instances will share a default list []

Let's look at the properties of this class.

bus2=funnyBus()
bus2.pick("hello")
bus3=funnyBus()
bus2.pick("hello ni mei")
print(dir(funnyBus.__init__))
print(funnyBus.__init__.__defaults__)

output:

(['hello', 'hello ni mei'],)

Run the following code:

print(funnyBus.__init__.__defaults__[0]  is bus2.passengers)
#The passengers attribute of instance bus2 is set to the first attribute of the class.

How to correctly handle default parameters:

class Bus:
    def __init__(self,passengers=None):
        #Create a passenger list
        if passengers is None:
        #If there is no default parameter, the instance creates one of its own.
            self.passengers=[] 
        else:
            self.passengers=list(passengers)

8.4.2 defense variable parameters

If a defined function receives variable parameters, you should carefully consider whether the caller expects to modify the incoming parameters.

The following example illustrates the risk of variable parameters:

class Bus:
    def __init__(self,passengers=None):
        #Create a passenger list
        if passengers is None:
            self.passengers=[] 
        else:
            self.passengers=passengers
            #(1)

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

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

At this time, the variable parameter is passed into the object. When the object changes the parameter, the external variable parameter will also change. Because a reference to a parameter was passed in.

So if we don't want to change the passed in parameters,
Part (1) of the above code should be written like this

self.passengers=list(passengers)
#Create a copy of the parameter

Another advantage of this is that tuples and other parameters can also be passed in.

When we are not sure whether we need to change the parameter object, we will use this method by default.

8.5 del and waste recycling

Objects will never be destroyed by themselves; However, when an object cannot be obtained, it may be garbage collected.

Del statementdeletes a reference, not an object. When the reference deleted by del is the only reference of the object, the object will be garbage collected.

python garbage collection mechanism:
Reference counting method
When the reference count returns to zero, the object is destroyed immediately.

The following example demonstrates the recycling of objects.

import weakref 
s1={1,2,3}
s2=s1
def bye():
print("Gone....")
ender=weakref.finalize(s1,bye)
ender.alive
def sl 
ender.alive
s2="spam"
ender.alive

8.6 weak references

In actual use, it is sometimes necessary to refer to objects, but do not let the objects exist longer than required.

For example, we save the data object in the cache for use. When the reference of the object does not exist, we want to release this part of the data in the cache. At this point, we can use weak references.
Explanation of the principle of weak reference:

Weak reference underlying implementation principle
A usage scenario for weak applications.

Suppose we have a multithreaded program that processes application data concurrently:

class Date:
    def __init__(self,key):
        pass

key is the unique identifier of the data. The data may be accessed by multiple threads at the same time.
When there is a large amount of Data, the creation cost is very high. We want to maintain only one copy of Data in the program,
Even if it is accessed by multiple threads at the same time, it is not created repeatedly.

You can design a caching middleware cache

import threading
#Data cache

class Cacher:
    def __init__(self):
        self.pool={}
        self.lock=threading.Lock()

    def get(self,key):
        with self.lock:
            #Lock
            data=self.pool.get(key)
            if data:
                return data 
            self.pool[key]=data=Data(key)
            return data

There is a serious problem with this code,

Once the Data is worn, it will be saved in the Data dictionary. Even if no thread needs the Data, because the Data is referenced by a key in the dictionary, the object will not be recycled.

At this time, the weak reference is used. The value in the dictionary key value pair is used to save the weak reference of the data object. When the reference of the data object is 0, the object will be automatically released.

import threading
import weakref
# Data cache
class Cacher:
    def __init__(self):
      self.pool=weakref.WeakValueDictionary()
        self.lock = threading.Lock()
    def get(self, key):
        with self.lock:
            data = self.pool.get(key)
            if data:
                return data
            self.pool[key] = data = Data(key)
            return data

Weak reference classes are in the breaker module,

Common are:

weakValueDictinary
weakKEYDictinary
weakSet

Finally, look at a small program and guess the result:

t1=(1,2,3)
t2=tuple(t1)

t2 is t1
?

t3=t1[:]

t3 is t1
?

Posted by Scotty2024 on Sat, 06 Nov 2021 05:24:08 -0700