声明
文章是基于:
哗哩哗哩上的《黑马程序员python教程,8天python从入门到精通,学python看这套就够了》视频教程所做的学习笔记,仅为学习使用!!如有侵权,联系删除
目录
面向对象编程
- 核心在于以对象为编程单位。
- 它把相关的数据(对象的属性)和相关功能(对象的方法)集合在一起,放在类和对象这个机制里。
- 这样在编程中可以以对象为单位,而不用以一行一行的代码为单位来展开了。
一、初始对象
使用变量记录数据太乱了,如果程序和生活中一样:
-
可以设计表格
-
可以将设计的表格打印出来
-
可以将打印好的表格供人填写内容
那么数据的组织就非常方便了
(一)设计类(class)
-
在程序中设计表格
-
Class Student: name = None # 记录学生姓名
(二)创建对象
-
在程序中打印生产表格
-
# 基于类创建对象 stu_1 = Student() stu_2 = Student()
(三)对象属性赋值
-
在程序中填写表格
-
stu_1.name = "周杰伦" # 为学生1对象赋予名称属性值 stu_2.name = "林俊杰" # 为学生2对象赋予名称属性值
二、成员方法
(一)类的定义和使用
-
使用类去封装属性,并基于类创建出一个个的对象来使用
-
类的使用语法:
-
class是关键字,表示要定义类了
-
类的属性,即定义在类中的变量(成员变量)
-
类的行为,即定义在类中的函数(成员方法)
-
创建类对象
(二)成员变量和成员方法
-
其中:
-
类中定义的属性(变量),我们称之为:成员变量
-
他是归属于类的一个变量,用来描述类的一些特性。
-
-
类中定义的行为(函数),我们称之为:成员方法
-
(三)成员方法的定义语法
-
self关键字:必须填写的
-
用来表示类对象自身的意思
-
当我们使用类对象调用方法时,self会自动被python传入
-
在成员方法内部,想要访问类的成员变量,必须使用self
-
def say_hi(self): print(f"hi大家好,我是{self.name}")
-
-
self关键字,尽管在参数列表中,但是传参的时候可以忽略它
-
传参时,self是透明的,不用理会
三、类和对象
使用程序中的类,可以完美的描述现实世界的事物
(一)语法
1、Python中使用class关键字来定义类,遵循下述格式:
class 类名:
def __init__(self, 参数, ...): # 构造函数,只在生成类的实例时被调用一次
def 方法名1(self, 参数, ...): # 方法1
def 方法名2(self, 参数, ...): # 方法2
# 在方法的第一个参数中明确地写入自身(自身的实例)的self是Python的一个特点
2、基于类创建对象的语法: 对象名 = 类名称( )
-
为什么非要创建对象才能使用呢?
-
面向对象编程:类只是一种程序内的“设计图纸”,需要基于图纸生产实体(对象),才能正常工作
-
(二)基于类创建对象
- 面向对象编程:设计类,基于类创建对象,由对象做具体的工作
- 实例化的具体代码执行时机,是在创建对象的时候。这时候默认调用了初始化函数__init__( )。
- 我们不用显性或编写代码来调用__init__()函数,因为这个调用过程默认是静默的。
四、构造方法
(一)属性(成员变量)的赋值
有没有更加高效的方式,能够一行代码就完成呢?能否像函数(方法)那样,通过传参的形式对属性赋值呢?
(二)构造方法
-
__init__( )【左右各有两个下划线_】
方法,称之为构造方法 -
可以实现:
-
在创建类对象(构造类)的时候,会自动执行/调用__init__函数
-
在创建类对象(构造类)的时候,将传入参数自动传递给
__init__
方法使用
-
-
class Student: name = None age = None tel = None def __init__(self, name, age, tel): self.name = name self.age = age self.tel = tel # 构造对象时,构造方法就会直接执行,输出语句 print("Studnet类创建了一个类对象") # 执行构造方法时,会将参数传入,为类的成员变量赋值 stu = Student("周杰伦", 12, "12345678901") print(stu.name) print(stu.age) print(stu.tel) """ 输出: Studnet类创建了一个类对象 周杰伦 12 12345678901 """
class Man:
def __init__(self, name):
self.name = name # 接收参数name,参数初始化实例变量self.name
# 实例变量是存储在各个实例中的变量
print("初始化!")
def hello(self):
print("Hello" + self.name + "!")
def goodbye(self):
print("Good-bye" + self.nsmr + "!")
m = Man("David)
m.hello()
m.goodby()
"""
初始化!
Hello David!
Good-bye David!
"""
-
构造方法也是成员方法,不要忘记在参数列表中提供:self
-
在构造方法内定义成员变量,需要使用self关键字
-
变量是定义在构造方法内部,如果要成为成员变量,需要self表示
-
五、其他内置方法
-
上文学习的
__init__
构造方法,是Python类内置的方法之一
(一)魔术方法
-
这些内置的类方法,各自有各自特殊的功能,这些内置方法我们称之为:魔术方法
1、__str__
字符串方法
-
可以通过
__str__
方法,控制类转换为字符串的行为 -
返回值:字符串
-
内容:自行定义
-
class Student: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f"Student类对象,name={self.name},age={self.age}" student = Student("周杰伦", 110) print(student) # 结果:student类对象,name=周杰伦,age=1 print(str(student)) # 结果:student类对象,name=周杰伦,age=1
2、__lt__
小于符号比较方法
-
直接对2个对象进行比较是不可以的,但是在类中实现
__lt__
方法,即可 -
传入参数:other,另一个类对象
-
返回值:True or Flase
-
内容:自行定义
-
大于:
__gt__
-
class Student: def __init__(self, name, age): self.name = name self.age = age def __lt__(self, other): return self.age < other.age stu1 = Student("周杰伦", 11) stu2 = Student("林俊杰", 13) # 比较两个对象的age,执行的是我们自定义的lt方法 print(stu1 < stu2) # 结果:True
3、__le__
小于等于符号比较方法
-
<=、>=
两种比较运算符上 -
传入参数:other,另一个类对象
-
返回值:True or Flase
-
内容:自行定义
-
大于等于:
__ge__
-
class Student: def __init__(self, name, age): self.name = name self.age = age def __le__(self, other): return self.age <= other.age stu1 = Student("周杰伦", 11) stu2 = Student("林俊杰", 13) print(stu1 <= stu2) # 结果:True
4、__eq__
比较运算符实现方法
-
传入参数:other,另一个类对象
-
返回值:True or Flase
-
内容:自行定义
-
不实现
__eq__
方法,对象之间可以比较,但是默认比较内存地址 -
不同对象==比较一定是False结果,因为两个独立的对象
-
实现了
__eq__
方法,就可以按照自己的想法来决定2个对象是否相等了 -
class Student: def __init__(self, name, age): self.name = name self.age = age def __eq__(self, other): return self.age == other.age stu1 = Student("周杰伦", 11) stu2 = Student("林俊杰", 11) print(stu1 == stu2) # 结果:True
六、封装
-
面向对象编程,是许多编程语言都支持的一种编程思想。
-
基于模板(类)去创建实体(对象),使用对象完成功能开发
-
3大主要特性:
-
封装:我们在调用一个函数或者使用一个对象时,不需要知道怎么实现这个功能,只需要知道我们应该输入什么,你会给我什么输出结果
-
继承
-
多态
-
(一)定义
-
将现实事物的:属性、行为,封装到类中,描述为
成员变量
、成员方法
-
隐藏内部的变量和过程代码,仅对外暴露公开的接口。
-
这个公开的接口包括:函数名,类的方法名,需要输入的参数,会返回的数据
(二)私有成员
-
现实世界中的事物,有属性和行为,但不代表都是开放给用户使用的
-
既然现实事物有不公开的属性和行为,那么作为现实事物在程序中映射的类,也应该支持。
-
私有成员变量:变量名以__开头(2个下划线)
-
私有成员方法:方法名以__开头(2个下划线)
(三)使用私有成员
-
私有成员无法被类对象直接使用,但是可以被其它的成员使用
-
在类中提供仅供内部使用的属性和方法,而不对外(类对象)开放
-
类对象只能调用公开的方法,而公开的方法内可以调用私有变量和私有方法,可以实现对私有成员和方法的访问
-
相当于用户使用手机的某个功能,用户不需要知道了解手机内部的功能是如何运行的,手机内部会帮助用户运行调用,用户只需要使用可以使用的即可
(四)练习
class Phone:
# 提供私有成员变量:__is_5g_enable
__is_5g_enable = False # 5g状态
def __check_5g(self):
if self.__is_5g_enable == True:
print("5g开启")
else:
print("5g关闭,使用4g网络")
def call_by_5g(self):
self.__check_5g()
print("正在通话中")
phone = Phone()
phone.call_by_5g()
七、继承
(一)单继承
-
继承表示:将从父类那里继承(复制)来成员变量 + 成员方法(不含私有)
-
# 演示单继承 class Phone: IMEI = None # 序列号 producer = "HM" # 厂商 def call_by_4g(self): print("4g通话") class Phone2022(Phone): face_id = "10010" # 面部识别ID def call_by_5g(self): print("2022新功能,5g通话") phone = Phone2022() phone.producer phone.call_by_4g() phone.call_by_5g()
(二)多继承
-
一个类,可以继承多个父类
-
pass:占位语句,补全函数体语法,避免报错,表示的意思就是“空”、“无内容”
-
-
多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级
-
先继承的保留,后继承的被覆盖
-
(三)复写
-
子类继承父类的成员属性和成员方法后,如果对其“不满意”,那么可以进行复写
-
即:在子类中重新定义同名的属性或方法即可
-
class Phone: IMEI = None # 序列号 producer = "ITCAST" # 厂商 def call_by_5g(self): print("父类的5G通话") class MyPhone(Phone): producer = "ITHEIMA" # 复写父类属性 def call_by_5g(self): # 复写父类方法 print("子类的5G通话")
-
(四)调用父类同名成员
-
一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员
-
需要使用被复写的父类的成员,需要特殊的调用方式:
-
方法一:
-
调用父类成员
-
使用成员变量:父类名.成员变量
-
使用成员方法:父类名.成员方法(self) 注意参数要写self
-
class Phone: IMEI = None # 序列号 producer = "ITCAST" # 厂商 def call_by_5g(self): print("父类的5G通话") class MyPhone: producer = "ITHEIMA" # 复写父类属性 def call_by_5g(self): # 方法1:调用父类成员 print(f"父类的品牌是:{Phone.producer}") Phone.call_by_5g(self)
-
-
方法二:使用super()调用父类成员
-
使用成员变量:父类名.成员变量
-
使用成员方法:父类名.成员方法()
-
class Phone: IMEI = None # 序列号 producer = "ITCAST" # 厂商 def call_by_5g(self): print("父类的5G通话") class MyPhone: producer = "ITHEIMA" # 复写父类属性 # 方式2调用父类成员 # super()相当于拿到了父类 print(f"父类的品牌是:{super().producer}") super().call_by_5g()
-
-
-
只能在子类内调用父类的同名成员
-
子类的类对象直接调用会调用子类复写的成员
八、类型注解
-
帮助第三方IDE工具(如PyCharm)对代码进行类型推断,协助做代码提示
-
帮助开发者自身对变量进行类型注释(备注)
-
并不会真正的对类型做验证和判断,类型注解仅仅是提示性的,不是决定性的
(一)变量的类型注解
-
变量: 类型
-
无法直接看出变量类型之时,会添加变量的类型注解
-
# 基础数据类型注解 var_1: int = 10 var_2: float = 3.1415926 var_3: bool = True var_4: str = "itheima" var_5: Student = func() # 类对象类型注解 class Student: pass #:Student表示stu变量是类对象类型,= Student()是对象创建语句 stu: Student = Student()
(二)容器的类型注解
1、基础容器类型注解
my_list: list = [1, 2, 3]
my_tuple: tuple = (1, 2, 3)
my_set: set = {1, 2, 3}
my_dict: dict = {"itheima": 666}
my_str: str = "itheima"
2、容器类型详细注解
my_list: list[int] = [1, 2, 3]
my_tuple: tuple[str, int, bool] = ("itheima", 666, True)
my_set: set[int] = {1, 2, 3}
my_dict: dict[str, int] = {"itheima": 666}
-
元组类型设置类型详细注解,需要将每一个元素都标记出来
-
字典类型设置类型详细注解,需要2个类型,第一个是key第二个是value
(三)注释的类型注解
-
语法:type : 类型
-
class Student: pass var_1 = random.randint(1, 10) # type: int var_2 = json.loafs(data) # type: dict[str, int] var_3 = func() # type:Studnet
(四)函数(方法)的类型注解
1、形参注解
2、返回值注解
(五)Union类型
-
目前所掌握的注解方式:
-
可如果遇到:数据类型无法用单一注解表示
-
使用Union[类型, ......, 类型], 可以定义联合类型注解
-
变量注解、函数(方法)形参和返回值注解中,均可使用
-
def func(data: Union[int, str]) -> Union[int, str]: pass
-
九、多态
-
多种状态,即完成某个行为时,使用不同的对象会得到不同的状态
-
同样的行为(函数),传入不同的对象,得到不同的状态
-
常作用在继承关系上
-
函数(方法)形参声明接收父类对象
-
实际传入父类的子类对象进行工作
即:
-
以父类做定义声明
-
以子类做实际工作
-
用以获得同一行为, 不同状态
-
(一)抽象类(接口)
-
父类Animal的speak方法,是空实现
-
父类用来确定有哪些方法
-
具体的方法实现,由子类自行决定
-
抽象类:含有抽象方法的类称之为抽象类
-
抽象方法:方法体是空实现的(pass)称之为抽象方法
-
抽象类就好比定义一个标准,包含了一些抽象的方法,要求子类必须实现。
-
配合多态完成:抽象的父类设计(设计标准) + 具体的子类实现(实现标准)
-
本质上:子类继承父类,子类可以使用父类中定义的成员变量和成员方法
-
都实现了制冷等,但是各自有各自的实现