• python中一切皆对象,命名皆引用


    先了解Python中自带的一个函数id(),这是一个很有用的函数:id()函数返回对象的内存地址,可以用来唯一标识对象。

    1. Python中一切皆对象,数值、字符串、函数、列表、类、文件等都是对象,加载到内存中时会给这些对象分配一些内存资源,可以通过id()函数来表示它们的内存地址。

    string = "hello python"
    alist = [1, 2, 3]
    def func():
        return 0
    class Dog(object):
        pass
    
    print(id(string))
    print(id(alist))
    print(id(func))
    print(id(Dog))
    
    """
    output:
    2998789135984
    2998789677376
    2998789780528
    2998757580832
    """
    

    2. Python在使用变量之前无须定义它的类型,但是必须声明以及初始化该变量。同一变量名可以(在不同阶段)代表不同类型的数据。

    i = 1   
    print(i, type(i), id(i))
    i = 10000000000
    print(i, type(i), id(i))
    i = 1.1
    print(i, type(i), id(i))
    
    """
    output:
              1   <class 'int'> 140736383850144
    10000000000   <class 'int'>   1871879428400
            1.1 <class 'float'>   1871879428048
    """
    

       和静态类型语言(如C等)有很大不同。静态语言只要一个变量获得了一个数据类型,它就会一直是这个类型,变量名代表的是用来存放数据的内存位置。

       而Python中使用的变量名只是各种数据及对象的引用,即Python中的变量名等同于C++中的左值引用名

    Type &引用名 = 左值表达式;
    

       可以用id()获取的才是存放数据的内存位置,我们输入的1、10000000000和1.1三个数据均会保存在id()所指示的这些内存位置中,直到垃圾回收车把它

       拉走(在系统确定你不再使用它的时候)。这是动态语言的典型特征,它确定一个变量的类型是在给它赋值的时候。

       即一切命名是引用。

    3. 一个对象与多个引用

       1)间接引用的情况

    a = 1
    b = a  // 间接引用 1
    c = b  // 间接引用 1
    d = a  // 间接引用 1
    
    print(id(a))
    print(id(b))
    print(id(c))
    print(id(d))
    
    """
    output:
    140736383850144
    140736383850144
    140736383850144
    140736383850144
    """
    

          由输出可知,a,b,c,d所引用的内存空间地址都是一样的

         

          经过测试将a = 1改为a = "hello python",a = [1, 2, 3],a = (1, 2, 3)等其它对象,输出的内存地址都是一样的。

          这是间接引用的情况,我们可以得到这么一个结论:无论什么数据类型,通过间接引用(引用与引用之间的传递),都指向同一个内存地址。

       2)接下来我们来看看直接引用

    a = 1
    b = 1
    print(id(a))
    print(id(b))
    
    """
    output:
    140736372512416
    140736372512416
    """

          经过测试对于string,number类型的变量,分别直接引用所指向的内存空间都是一样的。

          但是当类型变成list,dict时就不同了:

    a = [1, 2, 3]
    b = [1, 2, 3]
    print(id(a))
    print(id(b))
    
    """
    output:
    2522481549248
    2522481282496
    """
    

          分别直接引用相同的 list 对象,输出的内存地址是不一样的。

          经过测试知道:对于list、set、dict这样的对象,分别创建多个相同值的引用,并不是指向同一个内存地址,也就是说每定义一个list对象,

                        会分别在内存上开辟出不同的空间存放。

          结论:这个跟Python的内存机制有关,对于语言来说,频繁地创建销毁对象,会很影响机器性能。

          像number、string等数据类型在定义的时候,无论你定义多少个相同值的对象,也不管你是定义在函数体内的局部变量,还是函数外的全局变量,

          在Python中都只开辟一块内存空间,即所有的引用都指向的是同一个对象。

          对于像list、set、dict这样的“容器对象”,在Python中,你定义了几个对象,实际就为你创建几个,分别开辟不同的内存空间存储(不同的内存地址)。

    a = "hello"
    print(id(a))
    def func():
        b = "hello"
        print(id(b))
    func()
    
    """
    output:
    2930713419376
    2930713419376
    """

          注意:直接引用中存在一个比较特殊的类型,那就是tuple,先看一个例子

    a = (1,2,3)
    b = (1,2,3)
    print(id(a))
    print(id(b))
    
    """
    output:      // 内存地址相同
    2350039937344
    2350039937344
    """
    
    a = (1,2,[1,2])
    b = (1,2,[1,2])
    print(id(a))
    print(id(b))
    
    """
    output:      // 内存地址不同
    2350069670528
    2350040112448
    """
    

           对于tuple,我们认为当其元素含有可变数据类型时,它其实是可变的,反之为不可变的。

           对直接引用,我们总结一下:对于可变数据类型(含可变tuple),会分别开辟内存,对于不可变数据类型(含不可变tuple),只会开辟一块内存。

      

    4. 可变类型和不可变类型

       1)不可变类型对象的值是固定的,不可以被更改,如果需要存储一个新的值得话,会在内存中创建一个新的对象,并修改引用。

          不可变类型对象,包括bool, number,string,tuple(特殊)等。

          不可变类型对象扮演着重要角色,比如在字典中key的值必须是不可变类型的,比如像列表这样的对象,是不能作为key值的。

    a = 1
    print(id(a))
    a += 1
    print(id(a))
    
    """
    output:
    140736383850144
    140736383850176
    """
    

        上面例子中,a 加1前后的地址发生了变化,因为+1后指向了新的对象。

       2)可变类型的对象,值是可以改变的,但是内存地址不会改变。

    alist = [1, 2, 3]
    print(id(alist))
    alist[2] = 10
    print(id(alist))
    
    """
    output:
    1974126305088
    1974126305088
    """

          修改alist的元素,alist对象的地址没有发生改变。

          可变对象的内部每个元素的类型又可以是不可变对象和可变对象,其内存分配情况也是一样的。

          tuple(元组)虽然是不可变类型对象,但是可以修改其元素值(该元素类型得为可变类型),但tuple对象地址依然是不变的

    t = ('a', 'b', ['A', 'B'])
    print(id(t))
    t[2][0] = 'X'
    t[2][1] = 'Y'
    print(id(t))
    
    """
    output:
    1708397867328
    1708397867328
    """
    

          可以从指针角度来理解,其内存布局大概是下面这样子的,修改后变成下面右边这张图。

                 

          表面上看,tuple 的元素确实变了,但其实变的不是 tuple 的元素,而是list 的元素。tuple 一开始指向的 list 并没有改成别的 list,所以,tuple

          所谓的“不变”是说,tuple 的每个元素,指向永远不变。即指向'a',就不能改成指向'b',指向一个 list,就不能改成指向其他对象,但指向的这个 list 本身是可变的!

    5. 对函数传递参数做一个分析

       理解了上面的内容后,我们就知道:

       1)如果是间接引用,则内存地址相同。

       2)如果是直接引用,则不可变类型对象(含不可变tuple)在内存中只创建一个,可变类型对象(含可变tuple)在内存中会创建多份。

       函数的参数传递当然也包括直接引用和见直接引用,直接来看几个例子。

    def func(t):
        print(id(t))
    
    a = [1,2,3,4,5]
    print(id(a))
    func(a)
    
    """
    func()是间接引用[1,2,3,4,5],输出地址相同
    2657154588800
    2657154588800
    """
    
    print(id(a))
    func([1,2,3,4,5])
    
    """
    func()是直接引用[1,2,3,4,5],输出地址不同
    2657154588800
    2794855255488
    """
    
  • 相关阅读:
    c3p0死锁
    空间分析过程
    UUID.randomUUID().toString() 的作用
    ajax做的一些总结
    vue3组合式api
    引入高德地图
    高德地图做标记
    页面刷新回到顶部
    高德地图如何只显示中国地图,不显示国外地图
    vue使用高德地图错误 ‘AMapUI‘ is not defined , ‘AMap‘ is not defined 问题及解决。
  • 原文地址:https://www.cnblogs.com/yanghh/p/13130176.html
Copyright © 2020-2023  润新知