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
"""

 

是否创建了新的引用,是否创建了新的对象。

posted @ 2020-06-15 12:45  _yanghh  阅读(622)  评论(0编辑  收藏  举报