• 流畅的python,Fluent Python 第八章笔记


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

    8.1 变量不是盒子

    这一章相对来说概念比较多,我前期已经粗粗看了一遍,挑选我觉的经典的记录。

    a = [1 ,2, 3]

    按照说中书法,正确的理解是把变量(变量名)a分配给了对象([1,2,3])

    毕竟对象在赋值之前已经创建。

    为了理解Python中的赋值语句,应该始终先读右边。对象在右边创建或获取,在此之后左边的变量才会绑定到对象上,这就像对对象贴上标注。

    8.2标识、相等性和别名

    a = 1

    b = a

    a与b是别名,因为他们同时绑定了一个对象

    is判断两个变量是否id相等。

    ID一定是唯一的数值标注,而且在对象的生命周期中绝不会变。

    8.3默认做浅复制

    一般的复制(拷贝)都是浅拷贝,浅拷贝就是复制了最外层容器,副本内的元素是容器中元素的引用,好处就是节省内存。

    l = [1, [2, 3, ], 'ok']
    
    
    l1 = list(l)
    l2 = l[:]
    l3 = l.copy()
    

     上面三种都是浅拷贝。就复制了最外层的,除非里面的元素都是不可变元素,要不然里面的元素副本都是引用,还是会带来问题。

    为任意对象做深拷贝:

    import copy
    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)
    
    
    if __name__ == '__main__':
        bus1 = Bus(['1', '2', '3'])
        bus2 = copy.copy(bus1)
        bus3 = copy.deepcopy(bus1)
        # 用了copy内部的属性引用还是一样的
        print(id(bus1.passengers),id(bus2.passengers),id(bus3.passengers))
        bus1.drop('2')
        print(bus1.passengers, bus2.passengers, bus3.passengers, sep='
    ')
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第八章/t8_8.py
    4563857312 4563857312 4563740464
    ['1', '3']
    ['1', '3']
    ['1', '2', '3']
    
    Process finished with exit code 0
    

    copy拷贝的对象,只拷贝最表层,里面的属性引用地址都是一样的。

    8.4 函数的参与作为引用时

    Python唯一支持的参数传递模式时共享传参。

    共享传参指函数的各个形式参数获得实参中各个引用的副本,直接的说,函数内部的形参时实参的别名。

    所以当传入的参数为可变类型时,内部的形参改变参数,外部的实参也会发生变换。

    8.4.1不要使用可变类型作为参数的默认值

    # t8_12
    
    class HauntedBus:
    
        def __init__(self, passengers=[]):
            self.passengers = passengers
    
        def pick(self, name):
            self.passengers.append(name)
    
        def drop(self, name):
            self.passengers.remove(name)
    
     from t8_12 import HauntedBus                                                              
    
    In [78]: bus2 = HauntedBus()                                                                       
    
    In [79]: bus2.pick('sidian')                                                                       
    
    In [80]: bus3 = HauntedBus()                                                                       
    
    In [81]: bus3.passengers                                                                           
    Out[81]: ['sidian']
    
    In [82]: bus2.passengers is bus3.passengers                                                        
    Out[82]: True
    
    In [84]: bus2.__init__.__defaults__                                                                
    Out[84]: (['sidian'],)
    
    In [85]: bus3.pick('gray')                                                                         
    
    In [86]: bus2.__init__.__defaults__                                                                
    Out[86]: (['sidian', 'gray'],)
    
    In [87]: HauntedBus.__init__.__defaults__                                                          
    Out[87]: (['sidian', 'gray'],)
    
    In [88]: bus2.__init__.__defaults__                                                                
    Out[88]: (['sidian', 'gray'],)
    
    In [89]: import dis                                                                                
    
    In [90]: dis.dis(HauntedBus)                                                                       
    Disassembly of __init__:
      4           0 LOAD_FAST                1 (passengers)
                  2 LOAD_FAST                0 (self)
                  4 STORE_ATTR               0 (passengers)
                  6 LOAD_CONST               0 (None)
                  8 RETURN_VALUE
    
    Disassembly of drop:
     10           0 LOAD_FAST                0 (self)
                  2 LOAD_ATTR                0 (passengers)
                  4 LOAD_METHOD              1 (remove)
                  6 LOAD_FAST                1 (name)
                  8 CALL_METHOD              1
                 10 POP_TOP
                 12 LOAD_CONST               0 (None)
                 14 RETURN_VALUE
    
    Disassembly of pick:
      7           0 LOAD_FAST                0 (self)
                  2 LOAD_ATTR                0 (passengers)
                  4 LOAD_METHOD              1 (append)
                  6 LOAD_FAST                1 (name)
                  8 CALL_METHOD              1
                 10 POP_TOP
                 12 LOAD_CONST               0 (None)
                 14 RETURN_VALUE
    

    为什么会这样因为定义函数的时候(通常在加载的时候),默认值变成了函数对象的属性。

    所以在定义函数的时候,默认指为可变参数,也会发送同样的问题。

    8.5 del和垃圾回收

    del删除的是名称是变量,但并不是那个对象。

    Python的垃圾清楚,主要用的是引用技术,另外还有分代收集,标记清除。

    上一段弱引用的事例,弱引用可以帮你获得对象,但不会增加对象的引用数量。

    Python 3.7.4 (default, Jul  9 2019, 18:13:23) 
    Type 'copyright', 'credits' or 'license' for more information
    IPython 7.7.0 -- An enhanced Interactive Python. Type '?' for help.
    PyDev console: using IPython 7.7.0
    Python 3.7.4 (default, Jul  9 2019, 18:13:23) 
    [Clang 10.0.1 (clang-1001.0.46.4)] on darwin
    import weakref, sys
    s1 = {1,2,3}
    s2 = s1
    def bye():
        print('Gone with the wind...')
        
    ender = weakref.finalize(s1 ,bye)
    sys.getrefcount(s1)
    Out[7]: 3
    del s2
    sys.getrefcount(s1)
    Out[9]: 2
    ender.alive
    Out[10]: True
    del s1
    Gone with the wind...
    ender.alive
    Out[12]: False
    

     经过本人多次调试,在shell中执行,书中的weakref.ref实例获取的对象,该对象一旦执行(),内部的对象标签直接上升很多。但在py文件里面可以执行。

    先上在py文件正常执行的代码:

    import weakref
    import sys
    
    a_set = {0, 1}
    
    func = lambda x: print(repr(x), 'is deleting')
    wref = weakref.ref(a_set, func)
    print(sys.getrefcount(a_set))
    
    print(wref())
    
    print(sys.getrefcount(a_set))
    
    a_set = {0 ,1}  # 重新复制,相当于删除了老的便签变量
    
    print(wref())
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第八章/t8_17.py
    2
    {0, 1}
    2
    <weakref at 0x10d0976b0; dead> is deleting
    None
    
    Process finished with exit code 0
    

     然后上在python 控制台写的代码:

    /usr/local/bin/python3.7 /Applications/PyCharm.app/Contents/helpers/pydev/pydevconsole.py --mode=client --port=54889
    import sys; print('Python %s on %s' % (sys.version, sys.platform))
    sys.path.extend(['/Users/shijianzhong/study', '/Users/shijianzhong/study/Fluent_Python/第六章'])
    Python 3.7.4 (default, Jul  9 2019, 18:13:23) 
    Type 'copyright', 'credits' or 'license' for more information
    IPython 7.7.0 -- An enhanced Interactive Python. Type '?' for help.
    PyDev console: using IPython 7.7.0
    Python 3.7.4 (default, Jul  9 2019, 18:13:23) 
    [Clang 10.0.1 (clang-1001.0.46.4)] on darwin
    import weakref
    import sys
    a_set = {0, 1}
    func = lambda x: print(repr(x), 'is deleting')
    wref = weakref.ref(a_set, func)
    print(sys.getrefcount(a_set))
    2
    wref()
    Out[3]: {0, 1}
    sys.getrefcount(a_set)
    Out[4]: 9
    

     对象的便签一下子变成了9,那就没有后面的事情了。

    后面weakref提供

    In [225]: weakref.WeakKeyDictionary                                                                
    Out[225]: weakref.WeakKeyDictionary

    In [226]: weakref.WeakValueDictionary                                                              
    Out[226]: weakref.WeakValueDictionary

    In [227]: weakref.WeakSet                                                                          
    Out[227]: _weakrefset.WeakSet
    三个模块,我自己用了一下WeakSet的模块,还是蛮有意思的,在一个WeakSet里面添加一个类属性,把所有还活着的对象在这个WeakSet里面,可以方便的查看活着的实例。

    from weakref import WeakSet
    
    
    class My_Demo:
        instances = WeakSet()
    
        def __init__(self,name):
            self.name = name
            My_Demo.instances.add(self)      # 初始化的时候,把实例放入WeakSet()里面
    
    
    my1 = My_Demo('my1')
    my2 = My_Demo('my2')
    del my2                 # 删掉一个实例
    my3 = My_Demo('my3')
    print(My_Demo.instances)
    print(my1.instances)    # 通过实例当然也可以调用类属性。
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第八章/t8_19_1.py
    <_weakrefset.WeakSet object at 0x107f1e3d0>
    <_weakrefset.WeakSet object at 0x107f1e3d0>
    
    Process finished with exit code 0
    

    本章小结(个人就摘录了两点):

    简单的赋值不创建副本

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

    本来的理解,书中我把变量,变量名,别名,可以理解为同一个事物,都在=的左边。

    =的右边是对象,是先与=的左边创建的。

    =左边的是在对象上面贴标签,也可以认为指向了对象,或者像是对象的快捷方式。

    我们不能直接删除对象,只能删除=号左边的,等把对象上面的所有便签都撕掉了,该对象也就升天了。

    所以以后在写程序时,一些不用的对象可以del掉,避免浪费内存。

  • 相关阅读:
    通过另外一个应用程序给多个文本框赋值, 模拟单击事件
    AngularJS
    九章算法
    实现继承
    二分查找
    NET Core依赖注入解读&使用Autofac替代实现
    NET SignalR 与 LayIM2.0
    WebVR
    依赖注入
    如何实现配置与源文件的同步
  • 原文地址:https://www.cnblogs.com/sidianok/p/12094479.html
Copyright © 2020-2023  润新知