C++中的指针和引用 ,可以参考博客

https://www.cnblogs.com/heyonggang/archive/2012/12/13/2815730.html

问题导入?

如果你使⽤Python声明了⼀个变量并同时为其赋值,即a = 20,你是否将这条语句简单理解为:1. 计算机开辟了⼀块地址别名为a的内存;2. 往这块内存存储了数据20?


如果你对上述问题的回答为是,那么你对诸如“Python是⼀门动态类型语⾔”、“Python效率远不如C语⾔等静态语言效率高”等说法也⼀定不了解其本质。

问题解答:

变量由三部分组成:

标识,表示对象所储存的内存地址,使用内置函数id()获取

类型,表示对象的数据类型,使用内置函数type获取

值,表示对象所储存的具体数据,使用print打印输出。

在Python中:

1.变量和对象是分开储存的,
2.和其他编程语言一样的是,变量是对象内存地址的别名,即a代表了地址0x1002;
3.和其他编程语⾔不⼀样的是,Python中的变量和数据(对象)分开存储:变量a的地址0x1001处仅保存了数据20存储的内存地址0x1002。
4.变量a所标示的内存空间存储数据20所在内存地址的过程称为引⽤。

5.变量是对象的一个引用,对对象的操作都是通过引用来完成的

6.赋值操作 = 就是给对象添加一个引用

python中一切的传递都是引用(地址),无论是赋值还是函数调用,即python中所有的变量赋值、参数传递等都是引⽤传递。

python中数值类型(int和float),布尔型bool,字符串,元组都是不可变对象,列表list,字典dict,集合set都是可变对象,自定义的类对象也是可变对象 。对于不可变对象,我们⽆法在内存中直接修改这个变量(如 100,"student"),如果我们尝试对不可变类型进⾏修改,就会断开原始的引⽤,重新分配内存地址;修改可变对象的值不会断开原始引⽤,是直接在原始值上进⾏修改,因此这些和原始值有引⽤关系的变量的“值”都被修改了。

函数调⽤时,传递给函数的参数中保存的仍然是变量的引⽤(真实值的地址),函数参数是⼀个局部变量,初始时,参数和函数外部的值指向同⼀个内存地址。
- 如果参数是不可变对象,则断开原始引⽤,重新进⾏新的引⽤,函数外部的值不受影响
- 如果参数是可变对象,直接修改原始值,函数外部的值同步变化


- 解释器对常⽤的不可变类型进⾏了优化,把常⽤的数值、短字符串赋值给某些变量时,这些变量都指向相同的内存地址
 

 示例一:

a='123'

执行上面这个赋值语句的时候,Python解释器首先在内存中创建一个字符串"123"对象。之后在内存中创建一个名为"a"的变量,将"a"指向"123"(将"123"的地址保存到“a”中)

 接着执行

b=a#指向同一个对象

因为a已经存在,所以不会创建a,会创建变量"b",并将"b"指向"a"指向的字符串"123"

然后执行

a = "456"#不可变对象,对其进行修改时会断开原来的引用

a还是存在的,Python会新分配一块内存来存储新的值,会创建字符串"456", 然后将"456"的地址赋给"a"

 对于不可变对象,我们⽆法在内存中直接修改这个变量(如 100,"student"),如果我们尝试对不可变类型进⾏修改,就会断开原始的引⽤,重新分配内存地址.那么下面的代码就不难理解

a=100
print(id(a))#1888557553104
a=a+1
print(id(a))#1888557553136

对于可变对象,修改可变对象的值不会断开原始引⽤

lst=[1,2,3]
print(id(lst))
lst.append(4)#2167621434240
print(id(lst))#2167621434240

给出一段代码检验是否理解

a=100
b=a
a=a+1
print(a,b)
print(id(a),id(b))
lst1=[1,2,3]
lst2=lst1
lst1.remove(2)
print(lst1,lst2)
print(id(lst1),id(lst2))
'''
运行结果
101 100
2413522146800 2413522146768
[1, 3] [1, 3]
2413527186432 2413527186432
'''

解释器对常⽤的不可变类型进⾏了优化,把常⽤的数值、短字符串赋值给某些变量时,这些变量都指向相同的内存地址,即python中的驻留机制。

a=100
b=100
c=100
print(id(a),id(b),id(c))
lst1=[1,2]
lst2=[1,2]
lst3=[1,2]
print(id(lst1),id(lst2),id(lst3))
'''
运行结果
1872572732880 1872572732880 1872572732880
1872577903488 1872577900416 1872579060160
'''

函数参数

参数的传递本质上是一种赋值操作函数的参数也是一种引用传递。

def foo(arg):
    arg = 2
    print(arg)
 
a = 1
foo(a)  # 输出:2
print(a) # 输出:1

 

变量 a 指向 1,调用函数 foo(a) 时,arg=a,,这时两个变量都指向 1。在函数里面 arg 重新赋值为 2 之后,不可变对象不可修改时原先引用会断开,而 a的引用没变。因此 print(a) 还是 1。
再来看下一段代码

def bar(args):
    args.append(1)
 
b = []
print(b)# 输出:[]
print(id(b)) # 输出:4324106952
bar(b)
print(b) # 输出:[1]
print(id(b))  # 输出:4324106952

执行 append 方法前 b 和 arg 都指向同一个对象,执行 append 方法时,并没有重新赋值操作,也就没有新的引用过程,append 方法只是对列表对象插入一个元素,对象还是那个对象,只是对象里面的内容变了。因为 b 和 arg 都是绑定在同一个对象上,执行 b.append 或者 arg.append 方法本质上都是对同一个对象进行操作,因此 b 的内容在调用函数后发生了变化(但id没有变,还是原来那个对象)。

 python中+=在可变对象运算中的特殊用法

num = 100
def update(a):
        a+=10
        print(a)  #打印110
update(num)
print(num)  #打印100
num1 = 100
def update1(a):
        a=a+10
        print(a)  #打印110
update(num1)
print(num1)  #打印100
'''
python中所有的变量都是引用类型
num和update(a),实参num和形参a都指向同一片内存地址
a += 10
这里对a做出修改的操作
因为a是数值类型,属于不可变类型,不能修改
所以,python会创建一个临时变量a,用来存储110
所以print(num) 打印的仍然是100
'''
list = [2]
def test1(num):
        num += num
        print(num)   #打印[2,2]
test1(list)
print(list)   #打印[2,2]
#从结果而言,修改了可变类型的值
list2 = [3]
def test2(num):
        num = num + num  #num+num 的结果是[3,3] 这里表示将[3,3]这个列表赋值给num这个临时变量
        print(num)  #打印[3,3]
test2(list2)
print(list2)#打印[3]
'''
在python中 +=运算符表示对当前变量进行操作
并不完全等同于  +
'''



简单的加法中,列表的运算还是创建了一个新的列表对象;但在简写的加法运算+=实现中,则并没有创建新的列表对象。这一点要十分注意。

验证一下是否理解

def add_list(p):
    p = p + [1]
p1 = [1,2,3]
add_list(p1)
print p1
>>> [1, 2, 3]

def add_list(p):
    p += [1]
p2 = [1,2,3]
proc2(p2)
print p2
>>>[1, 2, 3, 1]

词典的引用

a = []
b = {'num':0, 'sqrt':0}
resurse = [1,2,3]
for i in resurse:
  b['num'] = i
  b['sqrt'] = i * i
  a.append(b)
print a
#输出 [{'num': 3, 'sqrt': 9}, {'num': 3, 'sqrt': 9}, {'num': 3, 'sqrt': 9}]

但我们实际想要的结果是这样的:

输出 [{'num': 1, 'sqrt': 1}, {'num': 2, 'sqrt': 4}, {'num': 3, 'sqrt': 9}]

这是由于a中的元素就是b的引用。可以修改为:

a = []
resurse = [1,2,3]
for i in resurse:
    a.append({"num": i, "sqrt": i * i})
print(a)
#输出[{'num': 1, 'sqrt': 1}, {'num': 2, 'sqrt': 4}, {'num': 3, 'sqrt': 9}]

呕心沥血之作,。。。。,看了好长时间
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐