一、初识对象
对象是类的实例化,是类的具体表现。对象包含了类定义的属性和方法,并且可以根据需要修改或调用。
二、成员方法
1、类的定义和使用
class 类名称:
类的属性
类的行为
· class是关键字,表示定义类
· 类的属性,既定义在类中的变量(成员变量)
· 类的行为,既定义在类中的函数(成员方法)
创建类对象的语法:
对象 = 类名称()
2、成员方法的定义语法
def 方法名(self,形参1,形参2,......,形参n):
方法体
定义方法的参数列表中,有一个关键字:self。
self关键字必须存在:
· 表示类对象自身的意思
· 当我们使用类对象调用方法时,self会自动被python传入
· 在方法内部,想要访问类的成员变量,必须使用self
注意事项
self关键字,尽管在参数列表中,但是传参的时候可以忽略它。
# 设计一个类 class Student: name = None age = None def say_hi(self,msg): # 类内部的函数叫方法 print(f"大家好,我叫{self.name},{msg}") def say_hello(self): print("Hello") # 创建一个学生对象 stu1 = Student() stu1.name = "张三" stu1.age = 18 stu1.say_hello() # 调用时无需传参 stu1.say_hi("很高兴认识大家") # 调用时,需要传msg参数
三、类和对象
1、使用类描述现实世界事物
现实世界的事物由属性和行为组成,类也可以包含属性和行为,所有用类描述现实世界事物非常合适。
2、类和对象的关系
类:相当于设计图纸
对象:相当于生产实体
类只是一种程序内的“设计图纸”,需要基于图纸生成实体(对象),才能正常工作。这种套路,称之为:“面向对象编程”
3、什么是面向对象
使用对象进行编程。设计一个类,基于类去创建一个对象,并使用对象来完成具体的工作。
四、构造方法
属性(成员变量)的赋值
python类可以使用:__init__()方法,称之为构造方法。可以实现:
· 在创建类对象(构造类)的时候,会自动执行
· 在创建类对象(构造类)的时候,将传入参数自动传递给__init__方法使用
class Student: def __init__(self, name, age): self.name = name self.age = age print(f"Student类创建了一个对象,{self.name},{self.age}") stu1 = Student("张三", 18)
注意事项:
· 构造方法也是成员方法,不要忘记在参数列表中提供:self
· 在构造方法内定义成员变量,需要使用self关键字
五、其他内置方法
上述所学习的__init__构造方法,是python内置方法之一。
这些内置的类方法,各自有各自特殊的功能,这些内置方法我们称之为:魔术方法
· __init__ ——构造方法
· __str__ ——字符串方法
· __It__ ——小于、大于符号比较
· __le__ ——小于等于、大于等于符号比较
· __eq__ —— ==符号比较
1、 __str__ ——字符串方法
当类对象需要被转换为字符串是,会输出如下结果(内存地址)
class Student: def __init__(self, name, age): self.name = name self.age = age stu1 = Student("张三", 18) print(stu1) print(str(stu1))
运行结果为:
<__main__.Student object at 0x0000020374148760>
<__main__.Student object at 0x0000020374148760>
由于内存地址没有多大用途,故我们可以使用__str__方法,控制类转换为字符串的行为。
class Student: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f"学生姓名:{self.name},年龄:{self.age}" stu1 = Student("张三", 18) print(stu1) print(str(stu1))
运行结果:
学生姓名:张三,年龄:18
学生姓名:张三,年龄:18
2、__It__ ——小于、大于符号比较
直接两个对象进行比较时不可以的,但是在类中使用__lt__方法,可以同时完成:小于符号和大于符号的比较
class Student: def __init__(self, name, age): self.name = name self.age = age def __lt__(self,other): return self.age < other.age stu1 = Student("张三", 18) stu2 = Student("李四", 20) print(stu1 < stu2) print(stu1 > stu2)
方法名: __lt__
传入参数:other,另一个类对象
返回值:True或False
内容:自行定义
3、 __le__ —— <=、>=符号比较
class Student: def __init__(self, name, age): self.name = name self.age = age def __le__(self,other): return self.age <= other.age
4、 __eq__ —— ==符号比较
如果类中为定义__eq__方法,直接比较stu1和stu2类,默认比较这两个类的内存地址。结果返回为false。
定义__eq__方法,可以在方法内,定义比较哪个属性。
六、封装
1、私有成员
· 不公开的属性和行为,私有方法无法直接被类对象使用,私有变量无法被类对象赋值,也无法获取值。
定义方式:
· 私有成员变量:变量名以__开头(两个下划线)
· 私有成员方法:方法名以__开头(两个下划线)
class Phone: __current_voltage = None def __keep_single_core(self): print("保持手机单核运行") phone = Phone() phone.__keep_single_core()
运行结果报错,私有成员和私有变量都无法直接被类对象使用。
2、私有成员的访问
私有成员无法被类对象使用,但是可以被其他的成员使用。
class Phone: __current_voltage = 1 def __keep_single_core(self): print("保持手机单核运行") def call_by_5g(self): if self.__current_voltage >= 1: print("使用5G网络进行通话") else: self.__keep_single_core() print("无法开启5g") phone = Phone() phone.call_by_5g()
运行结果表明,私有成员变量可以被正常访问
将上述代码中的运行电压改为0.5
__current_voltage = 0.5
再次运行时:
运行结果表明:私有成员方法可以被内部其他成员访问
3、私有成员存在的意义
在类中提供仅供内部使用的属性和方法,而不对外开放(类对象不可以使用)。
定义不直接对用户开放的属性和行为。
七、继承
1、定义
手机一代一代的更迭,新一代都是在上一代的基础上开发出来。
简单来说,就是把已有的功能(成员变量和成员方法),继承过来
2、语法:
继承分为单继承和多继承。
class 类名(父类名):
类内容体
3、单继承
# 单继承 class Phone: # 父类 IMEI = None # 序列号 producer = "HM11" # 厂商 def call_by_4g(self): print("正在使用4G网络进行通话") class Phone2024(Phone): face_id = "10001" # 面部识别ID def call_by_5g(self): print("新功能使用5G网络进行通话") phone = Phone2024() print(phone.producer) # 父类中的属性 phone.call_by_4g() # 父类中的功能 phone.call_by_5g()
运行结果:
4、多继承
· 一个类,可以继承多个父类,按照顺序从左往右依次继承。
语法:
class 类名(父类1,父类2,......,父类N):
类内容体
class Phone: # 父类 IMEI = None # 序列号 producer = "HM11" # 厂商 def call_by_4g(self): print("正在使用4G网络进行通话") class NFCReader: nfc_type = "第五代" producer = "HM11" def read_nfc(self): print("读取NFC卡") def write_card(self): print("写入NFC卡") class RemoteControl: rc_type = "红外遥控" def control_phone(self): print("红外遥控已开启") class MyPhone(Phone, NFCReader, RemoteControl): pass # pass关键字补全语法,无功能 phone = MyPhone() phone.call_by_4g() phone.read_nfc() phone.control_phone() print(phone.producer) print(phone.nfc_type)
多继承的注意事项:
多个父类中,如果有同名的成员,那么默认以继承顺序(从左往右)为优先级。即:先继承的保留,后继承的覆盖。
示例:
class Phone: # 父类 IMEI = None # 序列号 producer = "HM11" # 厂商 class NFCReader: nfc_type = "第五代" producer = "HM22" class MyPhone(Phone, NFCReader): pass # pass关键字补全语法,无功能 phone = MyPhone() print(phone.producer)
运行结果显示的为父类Phone中的producer值,因为Phone在NFCReader的左侧,故优先级高于NFCReader。
5、复写和使用父类成员
(1)复写
子类在继承父类的成员属性和成员方法后,如果对其“不满意”,那么可以进行复写。
即:在子类中重新定义同名的属性和方法即可。
class Phone: # 父类 IMEI = None # 序列号 producer = "HM11" # 厂商 def call_by_5g(self): print("父类5G网络进行通话") class MyPhone(Phone): producer = "HM33" # 复写父类的成员属性 def call_by_5g(self): # 复写父类的成员方法 print("子类5G网络进行通话") phone = MyPhone() phone.call_by_5g() print(phone.producer)
运行结果:
(2)调用父类同名成员
方式一:
· 调用父类成员
使用成员变量:父类名.成员变量
使用成员方法:父类名.成员方法(self)
class Phone: # 父类 IMEI = None # 序列号 producer = "HM11" # 厂商 def call_by_5g(self): print("父类5G网络进行通话") class MyPhone(Phone): producer = "HM33" # 复写父类的成员属性 def call_by_5g(self): # 复写父类的成员方法 print("子类5G网络进行通话") Phone.call_by_5g(self) # 调用父类的成员方法 print(f"父类的厂商是{Phone.producer}") # 调用父类的成员属性 phone = MyPhone() phone.call_by_5g()
方式二:
· 使用super()调用父类成员
使用成员变量:super().成员变量
使用成员方法:super().成员方法()
class Phone: # 父类 IMEI = None # 序列号 producer = "HM11" # 厂商 def call_by_5g(self): print("父类5G网络进行通话") class MyPhone(Phone): producer = "HM33" # 复写父类的成员属性 def call_by_5g(self): # 复写父类的成员方法 print("子类5G网络进行通话") super().call_by_5g() # 调用父类成员方法 print(f"父类的厂商是{super().producer}") # 调用父类成员属性 phone = MyPhone() phone.call_by_5g()
注意:
只可以在子类内部调用父类的同名成员,子类的实体对象调用默认是调用子类复写的。
八、类型注解
类型注解:在代码涉及数据交互的地方,提供数据类型的注解(显示的说明)
主要功能:
· 帮助第三方IDE工具(如Pycharm)对代码进行类型推断,协助做代码提示
· 帮助开发者自身对变量进行类型注释(备注)
支持:
· 变量的类型注释
· 函数(方法)形参列表和返回值的类型注解
1、类型注解的语法
基础语法:变量:类型
# 基础类型注解
var_1:int = 10
var_2:str = "hello"
var_3:float = 3.14
var_4:bool = True
# 类类型注解
class MyClass:
pass
stu:MyClass = MyClass()
# 基础容器类型注解
my_List:list = [1,2,3]
my_tuple:tuple = (1,2,3)
my_set:set = {1,2,3}
my_dict:dict = {"name":"张三","age":18}
# 容器类型详细注解
my_list:list[int] = [1,2,3]
my_tuple:tuple[int,str,bool] = (1,"2",True)
my_set:set[int] = {1,2,3}
my_dict:dict[str,int] = {"name":18,"age":18}
注意:
· 元组类型设置类型详细注解,需要将每一个元素都标记出来
· 字典类型设置类型详细注解,需要两个类型,第一个是key第二个是value
语法2:在注释中进行类型注解
# type:类型
def func(): return 10 var_1 = random.randint(1,100) # type:int var_2 = json.loads('{"age:11}') # type:dict[str,int] var_3 = func() # type:int
2、注意事项
类型注解只是提示性的,并非决定性的。数据类型和注释类型无法对应也不会导致错误。
3、函数(方法)的类型注解
函数和方法的形参类型注解语法:
def 函数方法名(形参名:类型,形参名:类型,......)-> 返回值类型:
pass
def add(a:int,b:int)->int: return a+b
4、Union类型
使用Union[类型,......,类型],可以定义联合类型注解
Union联合类型注解,在变量注解、函数(方法)形参和返回值注解中,均可使用。
使用方法: from typing import Union
from typing import Union my_list:list[Union[int,str]] = [1,2,3,"hello"] def func(data:Union[int,str])->Union[int,str]: pass
九、多态
多态:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态。
同样的行为(函数),传入不同的对象,得到不同的状态。
多态常作用在继承关系上。比如:
· 函数(方法)形参声明接收父类对象
· 实际传入父类的子类对象进行工作
即:
· 以父类做定义声明
· 以子类做实际工作
· 用以获得同一行为,不同状态
class Animal: def speak(self): pass class Dog(Animal): def speak(self): print("汪汪汪") class Cat(Animal): def speak(self): print("喵喵喵") def animal_speak(animal:Animal): animal.speak() dog = Dog() cat = Cat() animal_speak(dog) animal_speak(cat)
父类的Animal的speak方法,是空实现
这种设计的含义是:
· 父类用来确定有哪些方法
· 具体的方法实现,由子类自行决定
这种写法,就叫做抽象类(也可称之为接口)
抽象类:含有抽象方法的类称之为抽象类
抽象方法:方法体是空实现的(pass)称之为抽象方法
抽象类(接口)
抽象类就好比定义一个标准,包含了一些抽象的方法,要求子类必须实现
配合多态,完成
· 抽象的父类设计(设计标准)
· 具体的子类实现(实现标准)
# 抽象类 class AC: def cool_wind(self): """制冷""" pass def heat_wind(self): """制热""" pass def wind_on(self): """左右摆风""" pass class Midea_AC(AC): def cool_wind(self): print("美的制冷") def heat_wind(self): print("美的制热") def wind_on(self): print("美的左右摆风") class GREE_AC(AC): def cool_wind(self): print("格力制冷") def heat_wind(self): print("格力制热") def wind_on(self): print("格力左右摆风") def make_cool(ac:AC): ac.cool_wind() midea_ac = Midea_AC() gree_ac = GREE_AC() make_cool(midea_ac) # 输出:美的制冷 make_cool(gree_ac) # 输出: 格力制冷
抽象类的作用:
多用于做顶层设计(设计标准),以便子类做具体的实现
也是对子类的一种软约束,要求子类必须复写(实现)父类的一些方法,并且配合多态去使用,就可以获得不同的工作状态。