• python 内存那些事


    一、可变对象和不可变对象

    1、不可变对象:字符串对象,数值(整数,浮点数,复数)对象,元组对象

       可变对象:列表对象,字典对象,集合对象

    所谓可变不可变即可不可以在其对应内存id处做修改。

    所谓类型都是对于对象而言的,而不是变量。所谓变量,是对对象的引用。

    a=1 (id(a)=31269208)翻译:在内存某处创建了一个整数对象,其内存id为31269208,变量a是一个对这个整数对象的引用,且这个整数对象是不可变对象,即不能在内存中做修改。

    对于函数,传入参数,本质是复制一份该参数的引用,若该参数指向不可变对象,函数的引用和外部的引用没啥关系。若该参数指向可变对象,函数的引用若对内存做了修改,会影响到外部的引用,

    下面看一下例子:

    >>> a = 1
    >>> id(a)
    31269208
    >>> def fun(a):
    ...     print(a,id(x))
    ...     a = 2
    ...     print(a,id(x))
    >>> fun(a)
    >>> a
    1                     ----------------函数的引用和外部的引用没啥关系

    -------------------------------------------------------------------------------------------

    >>> a = [1]
    >>> id(a)
    140508532361480
    >>> def fun(a):
    ...     print(a,id(x))
    ...     a.append(2)
    ...     print(a,id(x))
    >>> fun(a)
    >>> a
    [1,2]                 ----------------函数的引用对内存做了修改,影响到了外部的引用

    二、浅拷贝和深拷贝

    1、从内存id说深浅拷贝

    >>> import copy
    >>> a = [1,2,3,4,['a','b','c']]
    >>> id(a)
    139972700143128
    >>> b = a          ----这里是引用的传递,可以看到id不变
    >>> id(b)
    139972700143128
    >>> c=copy.copy(a)     ----浅拷贝,id变了
    >>> id(c)
    139972700332832
    >>> d=copy.deepcopy(a)   ------深拷贝,id变了
    >>> id(d)
    139972700224976      
    ----------------------------------------------------------------------------------

    接下来想要看一下列表中的每一个元素的内存id    

    a = [1,2,3,4,['a','b','c']]
    >>> for i in a:  ---前面四个值是四个整数对象的内存id,注意当你进入python的时候,整数对象的id就固定了。列表对象不是哦
    ...     id(i)
    ...
    31269208        ----整数对象1

    31269184        ----整数对象2
    31269160        ----整数对象3
    31269136        ----整数对象4
    139972700141832 ----列表对象['a','b','c']
    >>> for i in b:   
    ...     id(i)
    ...
    31269208      ---跟a都相同,不愧是引用传递
    31269184
    31269160
    31269136
    139972700141832
    >>> for i in c:
    ...     id(i)
    ...
    31269208      ---浅拷贝,也跟a都相同,解释了为啥浅拷贝,a中的列表做了修改,c会相应变化。
    31269184
    31269160
    31269136
    139972700141832
    >>> for i in d:
    ...     id(i)
    ...
    31269208      ---深拷贝,发现a中的列表对象['a','b','c']对应的内存id变了,这也是为啥a中的列表做了修改,d仍岿然不动
    31269184
    31269160
    31269136
    139972700333120

    -----------------------------------------------------------------------------------------------

    2、自己写的列表深拷贝

    带着上述从内存层面对深浅拷贝的理解,对于深拷贝,我只需新建一个空列表,其一定对应一个新的内存id,然后一个个把原列表中的元素添加进去即可。

    当然这里面用了递归的思想(递归--从形式来说,函数里有自个儿!)

    当然我只写了列表的深拷贝,感兴趣的可以读一读python的源码。

    而且我没考略对象指向自身,会出现的无限循环情况。解决办法:做一个字典memo={},里面key为对象内存id

    import copy
    def deepcopy_list(thislist):
    new_list = [] ---------新列表,新id
    for i in thislist:
    if isinstance(i,list):
    new_list.append(deepcopy_list(i)) ----递归
    else:
    new_list.append(i)
    return new_list

    a = [1,[2,[3],4],5]
    c = copy.copy(a)
    b = deepcopy_list(a)
    d = copy.deepcopy(a)
    a[1].append('shibushi')
    print('a=%s b=%s c=%s d=%s' % (a,b,c,d))

    三、反射 getattr

    1、反射的魅力在于把字符串反射成了built-in function or method

    反射的魅力在于这个字符串可以是变的,因此一个表达式你可以反射到不同的函数上。

    例如:字符串列表

    for attr in dir(self):       ---die 返回类的所有方法,以字符串的列表的形式

        if not attr.startswith('_'):

            value = getattr(self, attr)

    例如:

    func = getattr(self,self.sys_args[1])
    # sys.argv是用来获取命令行参数的, sys.argv[0]表示代码本身文件路径
    # linux 命令行输入‘test.py -a -b‘ sys.argv返回一个列表(字符串列表),列表的第一个元素是路径,接着是参数

    通过反射完美的把你输入的不同的命令,反射到不同的函数上。

    例如:

    data_calc_fun = getattr(self, 'get_%s' % self.expression_obj.data_calc_func)
    self.expression_obj.data_calc_func---expression-obj是一个Django表类,data_calc_func是类中的属性,你可以在Django admin视图中更改,通过反射,可以反射到不同的函数上

    2、函数名相当于是一个引用,指向一个内存中的函数对象

    a = getattr([],'append')
    print(type(a)) -------- <type 'builtin_function_or_method'>
    print(a) ------<built-in method append of list object at 0x7f7a183fb830> 相当于append的另一个名字
    def fun():
    a =2
    return a
    print(type(fun)) ---<type 'function'>
    print(fun) ----<function fun at 0x7f7a184095f0>

    四,python2中的range和xrange

    range(1个亿)------创建1亿个元素的列表,全保存在内存里,这样内存是不是炸了

    xrange(1个亿)------创建一个生成器,当你用的时候,一个元素一个元素的取,节省了很大的内存资源,因为你不需要一上来就开辟一个很大的内存空间。

    五、可迭代对象,迭代器,生成器

    1、一些概念

    可迭代对象的本质是对象里有iter这个方法

    可迭代对象有列表,字典,字符串,集合,迭代器

    迭代器本质是里面多了个next这个方法,生成器是特殊的迭代器

    简而言之,可迭代对象就是可以迭代(for)的对象,迭代器是可以迭代  可迭代对象 的对象。

    2、迭代器,生成器相对于其他可迭代对象最重要的优势在于对内存的节省。

    一个一亿个元素的列表在内存就炸了。

    一个可以迭代这一亿个元素的迭代器对象就可以很好的保存在内存中,当你想要用的时候,直接用就好,你可以通过next方法;也可以对其做for,做for的时候,用一个扔一个,始终保留很小的内存占用,当然你只能for这么一次......

    六,一些小东西

    # 返回一个key的列表
    list = {'data':{'status':0,'iowait':'0.17'}}
    data = list['data'].keys()
    print(list)
    l = [1,2,3,4,5]
    print(l[0:-1])
    a = '600,3'.split(',') # []
    print(a)
    ll = [1,'a']
    q,b = ll # 很帅气的列表变量赋值
    print(q)
    print(b)
  • 相关阅读:
    Android特色开发(3):Google Map
    Android特色开发(4):桌面组件
    Android用户界面开发(19):ProgressBar
    每日英语:Fewer Foreigners Eye US Graduate Science Programs
    每日英语:My Adventures With Liquid Chicken
    每日英语:Hard Math: Adding Up Just How Little We Actually Move
    每日英语:Thousands of Thumbs Down for Chinese Red Cross
    每日英语:Rescuers Struggle to Reach Quake Victims
    每日英语:When Simplicity Is The Solution
    【转】Matlab图像处理函数:regionprops
  • 原文地址:https://www.cnblogs.com/mengmengzhang/p/9692410.html
Copyright © 2020-2023  润新知