python入门学习笔记——详细基础知识篇(第九章 高级部分:面向对象)

类的定义

类!=面向对象,要有意义的面向对象的代码才行。 面向对象最核心的就是 类和对象。

class关键字 +类名 来定义类。

类的名字建议首字母大写,不建议使用下划线。

类的最基本的作用:封装一系列的变量和函数,即封装代码

类只负责定义,不负责运行。不建议把类的定义与使用放在同一个模块。

C7.py

类的定义:

class Student():
    name=''
    age=0   
    #在类的内部可以定义若干变量,描述类的特征
    
    def print_file(self):      
        print('name:'+ self.name)
        print('age:'+ str(self.age)
    #在类的内部还可以定义函数,要想在后面调用该函数则要用到self关键字,类下的函数叫做类的方法,描述类的行为


如何使用类:

student = Student()    #对类进行实例化:变量 = 类名()
student.print_file()   #调用类的方法

输出:
name:
age:0

浅谈函数与方法的区别

(1)从另一个模块调用类:

from C7 import Student

student = Student()
student.print_file()

(2)方法与函数的区别

方法:设计层面
函数:程序运行、过程式的一种称谓

在类下定义的 变量 称做是数据成员。类下定义的 函数 称做是方法。

注:初学者不用太纠结这些称谓

类与对象

(1)类的定义:类是现实世界或思维世界中的实体在计算机中的反映,它将数据以及这些数据上的操作封装在一起。
(2)类是总称,对象是具体的。类 实例化 就变成了对象。

构造函数

class Student():
    name=''
    age=0
     
    def __init__(self):        #构造函数,注意两边都有有两个横线
        print('s')

student1 = Student()   #实例化,在Python中不需要加上new
student1.__init__()

输出:
s
s

实例化的时候,python会自动调用构造函数。
构造函数默认返回None,且不能修改为返回其他值。

区别模块变量与类中的变量

(1)思考题:

class Student():
    name=''
    age=0   

    def __init__(self,name,age):     #构造函数增加了参数,则在实例化时必须传入参数
        name = name
        age = age   
    
    def do_homework(self):
        print('homework')

student1 = Student('小李', 18)
print(student1.name)

输出一行空字符串


(2)在Python中,局部变量不能覆盖全局变量,但是这里只打印出空字符串,并不是这个原因:并不是说name=‘’中的name就是全局变量,而后面构造函数中的name就是局部变量。我们要区别模块变量与类中的变量。真正的原因:与下下节要讲的类与对象的变量查找顺序有关。

类变量与实例变量

class Student():
    name='qi'
    age=0     #两个都是类变量 

    def __init__(self,name,age):
        # 构造函数
        # 初始化对象的属性
        self.name = name   #第一个name是实例变量,第二个是形参,两者是可以一样的。
        self.age = age   #实例变量推荐用self来定义,若换成其他的,则前面函数定义的参数中self也要换
    
    def do_homework(self):
        print('homework')

student1 = Student('小李', 18)
student2 = Student('小张',18)
print(student1.name)
print(student2.name)
print(Student.name)   #打印的是类的变量

输出:
小李
小张
qi

类与对象的变量查找顺序

如果访问一个实例变量,会在对象的实例变量列表里面去查找有没匹配的变量,若找不到,则会继续去类变量列表里面找。仍没有找到的话,会去其父类中查找。

扩展:

print(student1.__dict__)
print(student.__dict__)

在这里插入图片描述

self 与实例方法

(1)什么时候用到self?

实例可以调用的方法 叫做实例方法

在类下定义一个实例方法时,需要传入self 参数。调用时不用传入(因为会默认传入)。(也可以把self改成其他的,但是建议用self。在其他语言中的this就相当于这里的self。)

    def __init__(self,name,age):     
        self.name = name    # 用self指定对象的实例变量
        self.age = age  
        print(self.age)   
        print(self.name) 

(2)self 的意义:self 就是 当前调用的某一个方法的 对象。

self 只和对象有关,与类无关。谁调用了self的方法,则self就指代的谁。self 代表的是实例(对象)

例如某类中有该方法:
 def do_homework(self):
        print('homework')

调用该方法:
student1.do_homoework()    ,则此时self就是指的对象student1.
上面例子中:
self.name = ....   

相当于在给一个 一个对象.name 赋值 ,也就是在给一个对象的实例变量在赋值

在实例方法中访问实例变量与类变量

(1)目前所学知识框架梳理
在这里插入图片描述

(2)在实例方法里面操作实例变量是很容易的,用 self 访问实例变量即可。

 def __init__(self,name,age):     #构造函数也可看做是一个特殊的实例方法
        self.name = name
        self.age = age  
        print(self.age)    # 用 self 访问实例变量即可
        

补充:构造函数与实例方法的区别:

  • 构造函数也可看做是一个特殊的实例方法,几乎所有的行为 和 普通的实例方法 是一样的。
  • 调用的方式不一样:调用构造函数,是通过 创建对象类的实例化) 时就调用了的: 对象名 = 类名()
    而普通的实例方法是:通过 对象名.方法名() 调用的
  • 意义不一样:实例方法主要作用是用来描述类的行为的,而构造函数是用来初始化类的各种特征的

(3)易错点:打印实例变量忘记加 self.(这里是指在类下的方法里面打印要加self,在类外部调用实例方法时不用传入self参数!)

例子依然是前面所讲到的

def __init__(self,name1,age):     #某个类下的构造函数
        self.name = name1
        self.age = age  
        print(self.name)   —————— 会打印出实例变量
        print(name)       —————— 会打印出形参name的值

再看一个:
    def __init__(self,name,age):
        self.name = age     # 把年龄赋值给姓名
        print(self.name)    # 打印实例变量
        print(name)         # 打印形参的值

student1 = Student('小李', 18)

输出:
18
小李
def __init__(self,name1,age):
        self.name=name1     
        print(self.name) 
        print(name)

student1 = Student('小李', 18)

输出:
小李
报错信息.....

———————— 这里报错是因为把形参名改为naem1 后,print(name) 无法找到形参。
———————— 我们之前在小节 “类与对象的变量查找顺序 ” 中所说的查找机制并不会生效,该查找机制只会在类的外部,也就是调用类的时候 才会生效。

注:这里很多例子都是部分代码,都是基于 前面几小节中 写的Student类的例子。

(4)在类下的方法里如何访问类变量?———— 类名.类变量

class Student():  
    sum = 0  

    def __init__(self,name,age):
        self.name = name        
        print(Student.sum)   #访问类变量sum
    
student1 = Student("小李",18)

输出:0

还有另外一种方法:通过self 来访问:

print(Student.sum) 换成 print(self.__class__.sum) 仍然也可以打印出类变量的值。

类方法

(1)体会类变量的作用

class Student():  
    sum=0  

    def __init__(self,name,age):
        self.name=name        
        self.age=age
        self.__class__.sum += 1
        print('当前学生总数为:' + str(self.__class__.sum))

    
student1 = Student("小李",18)
student2 = Student("小张",18)
student2 = Student("小陈",18)

输出:
当前学生总数为:1
当前学生总数为:2
当前学生总数为:3
———————— 创建一个对象,则类变量sum就加1

(2)如何定义类方法?

在定义方法前加上语句:@classmethod

定义类方法时,参数要使用 cls(class的简写)

(3)类方法用于操作类变量

class Student():  
    sum=0  
    def __init__(self,name,age):
        self.name=name        
        self.age=age
        self.__class__.sum += 1      # 通过self来访问 类变量 sum
        print('当前学生总数为:' + str(self.__class__.sum))
   
    @classmethod
    def plus_sum(cls):    # 定义类方法要使用cls
        cls.sum += 1      # 通过 cls 调用 类变量 sum
        print(cls.sum)

student1 = Student("小李",18)
Student.plus_sum()         # 调用一次 类变量就加1
student2 = Student("小张",18)
Student.plus_sum()
student2 = Student("小陈",18)
Student.plus_sum()

输出:
当前学生总数为:1
2
当前学生总数为:3
4
当前学生总数为:5
6

注:cls 同样地也可更改为其他的 名字。但是建议使用cls

(3)类方法与实例方法的区别

类方法关联的是类本身,实例方法关联的是对象。
两者的根本区别不是 cls 与 self (因为它们都可更改为其他的名字),而是有没有 @classmethod

虽然在实例方法中可以调用类变量,但是有时候在意义上不好理解。

(4)cls 的意义:代表调用的类方法的类

比如上面例子中的 Student.plus_sum() 这里调用的类方法就是plus_sum(),它的类就是Student,则 cls就是代表Student 这个类

(5)Student.plus_sum() 是用类来调用类方法,那么可以用对象调用类方法吗?

答:可以,直接Student1.plus_sum() 即可,会有一样的结果,但是不建议这么做!!!

静态方法

(1)如何定义一个静态方法?

    @staticmethod
    def add(x,y):
        print('This is a static method')

(2)静态方法相关知识点

静态方法是可以被类和对象同时一起调用的。

静态方法内部是可以访问类变量的。

class Student():  
    sum=0  
    def __init__(self,name,age):
        self.name=name        
        self.age=age
        self.__class__.sum += 1
        print('当前学生总数为:' + str(self.__class__.sum))
    
    @staticmethod
    def add(x,y):
        print(Student.sum)     #静态方法访问类变量
        print('This is a static method')


student1 = Student("小李",18)
student1.add(1,2)
Student.add(1,2)      #类和对象同时调用静态方法

输出:
当前学生总数为:1
1
This is a static method
1
This is a static method

静态方法与类方法类似。

(3)静态 / 类 方法可不可以访问实例变量(self.实例变量名)?
答:不可以直接访问,会报错。

(4)静态方法与类方法的比较?

静态方法访问类变量:类名.类变量名
类方法访问类变量:cls.类变量名

在python中不推荐使用静态方法,因为它和面向对象的关联不大,和一个普通函数没什么区别。

(5)目前所学思维导图:

在这里插入图片描述

成员可见性:公开和私有

(1)

内、外调用:在类的外部、内部 调用 方法、变量。

(2)类的数据的不安全性

前面所学的类方法 / 静态方法 / 实例方法等 都可以 直接在外部调用,更改类和对象内部的数据,所以造成了内部数据的不安全性。

(3)

class Student():  
    sum=0  
    def __init__(self,name,age):
        self.name=name        
        self.age=age
        self.score = 0
        self.__class__.sum += 1
       
    def marking(self,score):
        if score < 0:
            return '不能给别人打负分'
        self.score = score
        print(self.name + '同学本次考试分数为:' + str(self.score))
        

student1 = Student("小李",18)
result = student1.marking(-1)      
print(result)


这里的marking方法的使用场景:如果这个类是提供给别人使用,他们并不熟悉类内部的机制,有可能会打负分。当有人在外部修改分数为负分时,会输出:不能给别人打负分。

编程提倡的规范:
类下的变量是类非常重要的特征,如果我们要修改类的特征的值,不应该直接通过访问变量的形式来改。而是应该通过方法来完成。

(4)成员的可见性问题:

公开的:一个变量/函数 可以在类的外部直接访问
私有的:在类的外部无法访问,既不能设置也不能读取。

如何变成私有的?

一般的,变量 / 方法 名加了双下划线"__",就认为是私有的(在名字后面不用加)。构造函数是前后都有双下划线(__init__),是公开的。

没有什么是不能访问的

(1)对一个私有的方法从外部调用时会报错,但是从外部调用 私有的变量 不会报错,为什么?

探索一:

class Student():  
    sum=0  
    def __init__(self,name,age):
        self.name=name        
        self.age=age
        self.__score = 0
        self.__class__.sum += 1
       
    def marking(self,score):
        if score < 0:
            return '不能给别人打负分'
        self.__score = score
        print(self.name + '同学本次考试分数为:' + str(self.__score))
    

student1 = Student("小李",18)

student1.__score = -1
print(student1.__score)

输出:     
-1

从外部调用 私有的实例变量 没有报错,是由于python语言的动态性,student1.__score = -1  实际上是给对象student1新添加了一个实例变量:__score

探索二:

同样是上面的类,重新实例化一个对象student2:

student1 = Student("小李",18)
student2 = Student("小陈",18)

student1.__score = -1
print(student1.__score)

print(student2.__score)

输出:
-1
报错信息。。。。
—————— 对象student2 调用私有变量__score却报错了,student1没报错是因为我们上面所说的student1.__score = -1给给对象student1新添加了一个实例变量:__score。
而student2没有添加变量__score,所以说找不到该变量。总的来说这里的__score和之前设的私有变量不是同一个变量了。

探索三:验证

同样是上面的类:

student1.__score = -1

print(student1.__dict__)
print(student2.__dict__)

输出:
{'name': '小李', 'age': 18, '_Student__score': 0, '__score': -1}
{'name': '小陈', 'age': 18, '_Student__score': 0}

可以看出:
原先的私有变量变成了'_Student__score',所以说会报找不到私有变量的错误。
student1重新生成了一个变量并赋值为-1'__score': -1


结论:python对私有变量的保护机制实际上就是把 定义的私有变量名进行了更改,让你找不到原来的变量。也就是说实际上在在Python中没有真正的私有变量。
应该要养成自觉不 更改/读取 私有变量的习惯。

继承(重点)

在这里插入图片描述
(1)什么叫做继承?

C7模块:

class Human():
    pass
from C8 import Human    # 先导入

class Student(Human):   # 再继承,
                        # Human此时称为父类,Student为Human的子类
    sum = 0
    def __init__(self,name,age):
        self.name = name
        self.age = age   
        self.__score = 0
        self.__class__.sum += 1
    
    def do_homework(self):
        print('homework')

(2)子类继承父类的变量和方法。

C8模块(父类):

class Human():
    sum=0
    
    def __init__(self,name,age):
        self.name = name
        self.age = age
    
    def get_name(self):
        print(self.name)

C7模块(子类):

from C8 import Human   

class Student(Human):
    pass

print(Student.sum)
输出:0

继承了父类的类变量sum
继续补充子类的代码:

from C8 import Human   

class Student(Human):
    pass
    
student1 = Student('小李',18)
print(student1.sum)
print(Student.sum)     #继承类变量
print(student1.name)   #继承实例变量
print(student1.age)

输出:
0
0
小李
18

父类的变量和方法是都可以被继承的。

(3)在其他编程语言中一般是单继承(一个子类仅有一个父类)。但在python中可以多继承(一个子类可以有多个父类)。
建议先把单继承用好!

用一张图表示单继承关系:
在这里插入图片描述

(4)
之前说过 在实例化的时候调用实例变量时,是不用我们传入self的。但在这里要传。(注:定义的时候不管哪种情况都要传入self)

from C8 import Human   

class Student(Human):  
    def __init__(self,school,name,age)  #因为父类的构造函数有name,age参数,所以这里也要传入
        self.school = school
        Human.__init__(self,name,age)  #调用 父类的构造函数,要传入self不然会报错

student1 = Student('第一中学','小李',18)
print(student1.name)
print(student1.age)

子类方法调用父类方法:super关键字

(1)如何在子类中调用父类的构造函数?(了解即可)

上一节中讲到的Human.__init__(self,name,age),要传入self。是因为这是通过一个类来调用的,就是一个普通的方法调用。
student1 = Student('第一中学','小李',18) 这是进行实例化,是会自动调用构造函数的,不用传入self。

用对象调用实例方法,不用传入self。

总的来说,用类来调用实例方法,则self就变成了一个普普通通的参数,所以调用时要传入一个参数。但是不推荐用子类方法调用父类的方法。(父类名称更改后还要更改这里的代码,极其地不方便!)

(2)另外一种 调用父类方法的方式?(重点)

super(Student,self).__init__(name,age) (super实际上代替的是父类名Human)

from C8 import Human   

class Student(Human):   
    # sum = 0
    def __init__(self,school,name,age):  
        self.school = school
        # Human.__init__(self,name,age)   #调用父类的构造函数 方法一
        super(Student,self).__init__(name,age) #调用父类的构造函数 方法二
     
     def do_homework(self):
        print('homework')

student1 = Student('第一中学','小李',18)

print(student1.name)
print(student1.age)

输出:
小李
18

(3)子类和父类函数同名的情况:不会报错,并会优先调用子类方法。

C8:
class Human():
    sum=0
    def __init__(self,name,age):
        self.name = name
        self.age = age  
    def get_name(self):
        print(self.name)    
    def do_homework(self):
        print('This is a parent method')
from C8 import Human   

class Student(Human):   
    def __init__(self,school,name,age):  
        self.school = school
        super(Student,self).__init__(name,age) #调用父类的构造函数    
    def do_homework(self):
        super(Student,self).do_homework()   #用super关键字调用父类的实例方法
        print('homework')

student1 = Student('第一中学','小李',18)
student1.do_homework()

总结:
super:用于调用父类 构造函数 和 实例方法。
用法:super (子类名,self) . 方法名 (参数1,参数2…)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值