目录
四十、类
1、定义:
在程序中是可以做到和生活中那样,设计表格,生产表格,填写表格的组织形式的。
在程序中设计表格,我们称之为:设计类(class)
class student:
name = None #记录学生姓名
在程序中打印生产表格,我们称之为:创建对象
# 基于类创建对象
stu1 = student()
stu2 = student()
在程序中填写表格,我们称之为:对象属性赋值
stu1.name = 'ikun' # 为学生1对象赋予属性值
stu2.name = 'geigei' # 为学生2对象赋予属性值
2、语法:
在上述中,我们简单了解到可以使用类去封装属性,并基于类创建出一个个的对象来使用。
现在我们来看看类的使用语法:
class 类名称: #class是关键字,表示要定义类了
类的属性 #类的属性,即定义在类中的变量(成员变量)
类的行为 #类的行为,即定义在类中的函数(成员方法)
创建类对象的语法:
对象 = 类名称()
成员方法的定义语法:
在类中定义成员方法和定义函数基本一致,但还是有细微区别:
def 方法名(self ,形参1,.......,形参N):
方法体
注意:在定义方法中self关键字是必须要填写的
当我们使用类对象调用方法的时,self会自动被python传入
在方法内部,想要访问类的成员变量,必须使用self
self关键字
尽管在参数列表中,但是传参的时候可以忽略它, 如:
class student:
name = None # 记录学生姓名
def say_hi(self, msg):
print(msg)
def say_name(self):
print(self.name)
# 基于类创建对象
stu1 = student()
stu1.name = 'ikun' # 为学生1对象赋予属性值
stu1.say_hi('hi') #调用的时候需要传入msg参数
stu1.say_name() #调用的时候无需传参
注意:定义方法的时候需要self关键字,在调用类的属性的时候也需要self关键字,传参则不用
3、思考
类和对象的关系?
类是程序的”设计图纸“;对象是基于图纸生产的具体实例
什么是面向对象编程?
面向对象编程就是,使用对象进行编程。即,设计类,基于类创建对象,并使用对象来完成具体的工作
4、__init__()方法
python类可以使用:__init__()方法,称之为构造方法
作用:
在创建类对象(构造类)的时候,会自动执行
在创建类对象(构造类)的时候,将传入参数自动传递给__init__方法使用
class student:
name = None # 记录学生姓名
age = None # 记录学生年龄
def __init__(self, name, age):
self.name = name
self.age = age
print("创建了一个对象")
# 基于类创建对象
stu = student('ikun',2.5) # 输出:创建了一个对象
print(stu.name) # 输出:ikun
print(stu.age) # 输出:2.5
注意:
构建类时传入的参数会自动提供给__init__方法
构建类的时候__init__方法会自动执行
构造方法名称init前后都有2个下划线,同时不要忘记了self
同时在__init__方法内部使用过的变量,可以不需要在外部定义如:
class student:
def __init__(self, name, age):
self.name = name
self.age = age
print("创建了一个对象")
# 基于类创建对象
stu = student('ikun',2.5) # 输出:创建了一个对象
print(stu.name) # 输出:ikun
print(stu.age) # 输出:2.5
5、魔术方法
上面学习的__init__()方法是python内置的方法之一
这些内置的类方法,各自有各自特殊的功能,这些内置方法我们称之为魔术方法
①、__init__()方法
上述已经介绍,不做过多赘述
②、__str__字符串方法
当我们需要将类对象转换为字符串之时,不写str函数会直接输出内存地址,如:
class student:
def __init__(self, name, age):
self.name = name
self.age = age
# 基于类创建对象
stu = student('ikun',2.5) # 输出:创建了一个对象
print(stu) # 输出:<__main__.student object at 0x000002A9F0C64230>
print(str(stu)) # 输出:<__main__.student object at 0x000002A9F0C64230>
内存地址没用多大作用,这时我们可以通过__str__方法,控制类转换为字符串的行为。
class student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'{self.name} is {self.age}'
# 基于类创建对象
stu = student('ikun',2.5) # 输出:创建了一个对象
print(stu) # 输出:ikun is 2.5
print(str(stu)) # 输出:ikun is 2.5
③、__lt__小于符号比较方法
直接对2个对象进行比较是不可以的,但是在类中实现__lt__方法,既可同时完成:小于符号和大于符号2种比较
class student:
def __init__(self, name, age):
self.name = name
self.age = age
def __lt__(self, other):
return self.age < other.age
# 基于类创建对象
stu = student('ikun',2.5) # 输出:创建了一个对象
stu1 = student('geigei',25) # 输出:创建了一个对象
print(stu < stu1) # 结果:True
print(stu > stu1) # 结果:False
其中的other是另外一个类对象,返回值为布尔类型,内容可以根据需要自行定义
④、__eq__比较运算符实现方法
不实现__eq__方法,对象之间是可以比较的,比较的是内存地址并没实际意义,不同对象比较的结果一定是False结果,实现后则可以根据需要自行决定了
class student:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return self.age == other.age
# 基于类创建对象
stu = student('ikun',2.5) # 输出:创建了一个对象
stu1 = student('geigei',25) # 输出:创建了一个对象
print(stu == stu1) # 结果:False
其中的other是另外一个类对象,返回值为布尔类型,内容可以根据需要自行定义
⑤、__le__小于等于比较符号方法
现实生活中,我们还有可能需要用到<=或者>=的比较运算符,那是不是运用__eq__方法和__le__方法就可以实现了呢,答案肯定是不可能的,你如果这么写了,程序会给你一个报错,说它不认识<=或>=符合,这个时候就要用到我们的__le__方法了
class student:
def __init__(self, name, age):
self.name = name
self.age = age
def __le__(self, other):
return self.age <= other.age
# 基于类创建对象
stu = student('ikun',2.5) # 输出:创建了一个对象
stu1 = student('geigei',25) # 输出:创建了一个对象
print(stu <= stu1) # 结果:True
print(stu >= stu1) # 结果:False
其中的other是另外一个类对象,返回值为布尔类型,内容可以根据需要自行定义
⑥、小结
方法 | 功能 |
__init__ | 构造方法,可用于创建类对象的时候设置初始化行为 |
__str__ | 用于实现类对象转字符串的行为 |
__lt__ | 用于2个类对象进行小于或大于比较 |
__le__ | 用于2个类对象进行小于等于或大于等于比较 |
__eq__ | 用于2个类对象进行相等比较 |
6、封装
①、概念
将现实世界事物在类中描述为属性和方法,即为封装
②、私有成员、私有变量
现实事物有部分属性和行为是不公开对使用者开放的。同样在类中描述属性和方法的时候也需要达到这个要求,就需要定义私有成员了
③、如何定义:
成员变量和成员方法的命名均以__作为开头即可(2个下划线)
④、访问限制:
类对象无法访问私有成员,但是可以通过类中其他成员间接的访问私有成员
7、继承
①、定义:
继承就是一个类,继承另外一个类的成员变量和成员方法
②、语法:
class 类(父类[,父类2,......,父类N]): 类内容体 |
子类构建的类对象可以有自己的变量和方法,也可以直接使用父类的变量和方法(不包含私有方法和私有变量)
注意:单继承就是一个类继承另外一个类,多继承就是一个类继承多个类,按照顺序从左向右依次继承,而在多继承中,如果父类有同名方法或者属性,先继承的优先级高(左边的高)
③、pass关键字
在子类中继承的变量和方法已经够用时,无需添加新内容时,为了编译能不报错,需要使用pass关键字,其表示无内容,空的意思
8、复写
子类继承父类的成员属性和成员办法后,如果对其“不满意”,那么可以进行复写。
即:在子类中重新定义同名的属性或方法即可
class ji:
IMEI = None # 序列号
producer = "ikun" # 厂商
def call_by_egg(self):
print("geigei")
class iji(ji):
producer = "IT" # 复写父类属性
def call_by_egg(self): # 复写父类方法
print("geigei的蛋")
一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员
如果需要使用被复习的父类的成员,需要特殊的调用方式:
方式1:
调用父类成员
使用成员变量:父类名.成员变量
使用成员方法:父类名.成员方法(self)
方式2:
使用super()调用父类成员
使用成员变量:super().成员变量
使用成员方法:super().成员方法()
class ji:
IMEI = None # 序列号
producer = "ikun" # 厂商
def call_by_egg(self):
print("geigei")
return None
class iji(ji):
producer = "IT" # 复写父类属性
def call_by_egg(self): # 复写父类方法
# 方式1调用父类成员
ji.call_by_egg(self)
# 方式2调用父类成员
super().call_by_egg()
return None
j = iji()
j.call_by_egg() # 输出俩个geigei
注意:只可以在子类内部调用父类的同名成员
9、类型注解
Python在3.5版本的时候引入了类型注解,以方便静态类型检查工具,IDE等第三方工具。
类型注解:在代码中涉及数据交互的地方,提供数据类型的注解(显式的说明)。
主要功能:
帮助第三方IDE工具(如PyCharm)对代码进行类型推断,协助做代码提示
帮助开发者自身对变量进行类型注释
并不会真正的对类型做验证和判断
也就是,类型注解仅仅是提示性的,不是决定性的
据类型和注解类型无法对应也不会导致错误
支持:
变量的类型注解
函数(方法)形参列表和返回值的类型注解
①、变量类型注解
基础语法1:变量:类型
# 基础数据类型注解
var1: int = 10
var2: float = 2.5
var3: bool = True
var4: str = "ikun"
# 类对象类型注解
class student:
pass
stu: student = student()
# 基础容器类型注解
my_list: list = [1, 2, 3]
my_tuple: tuple = (1, 2, 3)
my_set: set = {1, 2, 3}
my_dict: dict = {"ikun": 2.5}
my_str: str = 'ikun'
# 容器类型详细注解
my_list1: list[int] = [1, 2, 3]
my_tuple1: tuple[str, int, bool] = ('ikun', 2, True)
my_set1: set[int] = {1, 2, 3}
my_dict1: dict[str, float] = {'ikun': 2.5}
注意:元组类型设置类型详细注解,需要将每一个元素都标记出来
字典类型设置详细注解,需要2个类型,第一个是key第二个是value
基础语法2:#type:类型
# 除了使用变量:类型,这种语法做注解外,也可以在解释中进行类型注解。
import json
from random import random
from _ctypes_test import func
class student:
pass
var1 = random.randint(1,10) # type:int
var2 = json.loads(data) # type:dict[str,int]
var3 = func() # type:student
为变量设置注解,显示的变量定义,一般无需注解,无法直接看出来变量类型之时会添加变量的类型注解
②、方法类型注解
形参注解:
def 函数方法名(形参名:类型,形参名:类型,......):
pass
def test(x:int,y:int):
return x+y
返回值注解:
def 函数方法名(形参:类型,......,形参:类型)-> 返回值类型:
pass
def test(x:int,y:int) -> int:
return x+y
③、Union类型
使用Union可以定义联合类型注解
语法:
导包:from typing import Union
使用:Union[类型,类型,类型]
union联合类型注解,在变量注解、函数(方法)形参和返回值注解中,据可以使用
from typing import Union
my_list: list[Union[int, str]] = [1, 2, 'hello', 'world']
my_dict: dict[str,Union[int, str]] = {'name': 'ikun', 'age': 23}
def func(data: Union[int, str]) -> Union[int, str]:
pass
10、多态
多态指的是,同一个行为,使用不同的对象获得不同的状态
# 定义父类
class animal:
def speak(self):
pass
#子类继承
class dog(animal):
def speak(self):
print("汪汪汪")
#子类继承
class cat(animal):
def speak(self):
print("喵喵喵")
def main(animal):
animal.speak()
dog = dog()
cat = cat()
main(dog)
main(cat)
父类animal的speak方法是空实现,这种设计的含义是:父类用来确定由哪些方法
具体的方法实现,由子类自行决定,这种写法叫抽象类(也可叫接口)
一般是由组长决定执行的内容,然后组员去实现具体的内容
父类做定义声明,子类做实际工作,用以获得同一行为,不同状态