程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

[python] object reference, variability, and garbage collection

編輯:Python

《 smooth Python》 Luciano · Ramallo The first 8 Chapter Reading notes

8.1 Variables are not boxes

In order to understand Python The assignment statement in , You should always read the right first . Object created or acquired on the right , After that, the variables on the left will be bound to the object , It's like labeling objects .
Because variables are just annotations , Therefore, you cannot prevent multiple annotations from being pasted on objects .

8.2 identification 、 Equality and aliases

Each variable has an identifier 、 Type and value . Object once created , Its logo will never change ; You can understand identity as the address of the object in memory .
== Operator to compare the values of two objects ( Data saved in object )
is Identification of the comparison object ,id() Function returns an integer representation of the object ID

Most commonly used is Check whether the value of variable binding is None
x is None​​
x is not None​​

is Operator ratio == Fast , Because it can't be overloaded , therefore Python Don't look for and call special methods , Instead, compare two integers directly ID. and a==b It's grammar sugar , Equate to a.__eq__(b). Inherited from object Of __eq__ Method to compare the ID, Results and is equally . But most built-in types override... In a more meaningful way __eq__ Method , The value of the object property is taken into account .

8.3 Make light copy by default

A method of construction or [:] It's a shallow copy ( That is to copy the outermost container , The element in the replica is a reference to the element in the source container ). If all elements are immutable , So there's no problem , It also saves memory . however , If there are variable elements , May lead to unexpected problems .

Make deep and shallow copies of any object
Sometimes what we need is deep replication ( That is, the replica does not share references to internal objects ).copy Module provided deepcopy and copy Function can do deep copy and shallow copy for any object . To demonstrate copy() and deepcopy() Usage of , The following example defines a simple class ,Bus. This class represents the school bus that carries passengers , On the way, passengers will get on or off .
【 example 】 School bus passengers get on and off on the way
​​

class Bus:
def __init__(self, passengers=None):
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)

Interactive console Create a Bus Instance and two copies

>>> import copy
>>> from Bus import Bus
>>> bus1 = Bus(['Alice', 'Bill', 'Claire', 'David'])
>>> bus2 = copy.copy(bus1)
>>> bus3 = copy.deepcopy(bus1)
>>> id(bus1), id(bus2), id(bus3)
(140137100109976, 140137100110144, 140137100110312)
>>> bus1.drop('Bill')
>>> bus1.passengers
['Alice', 'Claire', 'David']
>>> bus2.passengers
['Alice', 'Claire', 'David']
>>> bus3.passengers
['Alice', 'Bill', 'Claire', 'David']
>>> id(bus1.passengers), id(bus2.passengers), id(bus3.passengers)
(140137100086024, 140137100086024, 140137136242952)
>>>

explain :
line4, bus2 yes bus1 Shallow copy
line5, bus3 yes bus1 Deep replication
line8, bus1 Medium 'Bill' Get off the bus
line12, bus2 None of them 'Bill'
line15, review passengers Property found ,bus1 and bus2 Share the same list object

8.4 When referring to a function as an argument

8.4.1 Do not use variable types as arguments

For example 1 Define a new class based on HauntedBus, And then modify __init__ Method .passengers The default value of is not None, It is [], So you don't have to use it as before if Determine the . This “ Smart move ” It will get us into trouble .
【 example 2】 A simple class , Explain the danger of variable defaults

class HauntedBus:
""" School bus tortured by ghost passengers """
def __init__(self, passengers=[]):
self.passengers = passengers
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)

line3  If not passengers Parameters , Use the default bound list object , At first, there is an empty list .
line4  This assignment statement puts self.passengers become passengers Another name for , And there was no introduction passengers When parameters are ,self.passengers Is the alias of the default list .
line6、8   stay self.passengers On the call .append() and .remove() When the method is used , The modification is actually the default list , It is a property of a function object .
HauntedBus The weird behavior of --> 

>>> from Bus import HauntedBus
>>> bus1 = HauntedBus(['Alice', 'Bill'])
>>> bus1.passengers
['Alice', 'Bill']
>>> bus1.pick('Charlie')
>>> bus1.drop('Alice')
>>> bus1.passengers
['Bill', 'Charlie']
>>>
>>> bus2 = HauntedBus()
>>> bus2.pick('Carrie')
>>> bus2.passengers
['Carrie']
>>>
>>> bus3 = HauntedBus()
>>> bus3.passengers
['Carrie']
>>> bus3.pick('Dave')
>>> bus3.passengers
['Carrie', 'Dave']
>>>
>>> bus2.passengers
['Carrie', 'Dave']
>>> bus2.passengers is bus3.passengers
True
>>> bus1.passengers
['Bill', 'Charlie']
>>>

explain :

line7 There is no problem at present ,bus1 No exception occurred .
line10 In limine ,bus2 It's empty. , So assign the default empty list to self.passengers.
line15 bus3 It was empty at first , Therefore, the default list of assignment .
line16 But the default list is not empty !
line22 On the bus3 Of Dave Appear in the bus2 in .
line24 The problem is ,bus2.passengers and bus3.passengers Refers to the same list .
line26 but bus1.passengers It's a different list .

The problem lies in , There is no designated initial passenger HauntedBus Instances will share the same passenger list .
Instantiation HauntedBus when , If incoming passengers , Will work as expected .
But not for HauntedBus If the initial passenger is designated ,self.passengers Turned into passengers Alias of the default value of the parameter .

review HauntedBus.__init__ object , Look at it. __defaults__ Those ghost students in the attribute :

>>> dir(HauntedBus.__init__)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>>
>>> HauntedBus.__init__.__defaults__
(['Carrie', 'Dave'],)
>>>

We can verify bus2.passengers It's an alias , It's bound to HauntedBus.__init__.__defaults__ Attribute on the first element :

>>> HauntedBus.__init__.__defaults__[0] is bus2.passengers
True
>>>


The problem caused by variable default values illustrates Why do you usually use None As the default value of the parameter that receives variable values .
In case 1 in ,__init__ Methods to check passengers Whether the value of the parameter is None, If so, assign a new empty list to self.passengers. If passengers No None, Correct realization meeting hold passengers Assign a copy of to self.passengers.

8.4.2 Defense variable parameters

stay __init__ in , Pass in passengers When parameters are , A copy of the parameter value should be assigned to self.passengers

Unless this method really wants to modify the object passed in through the parameter , Otherwise, you must think twice before assigning parameters to instance variables directly in the class , Because this creates an alias for the parameter object .

8.5 del And garbage collection

del Statement delete name ( References to objects ), Not the object .
Objects exist in memory because of references . When the number of references of the object is zero , The garbage collector will destroy the object .

Only if the deleted variable saves the last reference of the object , Or when you can't get the object ,del The command will cause the object to be garbage collected .
Rebinding may also cause the number of references to the object to return to zero , Cause the object to be destroyed .

8.6 Weak reference

Sometimes you need to reference objects , And don't let the object exist longer than it takes , This is often used in caching .
Weak references do not increase the number of references to an object   ==>  Weak references do not prevent the object from being garbage collected

The referenced target object is called the referenced object (referent), If the object does not exist , return None

>>> import weakref
>>> a_set = {0, 1}
>>> wref = weakref.ref(a_set) # Create weak reference object wref
>>> wref
<weakref at 0x7f86f289af98; to 'set' at 0x7f86f0b799e8>
>>> wref() # call wref() What is returned is the referenced object ,{0, 1}. Because this is a console session , therefore {0, 1} Will be bound to _ Variable
{0, 1}
>>> _
{0, 1}
>>>
>>> a_set = {2, 3, 4} #a_set No more referring to {0, 1} aggregate , Therefore, the number of references to the collection is reduced . however _ The variable still refers to it .
>>>
>>> _
{0, 1}
>>> wref() # call wref() Still return to {0, 1}.
{0, 1}
>>>
>>> wref() is None # When evaluating this expression ,{0, 1} There is , therefore wref() No None.
False
>>> _ # And then _ Bind to result value False. Now? {0, 1} There is no strong reference to .
False
>>>
>>> wref() is None # because {0, 1} The object doesn't exist , therefore wref() return None.
True
>>>

explain : Most programs are best to use WeakKeyDictionary、WeakValueDictionary、WeakSet and finalize( Use weak references internally ), Don't create and process by yourself weakref.ref example .

8.6.1 WeakValueDictionary brief introduction

WeakValueDictionary Class implements a variable mapping , The value inside is the weak reference of the object . After the referenced object is garbage collected elsewhere in the program , The corresponding key will automatically start from WeakValueDictionary Delete in . therefore ,WeakValueDictionary Often used for caching .

class Cheese:
    def __init__(self, kind):
        self.kind = kind
    def __repr__(self):
        return 'Cheese(%r)'%self.kind

hold catalog All kinds of cheeses in WeakValueDictionary Realized stock in . Delete catalog after ,stock There is only one kind of cheese left . You know why parmesan cheese (Parmesan) Does it last longer than other cheeses ? The answer is in the prompt behind the code .
Interactive environment testing :

>>> from Cheese import Cheese
>>> import weakref
>>> stock = weakref.WeakValueDictionary()   #stock yes WeakValueDictionary example
>>> catalog = [Cheese('Red Leicester'), Cheese('Tilsit'),Cheese('Brie'), Cheese('Parmesan')]   
>>> for cheese in catalog:
...     stock[cheese.kind] = cheese   #stock Map the name of cheese to catalog in Cheese On the weak reference of the instance
>>> sorted(stock.keys())
['Brie', 'Parmesan', 'Red Leicester', 'Tilsit']   #stock Is a full .
>>> del catalog
>>> sorted(stock.keys())
['Parmesan']   # Delete catalog after ,stock Most of the cheese in is missing , This is a WeakValueDictionary The expected behavior of . Why not all ?
>>>
>>> del cheese
>>> sorted(stock.keys())
[]
>>>

Temporary variables refer to objects , This may cause the variable to exist longer than expected . Usually , This is not a problem for local variables , Because they will be destroyed when the function returns . But in the above example ,for Variables in the loop cheese Global variable , Unless you explicitly delete , Otherwise it won't disappear .

And WeakValueDictionary The corresponding is WeakKeyDictionary, The key of the latter is weak reference .

8.6.2 Limitations of weak references

Not every Python Objects can be the target of weak references ( Or referred to as the object ).
Basic list and dict Instance cannot be the object of reference , But their subclasses can easily solve this problem ;
set Instance can be used as the object of reference , There is no problem with user-defined types ;
int and tuple An instance cannot be the target of a weak reference , Not even their subclasses .


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved