• 对象引用、可变性和垃圾回收


    对象的标注、引用、别名

    Python变量类似于Java中的引用式变量,因此最好把他们理解为附加在对象上的标注。

    Python赋值语句应该始终先读右边。

    对象在右边创建或获取,再此之后左边的变量才会绑定到对象上,这就像为对象贴上标注。

    变量只不过是标注,所以无法阻止为对象贴上多个标注。贴的多个标注,就是别名。 

    >>> f1 = {'name':'Li','age':19}
    >>> f2 = f1    # f2是f1的别名
    >>> f2 is f1
    True
    

    每个变量(object)都有标识(identity)、类型(type)和值(value)。

    对象一旦创建,它的标识绝不会变。可以把标识理解为对象在内存中的地址。

    is运算符比较两个对象的标识,id()函数返回对象标识的整数表示,==运算符比较两个对象的值(对象中保存的数据)。

     对+= 或 *= 所做的增量赋值来说,如果左边的变量绑定的是不可变对象,会创建新对象,如果是可变对象,会就地修改。

    元组的相对不可变性

    元组的相对不可变性:

    元组与多数Python集合(列表、字典、集)一样,保存的是对象的引用。

    如果引用的元素是可变的,即便元组本身不可变,元素依然可变。

    copy、deepcopy

    浅复制:(复制最外层容器,副本中的元素是源容器中元素的引用)

    复制列表最简单的方式:

    (1)使用构造方法

    (2)使用 [ :]

    >>> l1 = [1,2,3,4]
    >>> l2 = list(l1)
    >>> l3 = l1[:]
    

    深复制:副本不共享内部对象的引用

    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)
    
    import copy
    
    bus1 = Bus([1,2,3,'David','Tom'])
    bus2 = copy.copy(bus1)
    bus3 = copy.deepcopy(bus1)
    
    print(id(bus1),id(bus2),id(bus3))
    print(id(bus1.passengers),id(bus2.passengers),id(bus3.passengers))
    
    bus1.drop('David')
    bus1.pick(998)
    
    print(bus1.passengers)
    print(bus2.passengers)
    print(bus3.passengers)
    View Code

    函数参数传递模式

    函数参数作为引用时:

    Python唯一支持的参数传递模式:共享传参(call by sharing)

    共享传参:指函数各个形式参数获得实参中各个引用的副本。(函数内部的形参是实参的副本)

    def f(a,b):
        a += b
        return a
        
    a = 1
    b = 2
    c = f(a,b)
    >>> print(c)
    3
    >>> print(a,b)
    1,2
    
    # ========
    
    def f(a,b):
        a += b
        return a
        
    a = [1,2]
    b = [3,4]
    c = f(a,b)
    >>> print(c)
    [1, 2, 3, 4]
    >>> print(a,b)
    [1, 2, 3, 4] [3, 4]
    

    避免使用可变类型作为参数的默认值

    bus_team = ['Sue','David','Pat','Maria']
    bus = Bus(bus_team)
    bus.drop('David')
    bus.drop('Pat')
    >>> bus_team
    ['Sue','Maria']
    

    在类中直接把参数赋值给实例变量等于为参数对象创建别名。

    class Bus:
        def __init__(self,bus_team):
            if bus_team is None:
                self.bus_passengers = []
            else:
                self.bus_passengers = bus_team
    
    # =============
    
    class Bus:
        def __init__(self,bus_team):
            if bus_team is None:
                self.bus_passengers = []
            else:
                self.bus_passengers = list(bus_team)
    

    垃圾回收机制

    del和垃圾回收:

    del语句删除名称,而不是对象。

    del命令可能会导致对象被当作垃圾回收,但是仅当删除的变量保存的是对象的最后一个引用,或者无法得到对象时。

    在Cpython中,垃圾回收使用的主要算法是引用计数。实际上,每个对象都会统计有多少引用指向自己。

    当引用计数归零时,对象立即就被销毁。Cpython会在对象上调用__del__方法,然后释放分配给对象的内存。

    >>> import weakref 
    >>> s1 = {1,2,3}
    >>> s2 = s1
    >>> ender = weakref.finalize(s1,bye)   # 注册一个回调函数
    >>> ender.alive
    True
    >>> del s1
    >>> ender.alive
    True
    >>> s2 = '123'
    Gone with the wind
    >>> ender.alive
    False
    

    弱引用:

    正因为有引用,对象才会在内存中存在。

    弱引用不会增加对象的引用数量。

    引用的目标对象称为所指对象(referent),弱引用不会妨碍所指对象被当作垃圾回收。

    WeakValueDictionary 类实现的是一种可变映射,里面的值是对象的弱引用。

    被引用的对象在程序中的其他地方被当作垃圾回收后,对应的键会自动从WeakValueDictionary中删除。

    class Cheese:
        def __init__(self,kind):
            self.kind = kind
    
    >>> import weakref
    >>> stock = weakref.WeakValueDictionary()
    >>> catalog = [Cheese('Red Leicester'),Cheese('Tilsit'),Cheese('Parmesan')]
    >>> for cheese in catalog:
                stock[chess.kind] = cheese
    
    >>> sorted(stock.keys())
    ['Parmesan','Red Leicester','Tilsit']
    >>> del catalog
    >>> sorted(stock.keys())
    ['Parmesan']
    >>> del cheese
    >>> sorted(stock.keys())
    []
    

    ▲ for循环中的变量cheese是全局变量,除非显式删除,否则不会消失。

    weakref 模块还提供了WeakSet 类,如果一个类需要知道所有实例,一种好的方案是创建一个WeakSet 保存实例引用。

    常规Set实例永远也不会被垃圾回收,因为类中有实例的强引用。而类存在的时间与Python进程一样长,除非显式删除类。

    弱引用的局限:

    基本的list和dict实例不能作为所指对象,但是他们的子类可以。set实例和用户自定义类型可以作为所指对象。

    同时int和tuple实例不能作为弱引用的目标,甚至它们的子类也不行。

    class Mylist(list):
        ...
    
    a_list = Mylist(range(10))
    wref_to_a_list = weakref.ref(a_list)
    

      

  • 相关阅读:
    Chrome自带恐龙小游戏的源码研究(四)
    Chrome自带恐龙小游戏的源码研究(三)
    Chrome自带恐龙小游戏的源码研究(二)
    Chrome自带恐龙小游戏的源码研究(一)
    使用HTML5制作简单的RPG游戏
    EventListener中的handleEvent
    canvas drawImage方法不显示图片的解决方案
    canvas转盘抽奖的实现(二)
    股市高手的领悟
    《最伟大的投资习惯》读书笔记
  • 原文地址:https://www.cnblogs.com/5poi/p/11201564.html
Copyright © 2020-2023  润新知