• Python面向对象总结一


    还记得第一次接触Python是在刚刚进入大学的第一个学期,之后就没有再使用过python。虽然基本语法,内置函数等没有忘记,但最近的学习中,一直在用python、matlab混合编程,零零散散的知识点、模模糊糊的记忆,有时候令我眼花缭乱。现在趁着闲暇,系统的温习一下python的基础知识。


    目录

    一、初识对象

    二、self参数详解

    三、变量的划分(类变量、实例变量、局部变量)

    四、实例方法、静态方法、类方法


    一、初识对象

    我依稀记得,大一上python的某堂课,任课老师让我们用一段python程序描述班级中学生的信息。

    A同学回答:我的程序是这样的

    1. student_1={
    2. "姓名":sun,
    3. "性别":男,
    4. "籍贯":河南,
    5. 年龄:18
    6. }

    A同学选择使用字典来描述信息。

    B同学回答:我的程序是这样的

    student_2=["王五","南京","男",19]

    他使用列表描述学生信息。

    C同学回答道:我选择使用字符串描述信息

    student_3="张柳来自福建省,他是男生,今年19岁"

     不难看出,由于不同的同学采用不同数据类型的变量来描述学生信息,使得数据很乱。我们有没有什么其他办法来统一数据格式,使得学生信息看起来整齐有序呢?

    我们发现要描述一个学生的信息,形式是多样的(比如使用列表,字典等),但是无论是什么形式都需要包含姓名,年龄,性别,籍贯几个要素。不同的学生的姓名、年龄等个不相同,但是要描述清楚这个学生的信息,总得有这几个要素。如果我们使用一个“模板”,这个“模板”中包含姓名、性别、年龄、籍贯这几个要素,那么这个“模板”肯定能描述清楚任何一个特定学生的信息。因为任何一个特定的学生信息都是从这个“模板”中得来的,数据格式就变得整齐有序了。

    上面这个过程其实就是使用对象来组织数据,其形式如下:

    1. class Student:
    2. name:None
    3. age:None
    4. sex:None
    5. birthPalce:None

     那么A、B、C三位同学的程序就可以统一为:

    1. stu_1=Student()
    2. stu_1.name="sun"
    3. stu_1.age=18
    4. stu_1.sex="男"
    5. stu_1.birthPalce="河南"
    6. stu_2=Student();
    7. stu_2.name="王五"
    8. stu_2.age=19
    9. stu_2.sex="男"
    10. stu_2.birthPalce="南京"
    11. stu_3=Student();
    12. stu_3.name="张柳"
    13. stu_3.age=19
    14. stu_3.sex="男"
    15. stu_3.birthPalce="福建"

     在上面的几段程序中

    class Student:
        name:None
        age:None
        sex:None
        birthPalce:None 

    称为设计类。其中name,age,sex等称为类的属性,它是用来描述类的特征。比如,一个学生年龄19岁,籍贯河南,姓名李华,这里的年龄、籍贯、姓名就是这个学生的特征(或者称为属性),而19岁、河南,李华就是对应属性的值。又比如,丽丽长的很漂亮,这里丽丽的属性就是“外貌’,而对应的属性值是“漂亮”。其实,要全面描述清楚一个学生,单凭使用“年龄”“姓名”“外貌”等属性是描述不清的,一个学生还可以学习、社交、考试等等,我们无法使用属性来解释学生的这些行为。这时候就要使用“方法”来描述了,现在我们来拓展一下上面的学生类,在类中添加学生的“行为”:

    class Student:
        name:None
        age:None
        sex:None
        birthPalce:None 

        def study(self):

                print(f"大家好,我是{self.name},我非常擅长数学,如果有数学难题,欢迎一起探讨") 

     我们在类中定义了一个函数"study"来描述学生的学习行为,这里在类中定义的函数就称为类的成员方法,它是用来描述类的功能。在方法定义的参数列表中,有一个self关键字,它是用来表示类对象自身的意思当我们使用类对象调用方法的是,self会自动被python传入。关于self参数,在本篇文章的下一个章节会详细介绍。

    在上面的程序段中:

    1. stu_1=Student()
    2. stu_2=Student();
    3. stu_3=Student();

    称为创建对象。为什么非要创建对象才能使用呢?类是程序中的“设计图纸”,而对象是基于图纸生产的具体实体,只要创建出对象,才能用实体去实现类的功能。

    类只是一种程序内的“设计图纸”,需要基于图纸生产实体(对象),才能正常工作这种套路,称之为:面向对象编程。面向对象编程就是根据实体抽象出共性,设计出类,基于类创建对象,由对象做具体的工作。

    我们继续看上面的程序段:

    stu_1.name="sun"

    stu_1.age=18

    stu_1.sex="男"

    stu_1.birthPalce="河南"

    stu_2.name="王五"

    stu_2.age=19

    stu_2.sex="男"

    stu_2.birthPalce="南京"

    stu_3.name="张柳"

    stu_3.age=19

    stu_3.sex="男"

    stu_3.birthPalce="福建"

     称为对象属性赋值。但是在这段对象属性赋值代码中,为对象的属性赋值需要依次进行,略显繁琐。有没有更加高效的方式就完成呢?

    答案是肯定的,我们完全可以使用构造方法对成员变量进行赋值。

    1. class Student:
    2. def __init__(self,name,age,sex,birthPlace):
    3. self.name=name
    4. self.age=age
    5. self.sex=sex
    6. self.birthPlace=birthPlace

    Python类可以使用:__init__()方法,称之为构造方法。
    它可以实现:
    在创建类对象(构造类)的时候,会自动执行。
    在创建类对象(构造类)的时候,将传入参数自动传递给__init__方法使用。借此特性可以给成员变量赋值
    值得注意的是:构造方法名称:__init__ 千万不要忘记init前后都有2个下划线,构造方法也是成员方法,不要忘记在参数列表中提供:self

    二、self参数详解

    self 参数的具体作用是什么呢?举一个例子,如果把类比作造房子的图纸,那么类实例化后的对象是真正可以住的房子。根据一张图纸(类),我们可以设计出成千上万的房子(类对象),每个房子长相都是类似的(都有相同的类变量和类方法),但它们都有各自的主人,那么如何对它们进行区分呢?当然是通过 self 参数,它就相当于每个房子的门钥匙,可以保证每个房子的主人仅能进入自己的房子(每个类对象只能调用自己的类变量和类方法)。也就是说,同一个类可以产生多个对象,当某个对象调用类方法时,该对象会把自身的引用作为第一个参数自动传给该方法。

    在上面这段代码中,我们使用Person类创建了两个不同的对象,打印构造方法中的self发现也不同,这一点也恰好印证了:无论是类中的构造函数还是普通的类方法,实际调用它们的谁,则第一个参数 self 就代表谁。

    在上面的章节中我们通过生活中的例子,初步认识了对象,学到了如何定义一个类,知道了类中可以包含属性(变量)与方法(函数)。下面我们继续学习在类中定义的变量与函数,加深对python类的认识。

    三、变量的划分(类变量、实例变量、局部变量)

    类变量是定义在类内,但在函数外部的变量。

    实例变量是定义在类的函数体内,并且是以“self.变量名”定义的变量。

    局部变量是定义在类的函数体内,但不是以“self.变量名”定义的变量。

    3.1类变量

    1. class Person:
    2. name = "sun" # 这是一个类变量
    3. def __init__(self, age):
    4. self.age = age # 这是一个实例变量
    5. def talk(word):
    6. words = "Hi"+word # 这是一个局部变量
    7. print(words)
    8. p1 = Person(12)
    9. p2=Person(18)
    10. #类变量的调用方式
    11. print(Person.name)#调用类变量的第一种方式:类名.变量名
    12. print(p1.name)#调用类变量的第二种方式:对象名.变量名
    13. #类变量为所有实例共享
    14. print(p1.name)
    15. print(p2.name)
    16. #类变量的修改方式
    17. Person.name="张三"#通过类名.变量名的方式对类变量重新赋值
    18. print(Person.name)#类变量的值变为"张三"
    19. print(p1.name)#用对象p1调用类变量发现值变为"张三"
    20. print(p2.name)#用对象p2调用类变量发现值也变为“张三”

    在上面的这段代码中name为类变量。并且类变量具有两种调用方式:(1)类名.变量名(2)对象名.变量名。我们还可以通过类名.变量名的方式对类变量的属性值进行重新赋值修改。但是由于类变量为所有实例对象共享,修改类变量后,通过两个不同的实例对象重新调用name类变量,发现变量值也发生改变。

    还有一点需要注意的是,通过类对象是无法修改类变量的。通过类对象对类变量赋值,其本质将不再是修改类变量的值,而是在给该对象定义新的实例变量。看如下代码:

    1. class Student:
    2. name="王同学"
    3. def __init__(self,age):
    4. self.age=age
    5. s1=Student(16)
    6. s1.name="王五"
    7. print(s1.name)
    8. print(Student.name)

    王五
    王同学

    不仅可以通过类名.变量名的方式修改类中已有的类变量,还可以动态添加没有的类变量。

    在上段代码中添加以下代码:

    1. class Person:
    2. name = "sun"
    3. def __init__(self, age):
    4. self.age = age
    5. p1 = Person(12)
    6. p2=Person(18)
    7. Person.sex="男"
    8. print(Person.sex)
    9. print(p1.sex)
    10. print(p2.sex)

     运行结果



    3.2 实例变量 

    下面的代码中age,name为实例变量,其中,由于 __init__() 函数在创建类对象时会自动调用,而 learn() 方法需要类对象手动调用。因此,Person类的类对象都会包含 age实例变量,而只有调用了 learn() 方法的类对象,才包含 name实例变量。它们都可以通过对象名.变量名的方式调用

    1. class Person:
    2. def __init__(self, age):
    3. self.age = age # 这是一个实例变量
    4. def learn(self):
    5. self.name="sun"#这也是一个实例变量
    6. #实例变量调用方式:对象名.变量名
    7. p1=Person(18)
    8. p2=Person(19)
    9. print(p1.age)
    10. print(p2.age)
    11. #p2对象调用learn方法后再调用name属性
    12. p2.learn()
    13. print(p2.name)
    14. #p1对象在没有调用learn方法时直接调用name
    15. print(p1.name)#直接报错

    18
    19
    sun
    Traceback (most recent call last):
      File "e:\VsCodeWorkSpace\PythonProjects\PythonDemo\vari.py", line 76, in
        print(p1.name)#直接报错
    AttributeError: 'Person' object has no attribute 'name' 

    值得注意的是,如果类变量与实例变量同名,它会首选实例变量。看下面的例子

    1. class Student:
    2. name="王同学"
    3. def __init__(self,name):
    4. self.name=name
    5. s1=Student("王五")
    6. print(s1.name)
    7. print(Student.name)

    王五
    王同学

    另外,和类变量不同,通过某个对象修改实例变量的值,不会影响类的其它实例化对象,更不会影响同名的类变量。例如:

    1. class Person:
    2. def __init__(self, age):
    3. self.age = age # 这是一个实例变量
    4. p1=Person(18)
    5. p2=Person(19)
    6. print(p1.age)#18
    7. print(p2.age)#19
    8. #修改p1的age值
    9. p1.age=20
    10. print(p1.age)#20
    11. print(p2.age)#19

     可以看出,对p1对象的age修改值,并不会影响到p2的age

    3.3 局部变量

    下面的代码段中c就是一个定义在函数内部的局部变量

    1. class add:
    2. def sum(self,a,b):
    3. c=a+b
    4. print(c)
    5. s=add()
    6. s.sum(1,2)

    需要注意的一点是,局部变量只能用于所在函数中,函数执行完成后,局部变量也会被销毁。

    在这一章节,我们对python类中的属性(变量)进行了比较深入的分析,和类属性一样,类方法也可以进行更细致的划分,下面我们按照这个思路继续分析类中的方法(函数)

    四、实例方法、静态方法、类方法

     采用 @classmethod 修饰的方法为类方法;采用 @staticmethod 修饰的方法为静态方法;不用任何修改的方法为实例方法

    4.1 实例方法

    1. class math:
    2. def sum(self,var1,var2):
    3. return var1+var2

    上面这段代码中的sum方法就是一个实例方法,实例方法最大的特点就是,它最少也要包含一个 self 参数,用于绑定调用此方法的实例对象。实例方法通常可以用对象直接调用,也可以使用类名调用(但此方式需要手动给 self 参数传值)

    1. class Math:
    2. def sum(self,var1,var2):
    3. return var1+var2
    4. m=Math()
    5. print(m.sum(2,3))#使用对象调用实例方法
    6. print(Math.sum(m,2,6))#使用类名调用实例方法,但是必须传入对象本身

    值得注意的是,类的构造方法也是一种实例方法。

    4.2 类方法
    与实例方法相似,类方法最少也要包含一个参数,只不过类方法中通常将其命名为 cls,Python 会自动将类本身绑定给 cls 参数(注意,绑定的不是类对象)。也就是说,我们在调用类方法时,无需显式为 cls 参数传参。

    调用类方法的方式也有两种:(1)使用类名.方法名(2)使用对象名.方法名

    1. class Math:
    2. @classmethod
    3. def sum(cls,var1,var2):
    4. return var1+var2
    5. print(Math.sum(2,6))#使用类名调用类方法,但是不必显式传入类本身
    6. #也可以使用对象调用类方法
    7. m = Math()
    8. print(m.sum(2,3))

    值得注意的是,类方法需要使用@classmethod修饰符进行修饰,上面的sum方法如果不加@classmethod,python会将该方法认定为实例方法

    4.3 静态方法

    静态方法需要使用@staticmethod修饰,它没有类似 self、cls 这样的特殊参数,因此 Python 解释器不会对它包含的参数做任何类或对象的绑定。也正因为如此,类的静态方法中无法调用任何类属性和类方法。

    1. class Math:
    2. @staticmethod
    3. def sum(var1,var2):
    4. return var1+var2
    5. print(Math.sum(2,6))#使用类名调用静态方法
    6. #也可以使用对象调用静态方法
    7. m = Math()
    8. print(m.sum(2,3))

     到此,通过以上所有的章节我们系统地认识了python对象、类、类的属性、类的方法,有了这些基础,接下来我们继续探讨python封装继承与多态,由于篇幅有限,后续章节会在下一篇博客中介绍。

  • 相关阅读:
    工厂设计模式
    C++中带默认值的函数参数
    Puppeteer动态代理实战:提升数据抓取效率
    2023年天津天狮学院专升本市场营销专业《管理学》考试大纲
    开源与闭源:大模型发展的未来之路
    NSSCTF逆向题解
    “华为30岁以下员工仅占28%”上热搜,网友:说好的35岁天花板呢?
    Redis代码实践总结(二)
    ElasticSearch基础入门
    多目标海洋捕食者算法(MOMPA)(Matlab代码)
  • 原文地址:https://blog.csdn.net/qq_42681787/article/details/127931668