最近在基于python写的接口自动化脚本,从Excel表中读取所有数据,每一行数据保存为字典,再将很多行的字典数据保存到一个列表里,运行时发现,列表中的字典均相同,且一直是excel最后一行的数据,情况类比如下:
dd = {"a":1,"b":10} i = 2 list1 = [] while i< 5: dd["a"] = i i+=1 list1.append(dd) print("list1:{}".format(list1))
运行结果如下图,打印结果并不是预期的 list1:[{"b": 10, "a": 2}, {"b": 10, "a": 3}, {"b": 10, "a": 4}] ,为什么呢??
问题的关键在于,数据分为可变及不可变类型,python中字典是可变类型,列表实际保存的是字典所指向的那片内存,而这片内存的内容,保存的是最后一次修改的值。
为加深理解,重新温故下python关于类、对象、数据分类、函数传递参数的相关知识。
1、基本概念
1.1 类与对象的关系
对象:包含属性(特征)和方法(行为)。例如,狗作为一个对象,有年龄、眼睛等特征,有走路、觅食等行为。
类:即把有相同属性和方法的对象进行提取(抽象化),是对象的模板。例如,对狗这个对象进行抽象化,把具有觅食行为,具有年龄等相同特征的对象,抽象一个Animal类。1.2 self的作用
class Animal(): self.age = 0 #类属性 def Eat(self): #类方法 print ("觅食") dog = Animal() #类的实例化,即对象 cat = Animal() #类的实例化,即对象 dog.Eat() #相当于Animal.Eat(dog)
在python里,当对象调用类中的方法时,需要先把对象作为参数传入方法中,相当于告诉类,“老子来调用这个方法啦,留个名”。而对象把自己传入方法,就是通过self。
如上图,dog对象调用Eat(self)方法,执行dog.Eat()时,先用self接收dog对象,等同于执行Animal.Eat(dog)。
正是因为self参数接收的是对象本身,而self的英文翻译就是“自己,自个”,所以大家都约定俗成的用了这个单词,它并不是python的关键字,如果换成把self缓存that,here等,其实也一样。
1.3 对象的创建与引用
在python中,一切都是对象。对象均具备三个属性:地址,类型,值。
当"左边 = 右边"时,实际是创建、引用对象的过程。
如a = 3, 3实际上是一个对象,且对象的值为3,对象创建后存储在内存中,被a所引用。
如果对象中的值可以更改,该值属于可变类型;
如果对象中的值不能更改,该值属于不可变类型;
如果对象中又包含对其他对象的引用,该对象就是容器,如d={},list1[0]=d,list1就是容器。
2、数据的分类
数据可变不可变,指的是存储在内存的内容,即对象的值,是否可以被修改,分为俩大类:
- 不可变类型:例如整型,浮点型,字符串类型;
- 可变类型:例如字典,列表。
2.1 不可变类型
不可变类型,即对象本身的值不可以改变。
python在引用不可变类型的对象时,会寻找该对象是否创建过,若该对象已创建,则变量会直接引用该对象,不会再申请新的内存空间。
a = 3 b = 3 print(id(a)) print(id(b)) a = 4 print(id(a)) >> 502853488 >> 502853488 >> 502853520
3这个对象创建后,a、b都引用了它,所以打印出来的地址是相同的。
当a = 4后,因为3属于不可变类型,因此又创建了一个4的对象,将a指向这个新创建的对象。
2.2 可变类型
可变类型,即在对象本身的值允许改变,而内存地址不需要改变,如 列表.append。
python在引用不可变类型的对象时,会先申请新的内存空间,来存储这个对象,有别于不可变类型。
a = [1,2,3] b = [1,2,3] print(id(a)) print(id(b)) a.append(4) print(id(a)) >> 48751048 >> 48751560 >> 48751048
a、b创建了俩个相同内容的列表,但是其指向的内存地址不相同。当对a指向的可变对象增加元素后,a所引用的对象内容已改变,但地址依旧不变。
3、函数传递参数的方式
3.1 值传递
主函数向调用函数传递的参数是不可变类型时,实际上只是将实参的拷贝(即临时副本)传递给了被调用函数,并不是实参本身,这样被调函数不能直接修改主调函数中变量的值,而只能修改其私有的临时副本的值。
def ChangeList(list1): list1[1] = 5 li = [1,1,1] print (li) ChangeList(li) print (li) >> [1, 1, 1] >> [1, 5, 1]
如代码所示,s是字符串,属于不可变类型,传递给ChangeString(s)时,是将s实际的值传入,s本身不会被改变。
3.2 引用传递
主函数向调用函数传递的参数是可变类型时,实际上是将实参的引用传入了调用函数,对引用的操作等于对其指定的对象进行操作。
def ChangeList(list1): list1[1] = 5li = [1,1,1] print (li) ChangeList(li) print (li) >> [1, 1, 1] >> [1, 5, 1]
如代码所示,li是列表,属于可变类型,传递给ChangeList(list1)时,list1也指向了li所引用的同一片内存。
总结
1、类是对象的抽象化,对象是类的实例化,在python中一切都是对象。
2、self代表的是对象本身,将对象作为一个参数传入方法中执行。
3、内存中的内容按是否可以修改,分为可变类型和不可变类型,所对应的可变对象和不可变对象,创建和引用方式也不同。
4、不可变类型参数被函数调用时,是值传递,可变类型参数被函数调用时,是引用传递。
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注服务器之家的更多内容!
原文链接:https://www.cnblogs.com/cheerjude/p/10151844.html