• Python 类、对象、数据分类、函数参数传递的理解


      最近在基于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 ChangeString(s):
        s = '我是不可变类型,传递给函数'
    
    s = '我是不可变类型'
    print (s)
    ChangeString(s)
    print (s)
    
    >> 我是不可变类型
    >> 我是不可变类型
      如代码所示,s是字符串,属于不可变类型,传递给ChangeString(s)时,是将s实际的值传入,s本身不会被改变。

    3.2 引用传递

      主函数向调用函数传递的参数是可变类型时,实际上是将实参的引用传入了调用函数,对引用的操作等于对其指定的对象进行操作。
    def ChangeList(list1):
        list1[1] = 5
    li = [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、不可变类型参数被函数调用时,是值传递,可变类型参数被函数调用时,是引用传递。
     
     
     
     
     
  • 相关阅读:
    JAVA假期第五天2020年7月10日
    JAVA假期第四天2020年7月9日
    JAVA假期第三天2020年7月8日
    JAVA假期第二天2020年7月7日
    JAVA假期第一天2020年7月6日
    CTF-sql-group by报错注入
    CTF-sql-order by盲注
    CTF-sql-sql约束注入
    CTF-sql-万能密码
    X-Forwarded-for漏洞解析
  • 原文地址:https://www.cnblogs.com/cheerjude/p/10151844.html
Copyright © 2020-2023  润新知