初识对象

在程序中是可以做到和生活中那样,设计表格、生产表格、填写表格的组织形式的。

1
2
3
4
5
6
7
8
9
10
11
12
# 在程序中设计表格,我们称之为:设计类(class)
class Student:
name = None

# 在程序中打印生产表格,我们称之为:创建对象
# 基于类创建对象
stu_1 = Student()
stu_2 = Student()

# 在程序中填写表格,我们称之为:对象属性赋值
stu_1.name ="周杰轮" # 为学生1对象赋予名称属性值
stu_2.name ="林军杰" # 为学生2对象赋予名称属性值

成员方法

类的语法:

1
2
3
4
class 类名称# class是关键字,表示要定义类
类的属性 # 类的属性,即定义在类中的变量(成员变量)

类的行为 # 类的行为,即定义在类中的函数(成员方法)

注意:函数是写在类外的,定义在类内部的函数习惯上称为方法

成员方法的语法

1
2
3
4
5
6
7
class 类名称:
成员变量

def 成员方法(self, 参数列表):
成员方法体

对象 = 类名称()

self的作用:

  • 表示类对象本身的意思
  • 只有通过self,成员方法才能访问类的成员变量
  • self出现在形参列表中,但是不占用参数位置,因此调用时无需理会

创建类对象的语法:

1
对象 = 类名称()

类和对象

类和对象的关系是什么?

  • 类是程序中的“设计图纸”
  • 对象是基于图纸生产的具体实体

面向对象编程:使用对象进行编程。即设计类,基于类创建对象,并使用对象来完成具体的工作


构造方法

Python类可以使用:__init__()方法,称之为构造方法。

  • 在创建类对象(构造类)的时候,会自动执行。
  • 在创建类对象(构造类)的时候,传入参数时会自动传递给__init__方法使用。
1
2
3
4
5
6
7
8
9
10
11
12
class Student:
name = None # 可以省略
age = None # 可以省略
tel = None # 可以省略

def __init__(self,name,age, tel):
self.name = name
self.age = age
self.tel = tel
print("student类创建了一个对象")

stu = Student("周杰轮"31,"18500006666")

注意:构造方法也是成员方法,不能忘记在参数列表中提供self


魔术方法

__init__构造方法,是Python类内置的方法之一。
这些内置的类方法各自有各自特殊的功能,称之为魔术方法

__str__字符串魔术方法

当类对象需要被转换为字符串时,会输出其内存地址

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

student = Student("周杰轮", 11)
print(student) # 结果:<__main__.Student object at 0x000002200cFD7040>
print(str(student)) # 结果:<__main__.Student object at Ox000002200cFD7040>

但是内存地址没有多大作用,可以通过str 方法,控制类转换为字符串的行为

1
2
3
4
5
6
7
8
9
10
11
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("周杰轮", 11)
print(student) # 结果:Student类对象,name=周杰轮,age=11
print(str(student)) # 结果:Student类对象,name=周杰轮,age=l1

__lt__小于符号魔术方法

两个对象之间不可以直接进行比较

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

stu1 = Student("周杰伦",11)
stu2 = Student("林俊杰",13)
print(stul < stu2) # 此处无法比较,会直接报错

此时需要在类中实现__lt__方法,即可同时完成:小于符号和大于符号2种比较
1
2
3
4
5
6
7
8
9
10
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)
print(stu1 < stu2) # 结果:True
print(stu1 > stu2) # 结果:False

__le__小于等于符号魔术方法

1
2
3
4
5
6
7
8
9
10
11
12
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
print(stu2 >= stu2) # 结果:False

__eq__比较运算符魔术方法

1
2
3
4
5
6
7
8
9
10
11
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大主要特性:

  • 封装
  • 继承
  • 多态

封装是指将现实世界事物在类中描述为属性和方法。

现实事物的部分属性和行为是不公开对使用者开放的。同样在类中描述属性和方法的时候也需要达到这个要求

定义私有成员:成员变量和成员方法的命名均以__作为开头

1
2
3
4
5
class Phone:  
__current_voltage = 1

def __keep_single_core(self):
print("让cpu以单核模式运行")

注意:类对象无法访问私有成员,类中的其它成员可以访问私有成员


继承

继承的基础语法

继承:将从父类那里继承(复制)来成员变量和成员方法(不含私有)

子类构建的类对象,可以

  • 有自己的成员变量和成员方法
  • 使用父类的成员变量和成员方法

单继承和多继承

类的单继承语法:

1
2
class 类名(父类名):
类内容体

类的多继承语法:

1
class 类名(父类1, 父类2, ..., 父类N):

注意:多继承中,如果父类有同名方法或属性,先继承的优先级高于后继承

pass

pass是占位语句,用来保证函数(方法)或类定义的完整性,表示无内容,空的意思


复写和使用父类成员

复写

对父类的成员属性或成员方法进行重新定义

调用父类成员

一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员。
如果需要使用被复写的父类的成员,需要特殊的调用方式:

1
2
3
4
5
6
7
# 方式1:调用父类成员
使用成员变量:父类名.成员变量
使用成员方法:父类名.成员方法(self)

# 方式2:使用super()调用父类成员
使用成员变量:super().成员变量
使用成员方法:super().成员方法()

类型注解

类型注解支持:

  1. 变量的类型注解
  2. 函数(方法)的形参和返回值的类型注解

变量的类型注解

语法1:变量:类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 基础数据类型注解
var_1: int = 10
var_2: float = 3.1415926
var_3: bool = True
Var_4: str = "itheima”

# 基础容器类型注解
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"

# 容器类型详细注解
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

# 类对象类型注解
class Student:
pass
stu: Student = Student()

语法2:在注释中,#type:类型

1
2
3
4
5
6
# 在注释中进行类型注解
class Student:
pass
var_1 = random.randint(1, 10) # type:int
var_2 = json.loads(data) # type: dict[str, int]
var_3 = func() # type: Student

注意:类型注解只是提示性的,并非决定性的。数据类型和注解类型无法对应也不会导致编译错误

函数(方法)的类型注解

形参的类型注解

1
2
3
4
5
def add(x: int, y: int):
return x + y

def func(data: list):
pass

返回值的类型注解

1
2
def 函数方法名(形参: 类型,......, 形参:类型) -> 返回值类型:
pass

Union联合类型注解

1
2
3
4
5
6
7
8
9
# 变量的联合类型注解
from typing import Union

my_list: list[union[str, int]] = [1, 2, "itheima", "itcast"]
my_dict: dict[str, Union[str, int]] = {"name": "周杰伦", "age":31}

# 函数(方法)的联合类型注解
def func(data: union[int, str]) -> union[int, str]:
pass

多态

多态常用在继承关系上。

  • 以父类做定义声明
  • 以子类做实际工作
  • 用以获得同一行为,不同状态
    这种写法,就叫做抽象类(也可以称之为接口)

抽象类:含有抽象方法的类称之为抽象类
抽象方法:方法体是空实现的(pass)称之为抽象方法

Repository

本章示例代码已归档至 GitHub,可用于本地复现、调试与扩展。

查看本章源码