智泊Python--Day13

智泊Python–Day13

  Day13 是面向对象的深水区,把 Day12 的「类与对象」往工程化方向推进。这一天的线索是封装的强化 → 属性的受控访问 → 方法的分类 → 继承与多态 → 特殊机制。重点掌握四个:私有属性/方法(__ 前缀做信息隐藏)、**property(把方法当属性用、还能加校验)、类方法 / 静态方法(不建对象就能调用、省内存)、以及继承 / 重写 / 多继承**(代码复用与行为定制)。最后用运算符重载和一套「学生」继承体系作业收尾。

 

1.私有属性和私有方法

  在属性 / 方法名前加 双下划线 __ 就是私有成员,只能在当前类内部访问;类外直接访问会报错。Python 的「私有」是命名改写实现的——外部其实可通过 _类名__属性 访问到,但强烈不建议,会破坏封装。公有成员则类内外都能用。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 私有属性和私有方法
# 公有属性和公有方法

# 公有属性和公有方法
class Girl:
def __init__(self,name,age):
self.name = name
self.age = age

def dance(self):
print(f"{self.name}今年{self.age}岁,会跳舞了")

# 私有属性和私有方法 在属性名前添加 __ 来表示私有属性
# 私有属性只能在当前类中访问
class Boy:
def __init__(self,name,age):
self.__name = name
self.__age = age

def dance(self):
print(f"{self.__name}今年{self.__age}岁,会跳舞了")
self.__run()

# 私有方法
def __run(self):
print(f"{self.__name}正在跑")


# 对象
g1 = Girl("刘亦菲",18)
g1.dance()
print(g1.name)
print(g1.age)

# 对象
b1 = Boy("吴彦祖",18)
b1.dance()
# print(b1.__name) # 报错,因为私有属性只能在当前类中访问
# print(b1.__age) # 报错,因为私有属性只能在当前类中访问

#扩展
#特殊:可以通过内部的属性调用私有方法
print(b1._Boy__age)
# 但是不建议这样做,因为这样会暴露私有方法,导致代码的可维护性下降

 

2.property属性

  @property 让一个方法像属性一样被读取(不用加 ()),它本质是 getter。规则:必须有返回值、不能有参数、定义在类里。搭配 @属性名.setter 还能写 setter 来赋值并做校验(比如年龄不能负数)。没有 setter 时该属性只读。

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
26
27
28
29
30
31
32
33
34
35
# 作用是让函数变成属性的方法来调用
# 语法:@property
# 1 必须要有返回值
# 2 必须没有参数
# 3 必须在类中定义
# 作用:将方法变成属性的方法来调用


class Person:
def __init__(self, name, age):
self.name = name
self.__age = age

# getter方法 不加参数 用来获取属性的值
@property
def age(self):
return self.__age

# setter方法 加参数 用来修改属性的值
@age.setter
def age(self, new_age):
if new_age >= 0:
self.__age = new_age

# 对象
p = Person('张三', 18)
print(p._Person__age)
print(p.age)
# 不加@property,就需要加入(),因为age是一个方法,需要调用方法的格式:方法名(),否则会报错
# print(p.age())

# 没有setter方法,就不能修改属性的值,会报错
p.age = 33
print(p.age)
print(p._Person__age)

 

3.类方法和静态方法

  方法按归属分三类:

  • 对象方法(带 self):最常见,操作对象
  • 类方法 @classmethod(带 cls):可用 类名.方法() 直接调,不创建对象,省内存;能访问类属性 / 类方法,但访问不了对象属性
  • 静态方法 @staticmethod(无 self 也无 cls):纯工具函数,和类 / 对象都松耦合,推荐用 类名.方法() 调用
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 类
# 属性:类属性,对象属性,私有属性
# 方法:类方法,对象方法,私有方法,静态方法

class Person:

class_name = "人" # 类属性

def __init__(self, name, age):
self.name = name # 对象属性
self.__age = age

# 对象方法==成员方法==公有方法
def run(self):
print(f"作为一个{Person.class_name},{self.name}正在跑,跑完后就会{self.__sleep()}")

# 私有方法
def __sleep(self):
return "睡觉"

# 类方法 (掌握)
# @classmethod
# 类方法可以用类名调用(Person.eat()),也可以用对象名调用(p1.eat()),推荐是用类名调用
# 类方法的作用是,不需要创建对象,就可以调用类方法,从而节省内存
# 类方法中有cls,没有self,可以调用类属性和类方法,但是不能调用对象属性和对象方法
@classmethod
def eat(cls):
# cls 是类本身,可以调用类属性和类方法
print(cls.class_name)
print("吃饭")
return "吃饭"

# 静态方法 (掌握)
# @staticmethod
# 静态方法可以用类名调用(Person.game()),也可以用对象名调用(p1.game()),推荐是用类名调用
# 静态方法的作用是,不需要创建对象,就可以调用静态方法,从而节省内存
# 既没有cls,也没有self,不能调用类属性和类方法,也不能调用对象属性和对象方法
@staticmethod
def game():
print("玩游戏")
return "玩游戏"



p1 = Person("张三", 18)
p1.run()
print("-----------------")
Person.eat()
print("-----------------")
# 静态方法
Person.game()
print("-----------------")

 

4.练习

  用一个 Number 工具类把类方法 / 静态方法练熟:add_num/sub_num/mul_num/div_num 是类方法,直接操作类属性 num;add/sub 是静态方法,纯粹的二元运算。注意类方法通过 cls.num 读写类属性,静态方法不依赖任何状态。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 练习
# 创建一个工具类Number,其中:
# 类属性:num初始值为10
# 类方法:
# add_num(cls) 让num加1
# sub_num(cls) 让num减1
# mul_num(cls,n) 让num乘以n
# div_num(cls,n) 让num除以n

# 静态方法
# add(x,y):求x+y的结果并返回
# sub(x,y):求x-y的结果并返回

class Number:
num = 10

@classmethod
def add_num(cls):
return cls.num + 1

@classmethod
def sub_num(cls):
return cls.num - 1

@classmethod
def mul_num(cls,n):
return cls.num * n

@classmethod
def div_num(cls,n):
return cls.num / n


@staticmethod
def add(x,y):
return x + y

@staticmethod
def sub(x,y):
return x - y

n = Number()
print(n.add_num)
print(n.sub_num)
print(n.mul_num(2))
print(n.div_num(2))
print(Number.add(1,2))
print(Number.sub(1,2))

 

5.继承

  面向对象的四大特征:封装、继承、多态、抽象。继承让子类直接获得父类的属性和方法,实现代码复用。单继承(一个父类)最常见:子类 __init__ 里用 super().__init__(...) 调父类构造来初始化共有部分,再追加自己的属性。Dog/Cat 都继承自 Animal 就是典型场景——公共的 name/age/run/sleep 提到父类。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# 面向对象的4大特征
# 1 封装:属性和方法
# 2 继承:子类可以继承父类的属性和方法
# 3 多态:子类可以重写父类的方法
# 4 抽象:将属性和方法封装在类中,只暴露必要的接口


# 继承:子类继承父类的属性和方法

# 单继承:一个子类只能继承一个父类

# 父类
class IPad:
def __init__(self, color, price):
self.color = color
self.price = price

def movie(self):
print("看电影")

# 子类
class IPhone(IPad):
# 如果子类的属性和父类的属性相同,可以不用写init方法
def __init__(self, color, price, size):
super().__init__(color, price) # 调用父类的init方法来初始化color和price
# IPad.__init__(self, color, price) # 也可以用子类的init方法来初始化color和price
self.model = "iPhone"
self.size = size

def call(self):
print("打电话")

phone16 = IPhone("白色", 9999, "6")
print(phone16.color)
print(phone16.price)
print(phone16.model)
print(phone16.size)
phone16.call()
phone16.movie()
print("-----------------")

# 继承的使用场景
# 子类在父类的基础上,额外扩展功能
# 将不同子类的公共属性和方法,提取到父类中
# 比如 Dog类和Cat类,都有name,age属性和run和sleep方法,就可以提取到父类animal中

class Animal:
def __init__(self, name, age):
self.name = name
self.age = age

def run(self):
print(f"{self.name}正在跑")

def sleep(self):
print(f"{self.name}正在睡觉")

class Dog(Animal):
def __init__(self, name, age):
super().__init__(name, age)
self.kind = "狗"
def bark(self):
print(f"{self.name}正在汪汪叫")

class Cat(Animal):
def __init__(self, name, age):
super().__init__(name, age)
self.kind = "猫"
def meow(self):
print(f"{self.name}正在喵喵叫")


Dog1 = Dog("旺财", 1)
Dog1.run()
Dog1.sleep()
Dog1.bark()
print("-----------------")
Cat1 = Cat("小白", 1)
Cat1.run()
Cat1.sleep()
Cat1.meow()

 

6.多继承

  多继承是「一个子类继承多个父类」,把多个父类的特性合并。Python 用 MRO(方法解析顺序) 决定属性 / 方法按什么顺序查找,可用 类名.__mro__ 查看继承链。下面的 Daughter(Father, Mother) 通过 super() 链式调用分别初始化父亲给的 name 和母亲给的 sex。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 单继承 只有一个父类
# 多继承 多个父类

# 2个父类: 父亲 , 母亲
# 1个子类: 女儿

class Father:
def __init__(self, name):
self.name = name

def smoke(self):
return "吸烟"

class Mother:
def __init__(self, sex):
self.sex = sex

def cook(self):
return "做饭"

#女儿继承父亲的姓,母亲的性别
class Daughter(Father, Mother):
def __init__(self, name, age, sex):
# Father.__init__(self, name) # 显示调用
# Mother.__init__(self, sex) # 显示调用
super(Daughter, self).__init__(name) # 隐式调用 先调用Daughter的__init__方法
super(Father, self).__init__(sex) #链式结构 调用Father的__init__方法
self.age = age
self.kind = "女儿"

def play(self):
return "正在玩"

d1 = Daughter("小红", 18, "女")
print(d1.name)
print(d1.age)
print(d1.sex)
print(d1.kind)
print(d1.play())
print(d1.smoke())
print(d1.cook())

# 继承链
print(Daughter.__mro__) # 查看继承链

 

7.重写

  重写发生在继承基础上:子类和父类有同名方法时,子类的方法会覆盖父类。这正是「多态」的体现——同样是 run(),父类返回「正在跑」、子类 Boy 返回「正在跑快」。外部调用时看对象的实际类型。

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
26
# 重写 方法重写,在继承的基础上,子类和父类有一样的方法名称

class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def run(self):
return "正在跑"

#重写run方法,子类的run方法会覆盖父类的run方法
class Boy(Person):
def __init__(self, name, age):
super().__init__(name, age)
def run(self):
return "正在跑快"

b1 = Boy("小明", 18)
print(b1.run())
print(b1.name)
print(b1.age)
print("="*20)

b2 = Person("小红", 18)
print(b2.run())
print(b2.name)
print(b2.age)

 

8.私有属性不能继承

  再强调一遍封装边界:父类的私有属性(如 __age)只在父类内部可见,子类访问不到——即使通过继承拿到了父类的方法,方法内部能用自己的 __age,但子类自己无法直接 对象.__age。非私有的 name、sex、方法都能正常继承使用。

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
# 私有属性不能继承
# 私有属性只能在当前类中使用,不能在子类中使用

class Person:
def __init__(self, name, age):
self.name = name
self.__age = age

def eat(self):
return "正在吃"

class Boy(Person):
def __init__(self, name, age, sex):
super().__init__(name, age)
self.sex = sex

def sleep(self):
return "正在睡"

b1 = Boy("小明", 18, "男")
print(b1.name)
# print(b1.__age) # 报错
print(b1.sex)
print(b1.eat())
print(b1.sleep())

 

9.特殊属性和运算符重载

  (本节了解即可)Python 里大量以双下划线包裹的成员叫「特殊属性 / 魔法方法」,通常由解释器自动调用。__slots__ 可限制实例能有哪些属性;__class__ / __dict__ 用于自省;__str__ / __repr__ 控制打印样式;__add__ 等则实现运算符重载——让自定义对象支持 +、直接 print 等原生语法。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# 特殊属性和运算符重载(仅了解)

# __slots__属性
# 用来限制属性名

class Person:

# 表示该类的实例只能有name、age、sex这三个属性
__slots__ = ["name", "age", "sex"]

def __init__(self, name, age):
self.name = name
self.age = age

p = Person("张三", 18)
p.sex = "男"
# p.sex2 = "男" # 会报错
print(p.name, p.age, p.sex)

# 特殊属性:了解
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age


d = Dog("旺财", 1)
print(d)
print("="*50)

# __class__属性
print(d.__class__) # <class '__main__.Dog'>
print(Dog.__class__) # <class 'type'>

# __dict__属性
# 用来存储实例的属性,组成一个字典
print(d.__dict__) # {'name': '旺财', 'age': 1}
print("="*50)


d2 = Dog(**{"name": "旺财", "age": 1})
print(d2)
d2 = Dog(name="旺财", age=1) # 与上一行效果相同
print(d2)
print("="*50)

# 魔法方法:一般是自动调用
# __new__()方法:用来创建实例对象
# 该方法返回一个实例对象
# 如果不返回实例对象,则会报错
# def __new__(cls, name, age):
# return super().__new__(cls)

# # __init__()方法:用来初始化实例对象
# def __init__(self, name, age):
# self.name = name
# self.age = age

# # __str__()方法:用来返回实例对象的字符串表示
# def __str__(self):
# return f"{self.name}的年龄是{self.age}"

# # __del__()方法:用来删除实例对象
# def __del__(self):
# print("del方法")

# 运算符重载:扩展
class Number:
def __init__(self, num):
self.num = num

def __add__(self, other):
return Number(self.num + other.num)

# 比较重要
# __str__()方法:用来返回实例对象的字符串表示
def __str__(self):
return f"--{self.num}--" # 让实例对象的字符串表示包含--符号

# __repr__()方法:用来返回实例对象的字符串表示,主要是用来列表中显示
def __repr__(self):
print("repr__方法")
return f"**{self.num}**"



n1 = Number(10)
n2 = Number(20)
print(n1 + n2)
print(n1)
print([n1, n2])

 

10.作业

  综合作业:用封装 + 继承 + 重写实现「学生」体系。student 作为父类,带着 name/id/age/sex 和 learn();Pupil(小学生)、Middle_student(中学生)、College(大学生)各自继承并**重写 learn()**,打印不同的学习内容。这个结构正是 Day12 封装与本节继承 / 多态的完整串联。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 1. 利用封装和继承的特性完成如下操作:
# 小学生:
# 属性: 姓名 学号 年龄 性别
# 行为: 学习 打架
#
# 中学生:
# 属性: 姓名 学号 年龄 性别
# 行为: 学习 谈恋爱
#
# 大学生:
# 属性: 姓名 学号 年龄 性别
# 行为: 学习 打游戏

# 调用:
# 创建小学生对象
# 调用学习的方法
# 打印内容为: xx 学习的内容为:语文 数学 英语
#
# 创建中学生对象
# 调用学习的方法
# 打印内容为: xx 学习的内容为:语数外 生物化 史地政
#
# 创建大学生对象
# 调用学习的方法:
# 打印内容为: 逃课中..

class student:
def __init__(self, name, id, age, sex):
self.name = name
self.id = id
self.age = age
self.sex = sex
def learn(self):
print(f"{self.name}正在学习")



class Pupil(student):
def __init__(self, name, id, age, sex):
super().__init__(name, id, age, sex)

def learn(self):
print(f"{self.name}正在学习语文 数学 英语")

class Middle_student(student):
def __init__(self, name, id, age, sex):
super().__init__(name, id, age, sex)

def learn(self):
print(f"{self.name}正在学习语数外 生物化 史地政")

class College(student):
def __init__(self, name, id, age, sex):
super().__init__(name, id, age, sex)

def learn(self):
print(f"{self.name}正在逃课中..")

智泊Python--Day13
https://one-null-pointer.github.io/2026/08/01/智泊Python--Day13/
Author
liaoyue
Posted on
August 1, 2026
传送口