Python 类,对象,数据分类,函数参数传递详解

2022-07-21,,,,

目录

    最近在基于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、不可变类型参数被函数调用时,是值传递,可变类型参数被函数调用时,是引用传递。

    本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注的更多内容!

    《Python 类,对象,数据分类,函数参数传递详解.doc》

    下载本文的Word格式文档,以方便收藏与打印。