08-27-周三_17-09-29
443
Python/Python面向对象/初识面向对象.md
Normal file
@@ -0,0 +1,443 @@
|
||||
# 面向过程
|
||||
|
||||
面向过程编程(Procedural Programming)是一种以**步骤和过程**为核心的编程范式,其核心思想是将复杂问题分解为一系列可顺序执行的函数或过程,通过逐步调用来实现整体功能
|
||||
|
||||
## 核心思想
|
||||
|
||||
- **步骤分解**:将问题拆解为多个子任务,每个子任务由独立的**函数/过程**实现。例如,处理学生早上的活动可分解为“起床→穿衣→洗漱→去学校”等步骤,每个步骤对应一个函数。
|
||||
- **顺序执行**:程序按代码的书写顺序从上到下执行,通过**条件语句**(如if)和**循环结构**(如for)控制流程
|
||||
- **数据与操作分离**:数据存储在全局或局部变量中,函数通过参数接收数据并处理,结果通过返回值或修改变量传递。
|
||||
|
||||
## 典型特征
|
||||
|
||||
- **模块化函数**:功能封装为函数,例如计算两数之和的函数 `add()`,通过调用实现代码复用。
|
||||
- **线性流程**:程序逻辑清晰,易于调试。例如读取文件数据→处理数据→输出结果的流程。
|
||||
- **高效性**:适用于简单任务或对性能要求高的场景,因无需对象创建开销。
|
||||
|
||||
# 面向对象
|
||||
|
||||
面向对象编程(Object-Oriented Programming,OOP)是一种以**对象**为核心的编程范式,通过模拟现实世界中事物的交互逻辑来构建程序。其核心思想是将数据与操作数据的方法封装成独立的对象,通过对象之间的协作实现复杂功能
|
||||
|
||||
## 核心概念
|
||||
|
||||
1. **类(Class)**:定义对象的模板,描述一类事物的**共性特征**(如属性)和**行为**(如方法)。例如,"汽车"类包含属性"颜色"和方法"加速"。
|
||||
2. **对象(Object)**:类的具体实例,拥有独立的**状态**(属性值)和**行为**。例如,一辆红色汽车是"汽车"类的对象
|
||||
3. **封装(Encapsulation)**:将数据和方法捆绑在对象内部,仅通过暴露的接口与外界交互,保护数据安全并简化使用。例如,银行账户的余额只能通过特定方法修改。
|
||||
4. **继承(Inheritance)**:子类可复用父类的属性和方法,并扩展新功能,实现代码复用和逻辑分层。例如,"电动车"类继承自"汽车"类,新增"充电"方法。
|
||||
5. **多态(Polymorphism)**:同一方法在不同对象中呈现不同行为,增强代码灵活性。例如,"动物"类的"发声"方法在"狗"和"猫"对象中分别输出"汪汪"和"喵喵"。
|
||||
6. **抽象(Abstraction)**:提取共性特征形成接口或抽象类,隐藏复杂实现细节,例如定义"图形"类的抽象方法"计算面积"。
|
||||
|
||||
## 核心优势
|
||||
|
||||
- **可维护性**:对象间低耦合,修改某部分代码不影响整体系统。
|
||||
- **可扩展性**:通过继承和多态灵活扩展功能,无需重写现有代码。
|
||||
- **复用性**:封装后的类可跨项目重复使用,减少冗余代码。
|
||||
- **逻辑直观**:以现实世界模型组织代码,更符合人类认知。
|
||||
|
||||
# 两者对比
|
||||
|
||||
## 面向过程—怎么做
|
||||
|
||||
1. 把完成某一个需求的 `所有步骤` `从头到尾` 逐步实现
|
||||
2. 根据开发需求,将某些功能独立的代码封装成一个又一个函数
|
||||
3. 最后完成的代码,就是顺序地调用不同的函数
|
||||
|
||||
注重步骤与过程,不注重职责和分工,如果需求比较复杂,虽然有函数封装,但是还是会导致代码比较臃肿。开发起来比较复杂。
|
||||
|
||||
<img src="初识面向对象/面向过程.png" alt="img-面向过程" style="zoom: 50%;" />
|
||||
|
||||
## 面向对象—谁来做
|
||||
|
||||
相比较函数,面向对象是更大的封装,根据职责在一个对象中封装多个方法
|
||||
|
||||
1. 在完成某一个需求前,首先确定职责 —— 要做的事情(方法)
|
||||
2. 根据职责确定不同的对象,在对象内部封装不同的方法(多个)
|
||||
3. 最后完成的代码,就是顺序地让不同的对象调用不同的方法
|
||||
|
||||
**特点**
|
||||
|
||||
1. 注重对象和职责,不同的对象承担不同的职责。
|
||||
2. 更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路。
|
||||
3. 需要在面向过程基础上,再学习一些面向对象的语法。
|
||||
|
||||
<img src="初识面向对象/植物大战僵尸.png" alt="img-植物大战僵尸" style="zoom:80%;" />
|
||||
|
||||
<img src="初识面向对象/植物大战僵尸类.png" alt="img-植物大战僵尸类" style="zoom:80%;" />
|
||||
|
||||
# 类与对象
|
||||
|
||||
在面向对象中,类与对象是两个核心的概念
|
||||
|
||||
## 类
|
||||
|
||||
- 类是对一群具有相同特征或者行为的事物的一个统称,是抽象的,不能直接使用
|
||||
- **特征** 被称为 **属性**
|
||||
- **行为** 被称为 **方法**
|
||||
- 类就相当于制造飞机时的图纸,是一个模板,是负责创建对象的。
|
||||
|
||||
<img src="初识面向对象/飞机设计图纸.png" alt="img-飞机设计图纸" style="zoom:80%;" />
|
||||
|
||||
## 对象
|
||||
|
||||
- 对象是由类创建出来的一个具体存在,可以直接使用。
|
||||
- 由哪一个类创建出来的对象,就拥有在哪一个类中定义的:属性 & 方法。
|
||||
- 对象就相当于用图纸制造的飞机。
|
||||
|
||||
<img src="初识面向对象/飞机对象.png" alt="img-飞机对象" style="zoom: 80%;" />
|
||||
|
||||
## 类与对象的关系
|
||||
|
||||
- 类是模板,对象是根据类这个模板创建出来的,应该先有类,再有对象。
|
||||
- 类只有一个,而对象可以有很多个:不同的对象之间属性可能会各不相同。
|
||||
- 类中定义了什么属性和方法,对象中就有什么属性和方法,不可能多,也不可能少。
|
||||
|
||||
## 类的设计
|
||||
|
||||
在使用面相对象开发前,应该首先分析需求,确定一下,程序中需要包含哪些类
|
||||
|
||||
<img src="初识面向对象/植物大战僵尸类.png" alt="img-植物大战僵尸类" style="zoom:80%;" />
|
||||
|
||||
在程序开发中,要设计一个类,通常需要满足一下三个要素:
|
||||
|
||||
1. **类名** 这类事物的名字,**满足大驼峰命名法**
|
||||
2. **属性** 这类事物具有什么样的特征
|
||||
3. **方法** 这类事物具有什么样的行为
|
||||
|
||||
**大驼峰命名法**
|
||||
|
||||
```python
|
||||
CapWords
|
||||
```
|
||||
|
||||
**类名的确定**
|
||||
|
||||
通常类名的选择,我们应该根据整个业务流程来提取,或者从大的角度来选择
|
||||
|
||||
**属性和方法的确定**
|
||||
|
||||
- 对对象的特征描述,通常可以定义成属性
|
||||
- 对象具有的行为,通常可以定义成方法
|
||||
|
||||
## 类的定义
|
||||
|
||||
类的基本定于语法如下:
|
||||
|
||||
```python
|
||||
class Human:
|
||||
'''
|
||||
这里可以写上对于这个类的说明
|
||||
'''
|
||||
变量 = xxxx # 这里是类的静态属性,也可以理解为该类共有的特性
|
||||
dic = {}
|
||||
l1 = []
|
||||
def __init__(self,xxx,xxx): # 初始化方法
|
||||
pass
|
||||
|
||||
def func(self): # 方法,动态属性
|
||||
pass
|
||||
```
|
||||
|
||||
**示例**:
|
||||
|
||||
定义一个人"类",人类有思想,并且具备姓名,年龄,身高。人类还会吃饭,还会跑步等
|
||||
|
||||
<img src="初识面向对象/人类.png" alt="img-人类" style="zoom:80%;" />
|
||||
|
||||
```python
|
||||
class Human(object): # 默认继承自 object
|
||||
"""
|
||||
此类用来构造人类
|
||||
"""
|
||||
mind = "思考问题.."
|
||||
def __init__(self,name,age,height):、
|
||||
# 在__init__中,通过self给对象封装属性
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.height = height
|
||||
|
||||
def run(self):
|
||||
print('高高兴兴的跑步')
|
||||
|
||||
def eat(self):
|
||||
print('大口大口的吃饭')
|
||||
```
|
||||
|
||||
这里的 object 和 self 的解释如下:
|
||||
|
||||
`object` 是 Python 中所有类的基类。它是所有用户自定义类和内置类的顶层父类。可以理解为所有的类都继承自 object 类,所以才能具备类的初始化等许多基础特性。
|
||||
|
||||
`self` 是一个约定俗成的参数名,用于引用类的实例。它代表当前对象的实例,使我们能够访问实例的属性和方法。
|
||||
|
||||
在实例方法中,第一个参数通常命名为 `self`,但你可以使用其他名称(虽然不推荐)。
|
||||
|
||||
## 实例化对象
|
||||
|
||||
通过类生成具体的对象的过程,我们称之为实例化。
|
||||
|
||||
```python
|
||||
class Human(object): # 默认继承自 object
|
||||
"""
|
||||
此类用来构造人类
|
||||
"""
|
||||
mind = "思考问题.."
|
||||
def __init__(self,name,age,height):
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.height = height
|
||||
|
||||
def run(self):
|
||||
print('高高兴兴的跑步')
|
||||
|
||||
def eat(self):
|
||||
print('大口大口的吃饭')
|
||||
|
||||
# 实例化对象
|
||||
xiaoming = Human('小明',18, 173.5)
|
||||
xiaohong = Human('小红',20, 165)
|
||||
```
|
||||
|
||||
这里的小明和小红就是我们通过 Human 这个类实例化出来的具体的对象
|
||||
|
||||
其实实例化一个对象总共发生了三件事:
|
||||
|
||||
1. 在内存中开辟了一个对象空间。
|
||||
2. 自动执行类中的 `__init__` 方法,并将这个对象空间(内存地址)传给了 `__init__` 方法的第一个位置参数 self。
|
||||
3. 在 `__init__` 方法中通过 self 给对象空间添加属性。
|
||||
|
||||
|
||||
**对象访问类中的属性和方法**
|
||||
|
||||
```python
|
||||
# 访问静态属性
|
||||
print(xiaoming.mind)
|
||||
print(xiaohong.name)
|
||||
print(xiaohong.age)
|
||||
|
||||
# 访问动态方法
|
||||
xiaohong.run()
|
||||
xiaoming.eat()
|
||||
|
||||
```
|
||||
|
||||
**查看对象的所有属性**
|
||||
|
||||
我们可以通过 object 基类中提供的 `__dict__` 方法来查看某个对象的属性。
|
||||
|
||||
```python
|
||||
# 通过对象查看类中所有的属性
|
||||
print(xiaoming.__dict__)
|
||||
```
|
||||
|
||||
# 从类名的角度研究类
|
||||
|
||||
## 类名操作静态属性
|
||||
|
||||
查看类中所有的内容,用 `类名.__dict__`
|
||||
|
||||
```python
|
||||
class Human(object): # 默认继承自 object
|
||||
"""
|
||||
此类用来构造人类
|
||||
"""
|
||||
mind = "思考问题.."
|
||||
# 在__init__中,通过self给对象封装属性
|
||||
def __init__(self,name,age,height):
|
||||
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.height = height
|
||||
|
||||
def run(self):
|
||||
print('高高兴兴的跑步')
|
||||
|
||||
def eat(self):
|
||||
print('大口大口的吃饭')
|
||||
|
||||
print(Human.__dict__)
|
||||
print(Human.__dict__['mind'])
|
||||
Human.__dict__['mind'] = '高智慧'
|
||||
# 通过这种方式只能查询,不能增删改
|
||||
print(Human.__dict__)
|
||||
|
||||
```
|
||||
|
||||
**万能的点 `.`**
|
||||
|
||||
在面向对象中,我们更多的是使用 `.` 来获取类或者对象的属性或方法
|
||||
|
||||
```python
|
||||
class Human(object): # 默认继承自 object
|
||||
"""
|
||||
此类用来构造人类
|
||||
"""
|
||||
mind = "思考问题.."
|
||||
|
||||
def __init__(self, name, age, height):
|
||||
# 在__init__中,通过self给对象封装属性
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.height = height
|
||||
|
||||
def run(self):
|
||||
print('高高兴兴的跑步')
|
||||
|
||||
def eat(self):
|
||||
print('大口大口的吃饭')
|
||||
|
||||
|
||||
print(Human.mind)
|
||||
Human.mind = '高智慧'
|
||||
print(Human.mind)
|
||||
|
||||
del Human.mind
|
||||
Human.run = '慢慢悠悠的走路'
|
||||
print(Human.run)
|
||||
# 通过万能的点 可以增删改查类中的单个属性
|
||||
print('大口大口的吃饭')
|
||||
```
|
||||
|
||||
总结:如果想要查看类的内容,我们可以使用`__dict__`方法,如果想要操作类中的某个属性,可以使用`.`
|
||||
|
||||
## 类名操作动态方法
|
||||
|
||||
```python
|
||||
class Human(object): # 默认继承自 object
|
||||
"""
|
||||
此类用来构造人类
|
||||
"""
|
||||
mind = "思考问题.."
|
||||
# 在__init__中,通过self给对象封装属性
|
||||
def __init__(self, name, age, height):
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.height = height
|
||||
|
||||
def run(self):
|
||||
print(self,'高高兴兴的跑步')
|
||||
|
||||
def eat(self):
|
||||
print(self,'大口大口的吃饭')
|
||||
|
||||
# 可以直接通过human调用动态方法,也可以通过dict为类内部方法传递实参
|
||||
Human.eat('小明')
|
||||
Human.__dict__['run']('小红')
|
||||
|
||||
```
|
||||
|
||||
# 从对象的角度研究类
|
||||
|
||||
## 对象操作对象属性
|
||||
|
||||
对象也可以通过 `__dict__` 查看对象的所有属性
|
||||
|
||||
|
||||
```python
|
||||
class Human(object): # 默认继承自 object
|
||||
"""
|
||||
此类用来构造人类
|
||||
"""
|
||||
mind = "思考问题.."
|
||||
|
||||
def __init__(self, name, age, height):
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.height = height
|
||||
|
||||
def run(self):
|
||||
print('高高兴兴的跑步')
|
||||
|
||||
def eat(self):
|
||||
print('大口大口的吃饭')
|
||||
|
||||
|
||||
# 实例化出一个具体的对象
|
||||
xiaoming = Human('小明', 18, 173.5)
|
||||
xiaohong = Human('小红', 20, 165)
|
||||
|
||||
print(xiaoming.__dict__)
|
||||
```
|
||||
|
||||
同样也可以使用万能的点操作对象属性
|
||||
|
||||
```python
|
||||
class Human(object): # 默认继承自 object
|
||||
"""
|
||||
此类用来构造人类
|
||||
"""
|
||||
mind = "思考问题.."
|
||||
|
||||
def __init__(self, name, age, height):
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.height = height
|
||||
|
||||
def run(self):
|
||||
print('高高兴兴的跑步')
|
||||
|
||||
def eat(self):
|
||||
print('大口大口的吃饭')
|
||||
|
||||
|
||||
# 实例化出一个具体的对象
|
||||
xiaoming = Human('小明', 18, 173.5)
|
||||
xiaohong = Human('小红', 20, 165)
|
||||
|
||||
# 修改属性
|
||||
xiaoming.name = "小小明"
|
||||
|
||||
# 增加属性
|
||||
xiaoming.sex = "男"
|
||||
print(xiaoming.sex)
|
||||
|
||||
# 删除属性
|
||||
del xiaoming.height
|
||||
|
||||
# 查看属性
|
||||
print(xiaoming.__dict__)
|
||||
```
|
||||
|
||||
## 对象查看类的属性
|
||||
|
||||
```python
|
||||
class Human(object): # 默认继承自 object
|
||||
"""
|
||||
此类用来构造人类
|
||||
"""
|
||||
mind = "思考问题.."
|
||||
|
||||
def __init__(self, name, age, height):
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.height = height
|
||||
|
||||
def run(self):
|
||||
print('高高兴兴的跑步')
|
||||
|
||||
def eat(self):
|
||||
print('大口大口的吃饭')
|
||||
|
||||
|
||||
# 实例化出一个具体的对象
|
||||
xiaoming = Human('小明', 18, 173.5)
|
||||
xiaohong = Human('小红', 20, 165)
|
||||
|
||||
print(xiaoming.mind)
|
||||
```
|
||||
|
||||
# 类的内置方法
|
||||
|
||||
| 方法名 | 作用 |
|
||||
|:-------- | :------------------------------------------- |
|
||||
| `__new__` | 创建对象时,会被自动调用 |
|
||||
| `__init__`| 对象被初始化时,会被自动调用 |
|
||||
| `__del__` | 对象被从内存中销毁前,会被自动调用 |
|
||||
| `__str__` | 返回对象的描述信息,`print` 函数输出使用 |
|
||||
|`__dir__` | 查看对象内所有属性以及方法 |
|
||||
|
||||
# 总结
|
||||
|
||||
1. 现有类才有对象,类是模版,对象是通过类实例化出来的。
|
||||
2. 一个类可以实例化很多个对象。
|
||||
3. 类中包含静态属性和动态方法,包括内置方法,自定义方法。
|
||||
4. 实例化对象的时候,会自动调用 `__init__` 来初始化,我们可以在 `__init__` 中定义对象初始化的属性
|
||||
5. 实例化好的对象可以调用类中的方法或者是静态属性
|
BIN
Python/Python面向对象/初识面向对象/人类.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
Python/Python面向对象/初识面向对象/植物大战僵尸.png
Normal file
After Width: | Height: | Size: 969 KiB |
BIN
Python/Python面向对象/初识面向对象/植物大战僵尸类.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
Python/Python面向对象/初识面向对象/面向过程.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
Python/Python面向对象/初识面向对象/飞机对象.png
Normal file
After Width: | Height: | Size: 5.0 MiB |
BIN
Python/Python面向对象/初识面向对象/飞机设计图纸.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
345
Python/Python面向对象/反射与双下方法.md
Normal file
@@ -0,0 +1,345 @@
|
||||
# 反射
|
||||
|
||||
Python 中,反射(Reflection)是一种能力,使得程序能够在运行时查看和修改自身的结构和行为。通过反射,您可以动态地访问和操作类的属性和方法,而无需在编写代码时确定它们的确切名称。这在某些情况下非常有用,例如在框架、库或插件系统中。
|
||||
|
||||
## 对对象的反射
|
||||
|
||||
```python
|
||||
class Foo:
|
||||
f = "类的静态变量"
|
||||
|
||||
def __init__(self, name, age):
|
||||
self.name = name
|
||||
self.age = age
|
||||
|
||||
def say_hi(self):
|
||||
print("Hi %s" % self.name)
|
||||
|
||||
|
||||
obj = Foo("EaglesLab", 18)
|
||||
|
||||
# 检测是否含有某属性
|
||||
print(hasattr(obj, "name"))
|
||||
print(hasattr(obj, "say_hi"))
|
||||
|
||||
# 获取属性
|
||||
print(getattr(obj, "name"))
|
||||
func = getattr(obj, "say_hi")
|
||||
func()
|
||||
|
||||
print(getattr(obj, "job", "不存在啊")) # 报错
|
||||
|
||||
|
||||
# 设置属性
|
||||
setattr(obj, "job", "teacher")
|
||||
setattr(obj, "show_name", lambda self: self.name + " 真帅")
|
||||
print(obj.__dict__)
|
||||
print(obj.show_name(obj))
|
||||
|
||||
# 删除属性
|
||||
delattr(obj, "age")
|
||||
delattr(obj, "show_name")
|
||||
# delattr(obj,'show_name111') # 不存在,则报错
|
||||
print(obj.__dict__)
|
||||
```
|
||||
|
||||
## 对类的反射
|
||||
|
||||
```python
|
||||
class Foo(object):
|
||||
staticField = "test"
|
||||
|
||||
def __init__(self):
|
||||
self.name = "陈松"
|
||||
|
||||
def func(self):
|
||||
return "func"
|
||||
|
||||
@staticmethod
|
||||
def bar():
|
||||
return "bar"
|
||||
|
||||
|
||||
print(getattr(Foo, "staticField"))
|
||||
print(getattr(Foo, "func"))
|
||||
print(getattr(Foo, "bar"))
|
||||
```
|
||||
|
||||
## 案例:基于反射的用户管理
|
||||
|
||||
使用反射前
|
||||
|
||||
```python
|
||||
class User:
|
||||
def login(self):
|
||||
print('欢迎来到登录页面')
|
||||
|
||||
def register(self):
|
||||
print('欢迎来到注册页面')
|
||||
|
||||
def save(self):
|
||||
print('欢迎来到存储页面')
|
||||
|
||||
user = User()
|
||||
while 1:
|
||||
choose = input('>>>').strip()
|
||||
if choose == 'login':
|
||||
user.login()
|
||||
|
||||
elif choose == 'register':
|
||||
user.register()
|
||||
|
||||
elif choose == 'save':
|
||||
user.save()
|
||||
```
|
||||
|
||||
用了反射之后
|
||||
|
||||
```python
|
||||
class User:
|
||||
def login(self):
|
||||
print('欢迎来到登录页面')
|
||||
|
||||
def register(self):
|
||||
print('欢迎来到注册页面')
|
||||
|
||||
def save(self):
|
||||
print('欢迎来到存储页面')
|
||||
|
||||
user = User()
|
||||
while 1:
|
||||
choose = input('>>>').strip()
|
||||
if hasattr(user, choose):
|
||||
func = getattr(user, choose)
|
||||
func()
|
||||
else:
|
||||
print('输入错误...')
|
||||
```
|
||||
|
||||
# 函数 vs 方法
|
||||
|
||||
## 通过打印函数(方法)名确定
|
||||
|
||||
```python
|
||||
def func():
|
||||
pass
|
||||
|
||||
print(func)
|
||||
|
||||
class A:
|
||||
def func(self):
|
||||
pass
|
||||
|
||||
print(A.func)
|
||||
obj = A()
|
||||
print(obj.func)
|
||||
```
|
||||
|
||||
## 通过 types 模块验证
|
||||
|
||||
```python
|
||||
from types import FunctionType
|
||||
from types import MethodType
|
||||
|
||||
def func():
|
||||
pass
|
||||
|
||||
class A:
|
||||
def func(self):
|
||||
pass
|
||||
|
||||
obj = A()
|
||||
|
||||
print(isinstance(func,FunctionType))
|
||||
print(isinstance(A.func,FunctionType))
|
||||
print(isinstance(obj.func,FunctionType))
|
||||
print(isinstance(obj.func,MethodType))
|
||||
```
|
||||
|
||||
## 静态方法是函数
|
||||
|
||||
```python
|
||||
from types import FunctionType
|
||||
from types import MethodType
|
||||
|
||||
|
||||
class A:
|
||||
|
||||
def func(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def func1(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def func2(self):
|
||||
pass
|
||||
|
||||
|
||||
obj = A()
|
||||
|
||||
# 静态方法其实是函数
|
||||
print(isinstance(A.func2,FunctionType))
|
||||
print(isinstance(obj.func2,FunctionType))
|
||||
```
|
||||
|
||||
## 函数与方法的区别
|
||||
|
||||
那么,函数和方法除了上述的不同之处,我们还总结了一下几点区别。
|
||||
|
||||
1. 函数的是显式传递数据的。如我们要指明为len()函数传递一些要处理数据。
|
||||
2. 函数则跟对象无关。
|
||||
3. 方法中的数据则是隐式传递的。
|
||||
4. 方法可以操作类内部的数据。
|
||||
5. 方法跟对象是关联的。如我们在用 strip() 方法是,是不是都是要通过 str 对象调用,比如我们有字符串 s,然后 s.strip() 这样调用。是的,strip()方法属于str对象。
|
||||
|
||||
我们或许在日常中会口语化称呼函数和方法时不严谨,但是我们心中要知道二者之间的区别。
|
||||
|
||||
在其他语言中,如 Java 中只有方法,C 中只有函数,C++ 则取决于是否在类中。
|
||||
|
||||
# 双下方法
|
||||
|
||||
## `__init__`
|
||||
|
||||
用于初始化类的实例,接收参数并设置实例属性。
|
||||
|
||||
```python
|
||||
class Person:
|
||||
def __init__(self, name, age):
|
||||
self.name = name
|
||||
self.age = age
|
||||
```
|
||||
|
||||
## `__len__`
|
||||
|
||||
```python
|
||||
class B:
|
||||
def __len__(self):
|
||||
return 666
|
||||
|
||||
b = B()
|
||||
print(len(b)) # len一个对象就会触发 __len__方法。
|
||||
|
||||
class A:
|
||||
def __init__(self):
|
||||
self.a = 1
|
||||
self.b = 2
|
||||
|
||||
def __len__(self):
|
||||
return len(self.__dict__)
|
||||
a = A()
|
||||
print(len(a))
|
||||
```
|
||||
|
||||
## `__hash__`
|
||||
|
||||
```python
|
||||
class A:
|
||||
def __init__(self):
|
||||
self.a = 1
|
||||
self.b = 2
|
||||
|
||||
def __hash__(self):
|
||||
return hash(str(self.a)+str(self.b))
|
||||
a = A()
|
||||
print(hash(a))
|
||||
```
|
||||
|
||||
## `__str__`
|
||||
|
||||
如果一个类中定义了 `__str__` 方法,那么在 `print(obj)` 时,默认输出该方法的返回值。
|
||||
|
||||
```python
|
||||
class A:
|
||||
def __init__(self):
|
||||
pass
|
||||
def __str__(self):
|
||||
return '陈松'
|
||||
a = A()
|
||||
print(a)
|
||||
print('%s' % a)
|
||||
```
|
||||
|
||||
## `__repr__`
|
||||
|
||||
如果一个类中定义了 `__repr__` 方法,那么在` repr(obj)` 时,默认输出该方法的返回值。
|
||||
|
||||
```python
|
||||
class A:
|
||||
def __init__(self):
|
||||
pass
|
||||
def __repr__(self):
|
||||
return '陈松'
|
||||
a = A()
|
||||
print(repr(a))
|
||||
print('%r'%a)
|
||||
```
|
||||
|
||||
## `__call__`
|
||||
|
||||
通过 ` obj() ` 触发执行。
|
||||
|
||||
```python
|
||||
class Foo:
|
||||
|
||||
def __init__(self):
|
||||
print('__init__')
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
print('__call__')
|
||||
|
||||
|
||||
obj = Foo() # 执行 __init__
|
||||
obj() # 执行 __call__
|
||||
```
|
||||
|
||||
## `__eq__`
|
||||
|
||||
通过 `==` 触发类中的 `__eq__` 方法
|
||||
|
||||
```python
|
||||
class A:
|
||||
def __init__(self):
|
||||
self.a = 1
|
||||
self.b = 2
|
||||
|
||||
def __eq__(self,obj):
|
||||
if self.a == obj.a and self.b == obj.b:
|
||||
return True
|
||||
a = A()
|
||||
b = A()
|
||||
print(a == b)
|
||||
```
|
||||
|
||||
## `__del__`
|
||||
|
||||
析构方法,当对象在内存中被释放时,自动触发执行。
|
||||
|
||||
注:此方法一般无须定义,因为 Python 是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给 Python 解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
|
||||
|
||||
## `__new__`
|
||||
|
||||
在 Python 中,`__new__` 是一个控制对象创建过程的魔术方法,它比 `__init__` 更早执行。
|
||||
|
||||
- 优先级:`__new__` 是对象创建的第一步,负责生成实例;`__init__` 是第二步,负责初始化实例。
|
||||
- 返回值:`__new__` 必须返回实例对象(否则 `__init__` 不会执行),而 `__init__` 无返回值。
|
||||
- 静态方法:`__new__` 隐式作为静态方法存在,第一个参数是类本身 `cls`,而非实例 `self`
|
||||
- 继承链:若未重写 `__new__`,Python 会沿继承链调用父类的 `__new__`,直至 `object.__new__`
|
||||
|
||||
|
||||
```python
|
||||
class A:
|
||||
def __new__(cls, *args, **kwargs):
|
||||
print("in new function")
|
||||
return object.__new__(A, *args, **kwargs) # 调用父类的 __new__ 创建实例并返回实例
|
||||
|
||||
def __init__(self):
|
||||
self.x = 1
|
||||
print("in init function")
|
||||
|
||||
|
||||
a = A()
|
||||
print(a.x)
|
||||
|
||||
```
|
299
Python/Python面向对象/封装.md
Normal file
@@ -0,0 +1,299 @@
|
||||
# 封装
|
||||
|
||||
将数据和方法捆绑在对象内部,仅通过暴露的接口与外界交互,保护数据安全并简化使用。
|
||||
|
||||
# 案例解析
|
||||
|
||||
第一步:将内容封装到类中,并且实例化对象
|
||||
|
||||
```python
|
||||
class Foo:
|
||||
def __init__(self,name,age):
|
||||
self.name = name
|
||||
self.age = age
|
||||
|
||||
def detail(self):
|
||||
print(self.name)
|
||||
print(self.age)
|
||||
|
||||
obj1 = Foo('xiaohong',18)
|
||||
obj2 = Foo('xiaoming',16)
|
||||
```
|
||||
|
||||
第二步:通过对象调用被封装的内容
|
||||
|
||||
```python
|
||||
class Foo:
|
||||
def __init__(self,name,age):
|
||||
self.name = name
|
||||
self.age = age
|
||||
|
||||
def detail(self):
|
||||
print(self.name)
|
||||
print(self.age)
|
||||
|
||||
obj1 = Foo('chensong',18)
|
||||
obj2 = Foo('aaron',16)
|
||||
|
||||
# 通过对象直接调用被封装的内容
|
||||
print(obj1.name)
|
||||
print(obj2.age)
|
||||
|
||||
# 通过 self 间接调用被封装的内容
|
||||
obj1.detail()
|
||||
obj2.detail()
|
||||
|
||||
```
|
||||
|
||||
## 案例一:摆放家具
|
||||
|
||||
**需求**
|
||||
|
||||
1. 房子(House)有户型、总面积和家具名称列表
|
||||
2. 家具(HouseItem)有名字和占地面积,其中
|
||||
- 床(bed)占地 `4` 平米
|
||||
- 衣柜(chest)占地 `2` 平米
|
||||
- 餐桌(table) 占地 `1.5` 平米
|
||||
3. 将以上三件家具添加到房子中
|
||||
4. 打印房子时,要求输出:户型、总面积、剩余面积、家具名称列表
|
||||
|
||||
<img src="封装/摆放家具类图.png" alt="img-摆放家具类图" style="zoom:80%;" />
|
||||
|
||||
**剩余面积**
|
||||
|
||||
1. 在创建房子对象时,定义一个 **剩余面积的属性**,**初始值和总面积相等**
|
||||
2. 当调用 `add_item` 方法,向房间 **添加家具** 时,让 **剩余面积** -= **家具面积**
|
||||
|
||||
**思考**:应该先开发哪一个类?**家具类**
|
||||
|
||||
1. 家具简单
|
||||
2. 房子要使用到家具,**被使用的类**,通常应该先开发
|
||||
|
||||
**第一步:创建家具类并且实例化家具对象**
|
||||
|
||||
```python
|
||||
class HouseItem:
|
||||
|
||||
def __init__(self, name, area):
|
||||
"""
|
||||
:param name: 家具名称
|
||||
:param area: 占地面积
|
||||
"""
|
||||
self.name = name
|
||||
self.area = area
|
||||
|
||||
def __str__(self):
|
||||
return "[%s] 占地面积 %.2f" % (self.name, self.area)
|
||||
|
||||
|
||||
# 1. 创建家具
|
||||
bed = HouseItem("床", 4)
|
||||
chest = HouseItem("衣柜", 2)
|
||||
table = HouseItem("餐桌", 1.5)
|
||||
|
||||
print(bed)
|
||||
print(chest)
|
||||
print(table)
|
||||
```
|
||||
|
||||
**第二步:创建房间类并且实例化房间对象**
|
||||
|
||||
```python
|
||||
class House:
|
||||
|
||||
def __init__(self, house_type, area):
|
||||
"""
|
||||
house_type: 户型
|
||||
area: 总面积
|
||||
"""
|
||||
self.house_type = house_type
|
||||
self.area = area
|
||||
|
||||
# 剩余面积默认和总面积一致
|
||||
self.free_area = area
|
||||
# 默认没有任何的家具
|
||||
self.item_list = []
|
||||
|
||||
def __str__(self):
|
||||
# Python 能够自动的将一对括号内部的代码连接在一起
|
||||
return ("户型:%s\n总面积:%.2f[剩余:%.2f]\n家具:%s"
|
||||
% (self.house_type, self.area,
|
||||
self.free_area, self.item_list))
|
||||
|
||||
def add_item(self, item):
|
||||
print("要添加 %s" % item)
|
||||
|
||||
|
||||
# 2. 创建房子对象
|
||||
my_home = House("汤成一品两室一厅", 60)
|
||||
|
||||
print(my_home)
|
||||
```
|
||||
|
||||
**第三步:在House中完善添加家具的方法**
|
||||
|
||||
```python
|
||||
def add_item(self, item):
|
||||
print("要添加 %s" % item)
|
||||
# 1. 判断家具面积是否大于剩余面积
|
||||
if item.area > self.free_area:
|
||||
print("%s 的面积太大,不能添加到房子中" % item.name)
|
||||
return
|
||||
# 2. 将家具的名称追加到名称列表中
|
||||
self.item_list.append(item.name)
|
||||
# 3. 计算剩余面积
|
||||
self.free_area -= item.area
|
||||
```
|
||||
|
||||
**完整案例:**
|
||||
|
||||
```python
|
||||
class HouseItem:
|
||||
|
||||
def __init__(self, name, area):
|
||||
"""
|
||||
|
||||
:param name: 家具名称
|
||||
:param area: 占地面积
|
||||
"""
|
||||
self.name = name
|
||||
self.area = area
|
||||
|
||||
def __str__(self):
|
||||
return "[%s] 占地面积 %.2f" % (self.name, self.area)
|
||||
|
||||
|
||||
# 1. 创建家具
|
||||
bed = HouseItem("席梦思", 4)
|
||||
chest = HouseItem("衣柜", 2)
|
||||
table = HouseItem("餐桌", 1.5)
|
||||
class House:
|
||||
|
||||
def __init__(self, house_type, area):
|
||||
"""
|
||||
house_type: 户型
|
||||
area: 总面积
|
||||
"""
|
||||
self.house_type = house_type
|
||||
self.area = area
|
||||
|
||||
# 剩余面积默认和总面积一致
|
||||
self.free_area = area
|
||||
# 默认没有任何的家具
|
||||
self.item_list = []
|
||||
|
||||
def __str__(self):
|
||||
# Python 能够自动的将一对括号内部的代码连接在一起
|
||||
return ("户型:%s\n总面积:%.2f[剩余:%.2f]\n家具:%s"
|
||||
% (self.house_type, self.area,
|
||||
self.free_area, self.item_list))
|
||||
|
||||
def add_item(self, item):
|
||||
print("要添加 %s" % item)
|
||||
# 1. 判断家具面积是否大于剩余面积
|
||||
if item.area > self.free_area:
|
||||
print("%s 的面积太大,不能添加到房子中" % item.name)
|
||||
return
|
||||
# 2. 将家具的名称追加到名称列表中
|
||||
self.item_list.append(item.name)
|
||||
# 3. 计算剩余面积
|
||||
self.free_area -= item.area
|
||||
|
||||
|
||||
# 2. 创建房子对象
|
||||
my_home = House("汤成一品两室一厅", 60)
|
||||
|
||||
my_home.add_item(bed)
|
||||
my_home.add_item(chest)
|
||||
my_home.add_item(table)
|
||||
print(my_home)
|
||||
```
|
||||
|
||||
**小结**:
|
||||
- 主程序只负责创建房子对象和家具对象
|
||||
- 让房子对象调用 `add_item` 方法将家具添加到房子中
|
||||
- 面积计算、剩余面积、家具列表等处理都被封装到房子类的内部
|
||||
|
||||
## 案例二:士兵突击
|
||||
|
||||
**需求**:
|
||||
|
||||
1. 士兵许三多有一把AK47
|
||||
2. 士兵可以开火
|
||||
3. 枪能够发射子弹
|
||||
4. 枪装填装填子弹
|
||||
|
||||
<img src="封装/士兵突击类图.png" alt="img-士兵突击类图" style="zoom:80%;" />
|
||||
|
||||
**先开发枪类**
|
||||
|
||||
**`shoot` 方法需求**
|
||||
|
||||
- 判断是否有子弹,没有子弹无法射击
|
||||
- 使用 `print` 提示射击,并且输出子弹数量
|
||||
|
||||
```python
|
||||
class Gun:
|
||||
|
||||
def __init__(self, model):
|
||||
# 枪的型号
|
||||
self.model = model
|
||||
# 子弹数量,初始为0
|
||||
self.bullet_count = 0
|
||||
|
||||
def add_bullet(self, count):
|
||||
self.bullet_count += count
|
||||
|
||||
def shoot(self):
|
||||
# 判断是否还有子弹
|
||||
if self.bullet_count <= 0:
|
||||
print("没有子弹了...")
|
||||
|
||||
return
|
||||
|
||||
# 发射一颗子弹
|
||||
self.bullet_count -= 1
|
||||
|
||||
print("%s 发射子弹[%d]...突突突" % (self.model, self.bullet_count))
|
||||
|
||||
|
||||
# 创建枪对象
|
||||
ak47 = Gun("ak47")
|
||||
ak47.add_bullet(50)
|
||||
ak47.shoot()
|
||||
```
|
||||
|
||||
**开发士兵类:**
|
||||
|
||||
**`fire` 方法需求**:
|
||||
|
||||
- 判断是否有枪,没有枪没法冲锋
|
||||
- 喊一声口号
|
||||
- 装填子弹
|
||||
- 射击
|
||||
|
||||
```python
|
||||
class Soldier:
|
||||
def __init__(self, name, gun=None):
|
||||
# 姓名
|
||||
self.name = name
|
||||
# 枪,士兵初始没有枪 None 关键字表示什么都没有
|
||||
self.gun = gun
|
||||
|
||||
def fire(self):
|
||||
# 1. 判断士兵是否有枪
|
||||
if self.gun is None:
|
||||
print("[%s] 还没有枪..." % self.name)
|
||||
else:
|
||||
# 2. 高喊口号
|
||||
print("冲啊...[%s]" % self.name)
|
||||
if self.gun.bullet_count <= 0:
|
||||
print("没子弹了,快换弹夹...")
|
||||
# 3. 让枪装填子弹
|
||||
self.gun.add_bullet(50)
|
||||
# 4. 让枪发射子弹
|
||||
self.gun.shoot()
|
||||
|
||||
xsd = Soldier("xsd",ak47)
|
||||
xsd.fire()
|
||||
```
|
BIN
Python/Python面向对象/封装/士兵突击类图.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
Python/Python面向对象/封装/摆放家具类图.png
Normal file
After Width: | Height: | Size: 47 KiB |
256
Python/Python面向对象/类的成员.md
Normal file
@@ -0,0 +1,256 @@
|
||||
# 类的组成成员分类
|
||||
|
||||
Python 类的组成成员可以分为三大类:字段(变量)、方法(函数)和属性(特殊方法),它们的定义方式、作用域和调用规则各有不同。
|
||||
|
||||
## 字段(变量)
|
||||
|
||||
字段分为实例变量和类变量,核心区别在于存储位置和作用对象:
|
||||
|
||||
- 实例变量:每个对象独立拥有一份实例变量,修改不影响其他对象。
|
||||
- 类变量:通过类名或实例均可访问,但通过实例修改会创建同名实例变量,覆盖类变量。
|
||||
|
||||
**示例:**
|
||||
|
||||
```python
|
||||
class Dog:
|
||||
species = "Canis lupus" # 类变量
|
||||
def __init__(self, name):
|
||||
self.name = name # 实例变量
|
||||
|
||||
d = Dog("Buddy")
|
||||
print(d.name)
|
||||
|
||||
d1 = Dog()
|
||||
print(Dog.species)
|
||||
d1.species = "Mutt"
|
||||
print(Dog.species)
|
||||
|
||||
```
|
||||
|
||||
## 方法(函数)
|
||||
|
||||
方法分为实例方法、类方法和静态方法,区别在于参数和调用方式:
|
||||
|
||||
- 实例方法:第一个参数为 self,指向调用该方法的实例;必须通过对象调用,可访问实例变量和类变量。
|
||||
- 类方法:使用 @classmethod 装饰器,参数为 cls(指向类本身);操作类变量或实现工厂模式(创建实例)。
|
||||
- 静态方法:使用 @staticmethod 装饰器,无默认参数,不依赖类或实例状态;执行与类相关的工具函数。
|
||||
|
||||
|
||||
**示例:**
|
||||
|
||||
```python
|
||||
class Dog:
|
||||
species = "Canis lupus" # 类变量
|
||||
|
||||
def __init__(self, name, age=0):
|
||||
self.name = name # 实例变量
|
||||
self.age = age
|
||||
|
||||
def bark(self):
|
||||
print(f"{self.name} is barking!")
|
||||
|
||||
@classmethod
|
||||
def create_from_string(cls, s):
|
||||
name, age = s.split(",")
|
||||
return cls(name, int(age)) # 创建实例时调用__init__方法
|
||||
|
||||
@classmethod
|
||||
def get_species(cls):
|
||||
return cls.species
|
||||
|
||||
@staticmethod
|
||||
def describe():
|
||||
return "Dogs are domesticated animals."
|
||||
|
||||
d = Dog("Buddy", 11)
|
||||
d.bark() # Buddy is barking!
|
||||
|
||||
d1 = Dog.create_from_string("Max,5") # 通过类方法创建实例
|
||||
d2 = Dog("Bella", 3) # 通过类方法创建实例
|
||||
print(d2.name)
|
||||
print(d1.age)
|
||||
|
||||
print(Dog.get_species()) # 类方法调用
|
||||
d3 = Dog("Charlie", 2)
|
||||
print(d3.get_species()) # 实例调用
|
||||
|
||||
print(Dog.describe()) # 静态方法调用
|
||||
```
|
||||
|
||||
## 属性(Property)
|
||||
|
||||
属性是伪装成字段的方法,通过 @property 装饰器实现,用于封装逻辑。
|
||||
|
||||
```python
|
||||
|
||||
class BankAccount:
|
||||
def __init__(self, balance):
|
||||
self.__balance = balance
|
||||
|
||||
@property
|
||||
def balance(self):
|
||||
return self.__balance
|
||||
|
||||
@balance.setter
|
||||
def balance(self, value):
|
||||
if value >= 0:
|
||||
self.__balance = value
|
||||
|
||||
@balance.deleter
|
||||
def balance(self):
|
||||
del self.__balance
|
||||
|
||||
account = BankAccount(100)
|
||||
print(account.balance) # 调用getter方法
|
||||
account.balance = 200 # 调用setter方法
|
||||
del account.balance # 删除属性
|
||||
print(account.balance)
|
||||
|
||||
```
|
||||
|
||||
## 其他特殊成员
|
||||
|
||||
- 魔术方法:如 `__init__`(构造函数)、`__str__`(字符串表示)等,用于自定义类的行为。
|
||||
- 私有成员:通过双下划线前缀(如 __variable)实现封装,仅在类内部访问。
|
||||
|
||||
**示例**:
|
||||
|
||||
```python
|
||||
class MyClass:
|
||||
def __init__(self, value):
|
||||
self.value = value # 公有属性
|
||||
self.__value = value # 私有属性
|
||||
|
||||
def public_method(self):
|
||||
return f'这是类的公有方法, value: {self.value}'
|
||||
|
||||
def __private_method(self):
|
||||
return f'这是类的私有方法, value: {self.__value}'
|
||||
|
||||
def get_value(self):
|
||||
return self.__value # 通过公有方法访问私有属性
|
||||
|
||||
obj = MyClass(10)
|
||||
print(obj.value) # 访问公有属性
|
||||
print(obj.public_method()) # 调用公有方法
|
||||
print(obj.__value) # 访问公有属性
|
||||
print(obj.__private_method()) # 调用私有方法
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
# 案例
|
||||
|
||||
**需求**
|
||||
|
||||
- 设计一个 `Game` 类
|
||||
- 属性:
|
||||
- 定义一个类属性 `top_score` 记录游戏的历史最高分
|
||||
- 定义一个实例属性 `player_name` 记录当前游戏的玩家姓名
|
||||
- 方法:
|
||||
- 静态方法 `show_help` 显示游戏帮助信息
|
||||
- 类方法 `show_top_score` 显示历史最高分
|
||||
- 实例方法 `start_game` 开始当前玩家的游戏
|
||||
- 主程序步骤
|
||||
1. 查看帮助信息
|
||||
2. 查看历史最高分
|
||||
3. 创建游戏对象,开始游戏
|
||||
|
||||

|
||||
|
||||
```python
|
||||
class Game(object):
|
||||
|
||||
# 游戏最高分,类属性
|
||||
top_score = 0
|
||||
|
||||
@staticmethod
|
||||
def show_help():
|
||||
print("帮助信息:让僵尸走进房间")
|
||||
|
||||
@classmethod
|
||||
def show_top_score(cls):
|
||||
print("游戏最高分是 %d" % cls.top_score)
|
||||
|
||||
def __init__(self, player_name):
|
||||
self.player_name = player_name
|
||||
|
||||
def start_game(self):
|
||||
print("[%s] 开始游戏..." % self.player_name)
|
||||
|
||||
# 使用类名.修改历史最高分
|
||||
Game.top_score = 999
|
||||
|
||||
# 1. 查看游戏帮助
|
||||
Game.show_help()
|
||||
|
||||
# 2. 查看游戏最高分
|
||||
Game.show_top_score()
|
||||
|
||||
# 3. 创建游戏对象,开始游戏
|
||||
game = Game("小明")
|
||||
|
||||
game.start_game()
|
||||
|
||||
# 4. 游戏结束,查看游戏最高分
|
||||
Game.show_top_score()
|
||||
```
|
||||
|
||||
|
||||
# 总结
|
||||
|
||||
| 特性 | 普通方法 | 类方法 | 静态方法 |
|
||||
| :---------- | :------------------ | :---------------- | :-------------------- |
|
||||
| 定义方式 | 不需要装饰器 | `@classmethod` | `@staticmethod` |
|
||||
| 第一个参数 | `self` | `cls` | 无 |
|
||||
| 访问权限 | 访问实例属性和方法 | 访问类属性和方法 | 无法访问类和实例属性 |
|
||||
| 调用方式 | 通过实例调用 | 通过类或实例调用 | 通过类或实例调用 |
|
||||
| 适用场景 | 实例相关操作 | 与类相关的操作 | 与类无关的操作 |
|
||||
|
||||
|
||||
# isinstace 与 issubclass
|
||||
|
||||
isinstance(a,b):判断a是否是b类(或者b类的派生类)实例化的对象
|
||||
|
||||
```python
|
||||
class A:
|
||||
pass
|
||||
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
obj = B()
|
||||
|
||||
print(isinstance(obj,B))
|
||||
print(isinstance(obj,A))
|
||||
```
|
||||
|
||||
issubclass(a,b): 判断a类是否是b类(或者b的派生类)的派生类
|
||||
|
||||
```python
|
||||
class A:
|
||||
pass
|
||||
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
class C(B):
|
||||
pass
|
||||
|
||||
print(issubclass(B,A))
|
||||
print(issubclass(C,A))
|
||||
```
|
||||
|
||||
思考:那么 list str tuple dict等这些类与 Iterble类 的关系是什么?
|
||||
|
||||
```python
|
||||
from collections import Iterable
|
||||
|
||||
print(isinstance([1,2,3], list)) # True
|
||||
print(isinstance([1,2,3], Iterable)) # True
|
||||
print(issubclass(list, Iterable)) # True
|
||||
|
||||
# 由上面的例子可得,这些可迭代对象,list str tuple dict等 都是 Iterable 的子类。
|
||||
```
|
BIN
Python/Python面向对象/类的成员/方法综合案例.png
Normal file
After Width: | Height: | Size: 36 KiB |
383
Python/Python面向对象/类空间问题及类的关系.md
Normal file
@@ -0,0 +1,383 @@
|
||||
# 类的空间问题
|
||||
|
||||
## 添加类和对象的属性
|
||||
|
||||
在 Python 中,**对象属性**(实例属性)和**类属性**的添加可以分别在类的内部(定义类时)和外部(运行时)进行。
|
||||
|
||||
### 对象属性
|
||||
|
||||
- **内部添加**:在类的 `__init__` 方法中定义的属性,属于对象(实例),每个实例有自己独立的属性。
|
||||
- **外部添加**:可以在实例化对象后,动态地为某个对象添加属性。
|
||||
|
||||
### 类属性
|
||||
|
||||
- **内部添加**:在类的定义中直接定义的属性,属于类本身,所有实例共享。
|
||||
- **外部添加**:可以在类定义后,动态地添加类属性。
|
||||
|
||||
```python
|
||||
class MyClass:
|
||||
# 1. 类属性:内部添加
|
||||
class_attr = "I am a class attribute"
|
||||
|
||||
def __init__(self, name):
|
||||
# 2. 对象属性:内部添加
|
||||
self.name = name
|
||||
|
||||
def add_instance_attr(self, age):
|
||||
# 3. 对象属性:内部添加(通过方法动态添加)
|
||||
self.age = age
|
||||
|
||||
|
||||
# 创建实例
|
||||
obj1 = MyClass("Object 1")
|
||||
|
||||
# 访问类属性和对象属性
|
||||
print(obj1.name) # 输出: Object 1
|
||||
print(MyClass.class_attr) # 输出: I am a class attribute
|
||||
|
||||
# 4. 对象属性:外部添加
|
||||
obj1.gender = "Male" # 动态给 obj1 添加 gender 属性
|
||||
print(obj1.gender) # 输出: Male
|
||||
|
||||
# 5. 类属性:外部添加
|
||||
MyClass.new_class_attr = "I am a new class attribute"
|
||||
print(MyClass.new_class_attr) # 输出: I am a new class attribute
|
||||
|
||||
# 创建另一个实例,验证类属性的共享性
|
||||
obj2 = MyClass("Object 2")
|
||||
print(obj2.name) # 输出: Object 2
|
||||
print(obj2.new_class_attr) # 输出: I am a new class attribute
|
||||
|
||||
# 6. 在内部通过方法添加对象属性
|
||||
obj1.add_instance_attr(25)
|
||||
print(obj1.age) # 输出: 25
|
||||
|
||||
# 注意:obj2 没有 age 属性,因为 age 是通过 obj1 的方法动态添加的
|
||||
# print(obj2.age) # 访问时会报错,因为 obj2 没有 age 属性
|
||||
```
|
||||
|
||||
## 对象如何找到类的属性
|
||||
|
||||
**对象查找属性的顺序**:
|
||||
1. 先从对象空间找
|
||||
2. 类空间找
|
||||
3. 父类空间找
|
||||
4. 基类空间
|
||||
|
||||
**类名查找属性的顺序**:
|
||||
1. 先从本类空间找
|
||||
2. 父类空间找
|
||||
3. 基类空间
|
||||
|
||||
上面的顺序都是单向不可逆,类名不可能找到对象的属性。
|
||||
|
||||
# 类之间关系
|
||||
|
||||
类与类中存在以下关系:
|
||||
|
||||
- 依赖关系
|
||||
- 关联关系
|
||||
- 组合关系
|
||||
- 聚合关系
|
||||
- 实现关系
|
||||
- 继承关系(类的三大特性之一:继承)
|
||||
|
||||
## 依赖关系
|
||||
|
||||
例:将大象装进冰箱,需要两个类, ⼀个是⼤象类, ⼀个是冰箱类
|
||||
|
||||
```python
|
||||
class Elphant:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def open(self):
|
||||
'''
|
||||
开门
|
||||
'''
|
||||
pass
|
||||
|
||||
def go(self):
|
||||
# 大象进入冰箱
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
'''
|
||||
关门
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
class Refrigerator:
|
||||
def open_door(self):
|
||||
print('冰箱门打开了')
|
||||
|
||||
def close_door(self):
|
||||
print('冰箱门关上了')
|
||||
```
|
||||
|
||||
将大象类和冰箱类进行依赖
|
||||
|
||||
```python
|
||||
class Elphant:
|
||||
def __init__(self,name):
|
||||
self.name = name
|
||||
|
||||
def open(self,obj):
|
||||
print(self.name + '开门')
|
||||
obj.open_door()
|
||||
|
||||
def go(self):
|
||||
print(f'{self.name}进到冰箱里')
|
||||
|
||||
def close(self,obj):
|
||||
print(self.name + '关门')
|
||||
obj.close_door()
|
||||
|
||||
class Refrigerator:
|
||||
def __init__(self,name):
|
||||
self.name = name
|
||||
|
||||
def open_door(self):
|
||||
print(self.name + '门被打开了')
|
||||
|
||||
def close_door(self):
|
||||
print(self.name+'门被关上了')
|
||||
|
||||
elphant = Elphant('小飞象')
|
||||
refrigerator = Refrigerator('格力冰箱')
|
||||
elphant.open(refrigerator)
|
||||
elphant.go()
|
||||
elphant.close(refrigerator)
|
||||
```
|
||||
|
||||
## 关联-聚合-组合关系
|
||||
|
||||
其实这三个在代码上写法是⼀样的,但是从含义上是不⼀样的:
|
||||
|
||||
1. 关联关系:两种事物必须是互相关联的,但是在某些特殊情况下是可以更改和更换的。
|
||||
2. 聚合关系:属于关联关系中的⼀种特例,侧重点是xxx和xxx聚合成xxx,各⾃有各⾃的声明周期,比如电脑,电脑⾥有 CPU, 硬盘, 内存等等。电脑挂了, CPU 还是好的,还是完整的个体。
|
||||
3. 组合关系:属于关联关系中的⼀种特例,写法上差不多,组合关系比聚合还要紧密。比如⼈的⼤脑,⼼脏,各个器官,这些器官组合成⼀个⼈。这时,⼈如果挂了,其他的东⻄也跟着挂了。
|
||||
|
||||
**关联关系**
|
||||
|
||||
```python
|
||||
class Boy:
|
||||
def __init__(self,name, girlfriend = None):
|
||||
self.name = name
|
||||
self.girlfriend = girlfriend
|
||||
|
||||
def dinner(self):
|
||||
if self.girlfriend:
|
||||
print('%s 和 %s 一起共进晚餐' % (self.name, self.girlfriend.name))
|
||||
else:
|
||||
print('连女朋友都没有,还有心情吃饭')
|
||||
|
||||
class Girl:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
boy = Boy('张三')
|
||||
boy.dinner()
|
||||
girl = Girl('如花')
|
||||
|
||||
boy2 = Boy('李四', girl)
|
||||
boy2.dinner()
|
||||
```
|
||||
|
||||
注意, 此时 Boy 和 Girl 两个类之间就是关联关系,两个类的对象紧密联系着,其中⼀个没有了,另⼀个就孤单的不得了,关联关系,其实就是,我需要你,你也属于我。
|
||||
|
||||
学校和老师之间的关系
|
||||
|
||||
```python
|
||||
class School:
|
||||
|
||||
def __init__(self,name,address):
|
||||
self.name = name
|
||||
self.address = address
|
||||
|
||||
|
||||
class Teacher:
|
||||
|
||||
def __init__(self,name,school):
|
||||
self.name = name
|
||||
self.school = school
|
||||
|
||||
s1 = School('镇江校区','北京')
|
||||
s2 = School('常州校区','上海')
|
||||
s3 = School('南京校区','深圳')
|
||||
|
||||
t1 = Teacher('T1',s1)
|
||||
t2 = Teacher('T2',s2)
|
||||
t3 = Teacher('T3',s3)
|
||||
|
||||
print(t1.school.name)
|
||||
print(t2.school.name)
|
||||
print(t3.school.name)
|
||||
```
|
||||
|
||||
但是学校也是依赖于老师的,所以老师学校应该互相依赖。
|
||||
|
||||
```python
|
||||
class School:
|
||||
|
||||
def __init__(self,name,address):
|
||||
self.name = name
|
||||
self.address = address
|
||||
self.teacher_list = []
|
||||
def append_teacher(self,teacher):
|
||||
self.teacher_list.append(teacher)
|
||||
|
||||
|
||||
class Teacher:
|
||||
|
||||
def __init__(self,name,school):
|
||||
self.name = name
|
||||
self.school = school
|
||||
|
||||
s1 = School('北京校区','北京')
|
||||
s2 = School('上海校区','上海')
|
||||
s3 = School('深圳校区','深圳')
|
||||
|
||||
t1 = Teacher('T1',s1)
|
||||
t2 = Teacher('T2',s2)
|
||||
t3 = Teacher('T3',s3)
|
||||
|
||||
s1.append_teacher(t1.name)
|
||||
s1.append_teacher(t2.name)
|
||||
s1.append_teacher(t3.name)
|
||||
|
||||
print(s1.teacher_list)
|
||||
```
|
||||
|
||||
**组合:将一个类的对象封装到另一个类的对象的属性中,就叫组合。**
|
||||
|
||||
例:设计一个游戏,让游戏里面的人物互殴,加上一个武器类,让人使用武器攻击。
|
||||
|
||||
```python
|
||||
class Gamerole:
|
||||
def __init__(self,name,ad,hp,wea=None):
|
||||
self.name = name
|
||||
self.ad = ad
|
||||
self.hp = hp
|
||||
self.wea = wea
|
||||
|
||||
def attack(self,p1):
|
||||
p1.hp -= self.ad
|
||||
print('%s攻击%s,%s掉了%s血,还剩%s'%(self.name,p1.name,p1.name,self.ad,p1.hp))
|
||||
|
||||
def equip_weapon(self,wea):
|
||||
self.wea = wea
|
||||
wea.ad += self.ad
|
||||
wea.owner_name = self.name
|
||||
|
||||
class Weapon:
|
||||
def __init__(self,name,ad,owner_name = None):
|
||||
self.name = name
|
||||
self.owner_name = owner_name
|
||||
self.ad = ad
|
||||
def weapon_attack(self,p2):
|
||||
p2.hp = p2.hp - self.ad
|
||||
print('%s利用%s攻击了%s,%s还剩%s血'%(self.owner_name,self.name,p2.name,p2.name,p2.hp))
|
||||
|
||||
|
||||
man = Gamerole('人',10,100)
|
||||
dog = Gamerole('狗',50,100)
|
||||
stick = Weapon('木棍',40)
|
||||
|
||||
man.equip_weapon(stick)
|
||||
man.wea.weapon_attack(dog)
|
||||
# 人利用木棍攻击了狗,狗还剩50血
|
||||
```
|
||||
|
||||
## 案例:王者荣耀3V3
|
||||
|
||||
```python
|
||||
import time
|
||||
import random
|
||||
class Gamerole:
|
||||
def __init__(self,name,ad,hp):
|
||||
self.name = name
|
||||
self.ad = ad
|
||||
self.hp = hp
|
||||
|
||||
def attack(self,p1):
|
||||
p1.hp -= self.ad
|
||||
print('%s攻击%s,%s掉了%s血,还剩%s'%(self.name,p1.name,p1.name,self.ad,p1.hp))
|
||||
|
||||
def equip_weapon(self,wea):
|
||||
self.wea = wea
|
||||
wea.ad += self.ad
|
||||
wea.owner_name = self.name
|
||||
|
||||
class Weapon:
|
||||
def __init__(self,name,ad,owner_name = None):
|
||||
self.name = name
|
||||
self.owner_name = owner_name
|
||||
self.ad = ad
|
||||
def weapon_attack(self,p2):
|
||||
p2.hp = p2.hp - self.ad
|
||||
print('%s利用%s攻击了%s,%s还剩%s血'%(self.owner_name,self.name,p2.name,p2.name,p2.hp))
|
||||
|
||||
sunwukong = Gamerole("孙悟空", 20, 500)
|
||||
caocao = Gamerole("曹操", 20, 100)
|
||||
anqila = Gamerole("安琪拉", 50, 80)
|
||||
|
||||
zhaoyun = Gamerole("赵云", 30, 450)
|
||||
guanyu = Gamerole("关羽", 80, 200)
|
||||
diaochan = Gamerole("貂蝉", 60, 150)
|
||||
|
||||
blue_list = [sunwukong, caocao, anqila]
|
||||
red_list = [zhaoyun, guanyu, diaochan]
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("游戏开始加载")
|
||||
# 打印一个菜单
|
||||
for i in range(0, 101, 2):
|
||||
time.sleep(0.1)
|
||||
char_num = i // 2
|
||||
per_str = '\r%s%% : %s\n' % (i, '*' * char_num) \
|
||||
if i == 100 else '\r%s%% : %s' % (i, '*' * char_num)
|
||||
print(per_str, end='', flush=True)
|
||||
|
||||
info = input("游戏加载完毕,输入任意字符开始!")
|
||||
# 输出东邪吸毒阵营里的任务角色
|
||||
print("蓝方阵营".center(20, '*'))
|
||||
for i in blue_list:
|
||||
print(i.name.center(20))
|
||||
print("红方阵营".center(20, '*'))
|
||||
for i in red_list:
|
||||
print(i.name.center(20))
|
||||
|
||||
while True:
|
||||
# 判断游戏结束的条件是某一方全部阵亡
|
||||
if len(blue_list) == 0:
|
||||
print("红方阵营胜利!!!")
|
||||
break
|
||||
if len(red_list) == 0:
|
||||
print("蓝方阵营胜利!")
|
||||
break
|
||||
|
||||
|
||||
# 游戏逻辑,每次随机选择一名角色出击
|
||||
index1 = random.randint(0, len(blue_list) - 1)
|
||||
index2 = random.randint(0, len(red_list) - 1)
|
||||
|
||||
# 开始攻击
|
||||
time.sleep(1)
|
||||
role1 = blue_list[index1]
|
||||
time.sleep(1)
|
||||
role2 = red_list[index2]
|
||||
time.sleep(1)
|
||||
role1.attack(role2)
|
||||
role2.attack(role1)
|
||||
|
||||
# 判断是否有英雄阵亡
|
||||
if role1.hp <= 0:
|
||||
print("%s阵亡!" % role1.name)
|
||||
blue_list.remove(role1)
|
||||
if role2.hp <= 0:
|
||||
print("%s阵亡!" % role2.name)
|
||||
red_list.remove(role2)
|
||||
```
|
||||
|
789
Python/Python面向对象/继承与多态.md
Normal file
@@ -0,0 +1,789 @@
|
||||
# 继承
|
||||
|
||||
子类可复用父类的属性和方法,并扩展新功能,实现代码复用和逻辑分层。例如,"电动车"类继承自"汽车"类,新增"充电"方法。
|
||||
|
||||
不用继承创建对象
|
||||
|
||||
```python
|
||||
class Person:
|
||||
def __init__(self,name,sex,age):
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.sex = sex
|
||||
|
||||
class Cat:
|
||||
def __init__(self,name,sex,age):
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.sex = sex
|
||||
|
||||
class Dog:
|
||||
def __init__(self,name,sex,age):
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.sex = sex
|
||||
```
|
||||
|
||||
使用继承的方式
|
||||
|
||||
```python
|
||||
class Aniaml(object):
|
||||
def __init__(self,name, age):
|
||||
self.name = name
|
||||
self.age = age
|
||||
|
||||
def eat(self):
|
||||
print(f"{self.name}吃东西..")
|
||||
|
||||
class Dog(Aniaml):
|
||||
pass
|
||||
|
||||
xiaotianquan = Dog("哮天犬",5)
|
||||
xiaotianquan.eat()
|
||||
```
|
||||
|
||||
继承概念:子类拥有父类的所有方法和属性。
|
||||
|
||||
<img src="继承与多态/继承对比图示.png" alt="img-继承对比图示" style="zoom:80%;" />
|
||||
|
||||
**继承的优点**:
|
||||
|
||||
1. 增加了类的耦合性(耦合性不宜多,宜精)。
|
||||
2. 减少了重复代码。
|
||||
3. 使得代码更加规范化,合理化。
|
||||
|
||||
# 继承分类
|
||||
|
||||
上面的那个例子,涉及到的专业术语:
|
||||
|
||||
- `Dog` 类是 `Animal` 类的**子类**,`Animal` 类是 `Dog` 类的**父类**,`Dog` 类从 `Animal` 类**继承**
|
||||
- `Dog` 类是 `Animal` 类的**派生类**,`Animal` 类是 `Dog` 类的**基类**,`Dog` 类从 `Animal` 类**派生**
|
||||
|
||||
继承:可以分**单继承,多继承**。
|
||||
|
||||
这里需要补充一下 python 中类的种类(继承需要):
|
||||
|
||||
在 python 2 版本中存在两种类.:
|
||||
|
||||
- ⼀个叫**经典类**. 在 Python2 中,经典类是指没有显式继承自 `object` 的类。它们使用旧的类定义方式。
|
||||
- ⼀个叫**新式类**. 新式类是指显式继承自 `object` 或其他新式类的类。新式类在 Python 2.2 中引入,并在 Python 3 中成为默认。
|
||||
|
||||
# 单继承
|
||||
|
||||
## 对象执行父类方法
|
||||
|
||||
```python
|
||||
class Aniaml(object):
|
||||
type_name = '动物类'
|
||||
|
||||
def __init__(self,name,sex,age):
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.sex = sex
|
||||
|
||||
def eat(self):
|
||||
print('吃',self)
|
||||
|
||||
class Person(Aniaml):
|
||||
pass
|
||||
|
||||
class Cat(Aniaml):
|
||||
pass
|
||||
|
||||
class Dog(Aniaml):
|
||||
pass
|
||||
|
||||
print(Person.type_name)
|
||||
Person.eat('东西')
|
||||
print(Person.type_name)
|
||||
|
||||
p1 = Person('aaron','男',18)
|
||||
print(p1.__dict__)
|
||||
print(p1.type_name)
|
||||
p1.type_name = '666'
|
||||
print(p1)
|
||||
p1.eat()
|
||||
```
|
||||
|
||||
## 执行顺序
|
||||
|
||||
```python
|
||||
class Aniaml(object):
|
||||
def __init__(self,name, age):
|
||||
self.name = name
|
||||
self.age = age
|
||||
|
||||
def eat(self):
|
||||
print(f"{self.name}吃东西..")
|
||||
|
||||
class person(Aniaml):
|
||||
def eat(self):
|
||||
print('%s 用筷子吃饭' % self.name)
|
||||
|
||||
class Dog(Aniaml):
|
||||
pass
|
||||
|
||||
class Cat(Aniaml):
|
||||
pass
|
||||
|
||||
|
||||
person1 = person('张三',18)
|
||||
person1.eat()
|
||||
```
|
||||
|
||||
## 同时执行类以及父类方法
|
||||
|
||||
方法一:如果想执行父类的 eat() 方法,可以在子类的方法中写上:父类.eat(对象,其他参数)
|
||||
|
||||
```python
|
||||
class Aniaml(object):
|
||||
type_name = '动物类'
|
||||
def __init__(self,name,sex,age):
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.sex = sex
|
||||
|
||||
def eat(self):
|
||||
print('吃东西')
|
||||
|
||||
class Person(Aniaml):
|
||||
def __init__(self,name,sex,age,mind):
|
||||
Aniaml.__init__(self,name,sex,age)
|
||||
self.mind = mind
|
||||
|
||||
def eat(self):
|
||||
Aniaml.eat(self)
|
||||
print('%s 吃饭'%self.name)
|
||||
|
||||
class Cat(Aniaml):
|
||||
pass
|
||||
|
||||
class Dog(Aniaml):
|
||||
pass
|
||||
|
||||
p1 = Person('aaron','男',18,'想吃东西')
|
||||
p1.eat()
|
||||
```
|
||||
|
||||
方法二:利用 `super().func(参数)`
|
||||
|
||||
```python
|
||||
class Aniaml(object):
|
||||
type_name = '动物类'
|
||||
def __init__(self,name,sex,age):
|
||||
self.name = name
|
||||
self.age = age
|
||||
self.sex = sex
|
||||
|
||||
def eat(self):
|
||||
print('吃东西')
|
||||
|
||||
class Person(Aniaml):
|
||||
def __init__(self,name,sex,age,mind):
|
||||
# super(Person,self).__init__(name,sex,age)
|
||||
super().__init__(name,sex,age)
|
||||
self.mind = mind
|
||||
|
||||
def eat(self):
|
||||
super().eat()
|
||||
print('%s 吃饭' % self.name)
|
||||
|
||||
class Cat(Aniaml):
|
||||
pass
|
||||
|
||||
class Dog(Aniaml):
|
||||
pass
|
||||
|
||||
p1 = Person('aaron','男',18,'想吃东西')
|
||||
p1.eat()
|
||||
```
|
||||
|
||||
## 方法重写
|
||||
|
||||
- 如果在开发中,父类的方法实现和子类的方法实现,完全不同,可以使用覆盖的方式,在子类中重新编写父类的方法实现
|
||||
- 具体的实现方式,就相当于在子类中定义了一个和父类同名的方法并且实现
|
||||
- 重写之后,在运行时,只会调用子类中重写的方法,而不再会调用父类封装的方法
|
||||
|
||||
## 子类中扩展父类方法
|
||||
|
||||
- 如果在开发中,子类的方法实现中包含父类的方法实现
|
||||
- 父类原本封装的方法实现** 是 **子类方法的一部分**
|
||||
- 就可以使用扩展的方式
|
||||
1. 在子类中重写父类的方法
|
||||
2. 在需要的位置使用 `super().父类方法` 来调用父类方法的执行
|
||||
3. 代码其他的位置针对子类的需求,编写 子类特有的代码实现
|
||||
|
||||
**关于 `super`**
|
||||
|
||||
- 在 `Python` 中 `super` 是一个特殊的类
|
||||
- `super()` 就是使用 `super` 类创建出来的对象
|
||||
- 最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现
|
||||
|
||||
|
||||
## 父类的私有属性和私有方法
|
||||
|
||||
子类对象不能在自己的方法内部,直接访问父类的私有属性或私有方法,但可以通过父类的公有方法间接访问
|
||||
|
||||
- **私有属性、方法** 是对象的隐私,不对外公开,外界以及子类都不能直接访问
|
||||
- **私有属性、方法** 通常用于做一些内部的事情
|
||||
|
||||

|
||||
|
||||
- `B` 的对象不能直接访问 `__num2` 属性
|
||||
- `B` 的对象不能在 `demo` 方法内访问 `__num2` 属性
|
||||
- `B` 的对象可以在 `demo` 方法内,调用父类的 `test` 方法
|
||||
- 父类的 `test` 方法内部,能够访问 `__num2` 属性和 `__test` 方法
|
||||
|
||||
```python
|
||||
class Animal:
|
||||
def __init__(self,name):
|
||||
self.__name = name
|
||||
|
||||
def __eat(self):
|
||||
print(self.__name + "Eating...")
|
||||
|
||||
def eat2(self):
|
||||
self.__eat()
|
||||
|
||||
class Dog(Animal):
|
||||
pass
|
||||
|
||||
a = Dog('哮天犬')
|
||||
print(a.name)
|
||||
a.__eat()
|
||||
a.eat2()
|
||||
|
||||
```
|
||||
|
||||
# 多继承
|
||||
|
||||
**概念**
|
||||
|
||||
- **子类** 可以拥有 **多个父类**,并且具有 **所有父类** 的 **属性** 和 **方法**
|
||||
- 例如:**孩子** 会继承自己 **父亲** 和 **母亲** 的 **特性**
|
||||
|
||||

|
||||
|
||||
**语法**
|
||||
|
||||
```python
|
||||
class 子类名(父类名1, 父类名2...)
|
||||
pass
|
||||
```
|
||||
|
||||
**问题的提出**
|
||||
|
||||
- 如果 **不同的父类** 中存在 **同名的方法**,**子类对象** 在调用方法时,会调用 **哪一个父类中**的方法呢?
|
||||
|
||||
> 提示:**开发时,应该尽量避免这种容易产生混淆的情况!** —— 如果 **父类之间** 存在 **同名的属性或者方法**,应该 **尽量避免** 使用多继承
|
||||
|
||||

|
||||
|
||||
|
||||
```python
|
||||
class shengxian: # 神仙
|
||||
def fei(self):
|
||||
print("神仙会飞")
|
||||
|
||||
def eat(self):
|
||||
print("吃人参果")
|
||||
|
||||
class monkey: # 猴
|
||||
def eat(self):
|
||||
print("吃桃子")
|
||||
|
||||
class songwukong(shengxian,monkey): #孙悟空既是神仙也是猴
|
||||
def __init__(self):
|
||||
self.name = "孙悟空"
|
||||
|
||||
def eat(self):
|
||||
print("我是齐天大圣,我不用吃东西")
|
||||
|
||||
swk = songwukong()
|
||||
swk.eat()
|
||||
```
|
||||
|
||||
## 经典类
|
||||
|
||||
```python
|
||||
class A:
|
||||
pass
|
||||
class B(A):
|
||||
pass
|
||||
class C(A):
|
||||
pass
|
||||
class D(B, C):
|
||||
pass
|
||||
class E:
|
||||
pass
|
||||
class F(D, E):
|
||||
pass
|
||||
class G(F, D):
|
||||
pass
|
||||
class H:
|
||||
pass
|
||||
class Foo(H, G):
|
||||
pass
|
||||
```
|
||||
|
||||
示意图
|
||||
|
||||

|
||||
|
||||
在经典类中采⽤的是深度优先,遍历⽅案:优先遍历继承链的最左侧分支直至顶端,再向右查找其他分支。
|
||||
|
||||
类的 MRO (method resolution order): Foo-> H -> G -> F -> E -> D -> B -> A -> C。
|
||||
|
||||
## 新式类
|
||||
|
||||
C3 算法是 Python 中用于解决多继承场景下方法解析顺序(MRO)的核心算法,其设计目标是保证继承关系的一致性、本地优先级和单调性。
|
||||
|
||||
### 算法核心原理
|
||||
|
||||
C3 算法的核心是**线性化合并规则**,通过递归合并父类的MRO列表生成一个无冲突的继承顺序链。
|
||||
|
||||
对于类 `C(B1, B2, ..., Bn)` ,其 MRO 计算公式为为:`L[C] = C + merge(L[B1],L[B2],..,L[Bn],[B1,B2,...,Bn])`
|
||||
其中 `megre` 操作负责合并父类的 MRO 序列。
|
||||
|
||||
|
||||
### `merge` 合并规则
|
||||
|
||||
`merge` 操作是 C3 算法的核心步骤,具体执行流程如下:
|
||||
1. **选取第一个列表的头元素(Head):** 即列表的第一个元素,记为 `h`。
|
||||
2. **检查 `h` 的有效性:**
|
||||
- 若 `h` 不在其他列表的尾部(即其他列表除首元素外的部分),则将其加入结果序列,并从所有列表中删除 `h`。
|
||||
- 若 `h` 存在于其他列表的尾部,则跳过当前列表,选择下一个列表的头元素重复检查。
|
||||
3. **递归执行:** 重复步骤1-2,直到所有列表为空(成功)或无法找到有效头元素(失败并抛出 `TypeError`)
|
||||
|
||||
**示例解析**
|
||||
|
||||
假设类 `D` 继承自 `B` 和 `C`,其父类的 MRO 分别为 `L(B)=[B, A, O]` 和 `L(C)=[C, A, O]`,则 `D` 的 MRO 计算如下:
|
||||
`L(D) = D + merge([B,A,O],[C,A,O],[B,C])`
|
||||
|
||||
1. 初始合并列表为 `[[B, A, O], [C, A, O], B, C]`。
|
||||
2. 提取 `B`(不在其他列表尾部),结果序列为 `[D, B]`,剩余列表 `[[A, O], [C, A, O], C]`。
|
||||
3. 提取 `C`(不在其他列表尾部),结果序列扩展为 `[D, B, C]`,剩余列表 `[[A, O], [A, O]]`。
|
||||
4. 合并 `A` 和 `O`,最终得到 `L(D)=[D, B, C, A, O]`
|
||||
|
||||
|
||||
### 实践案例
|
||||
|
||||

|
||||
|
||||
计算 mro(A) 方式:
|
||||
|
||||
step1:
|
||||
L(A) = A + merge(L[B] + L[C], [B, C])
|
||||
L(B) = B + merge(L[D] + L[E], [D, E])
|
||||
L(C) = C + merge(L[E] + L[F], [E, F])
|
||||
L(D) = D + merge(L[O]) = [D, O]
|
||||
L(E) = E + merge(L[O]) = [E, O]
|
||||
L[F] = F + merge(L[O]) = [F, O]
|
||||
step2:
|
||||
L(B) = B + merge([D, O], [E, O], [D, E])
|
||||
= [B, D] + merge([O], [E, O], [E]) # 拿出并删除D
|
||||
= [B, D, E] + merge([O], [O]) # 拿出并删除E
|
||||
= [B, D, E, O]
|
||||
L(C) = C + merge([E, O], [F, O], [E, F])
|
||||
...
|
||||
= [C, E, F, O]
|
||||
step3:
|
||||
L(A) = A + merge([B, D, E, O], [C, E, F, O], [B, C])
|
||||
= [A, B] + merge([D, E, O], [C, E, F, O], [C]) # h = B 拿出并删除B
|
||||
= [A, B, D] + merge([E, O], [C, E, F, O], [C]) # h = D 拿出并删除D
|
||||
= [A, B, D] + merge([E, O], [C, E, F, O], [C]) # h = E 存在于其他列表的尾部则跳过
|
||||
...
|
||||
= [A, B, D, C, E, F, O]
|
||||
|
||||
**代码**
|
||||
|
||||
```python
|
||||
class D:
|
||||
pass
|
||||
class E:
|
||||
pass
|
||||
class F:
|
||||
pass
|
||||
class B(D,E):
|
||||
pass
|
||||
class C(E,F):
|
||||
pass
|
||||
class A(B,C):
|
||||
pass
|
||||
print(A.__mro__)
|
||||
```
|
||||
|
||||
# 多态
|
||||
|
||||
同一方法在不同对象中呈现不同行为,增强代码灵活性。例如,"动物"类的"发声"方法在"狗"和"猫"对象中分别输出"汪汪"和"喵喵"。
|
||||
|
||||
- 多态可以增加代码的灵活度
|
||||
- 以继承和重写父类方法为前提
|
||||
- 是调用方法的技巧,不会影响到类的内部设计
|
||||
|
||||

|
||||
|
||||
```python
|
||||
class human(object):
|
||||
def work(self):
|
||||
return "喝杯咖啡,开始工作"
|
||||
|
||||
class ps_job(human):
|
||||
def work(self):
|
||||
return "开始美工"
|
||||
|
||||
class IT_job(human):
|
||||
def work(self):
|
||||
return "开始敲代码"
|
||||
|
||||
|
||||
def job(person): # 多态函数
|
||||
print(person.work())
|
||||
|
||||
# 创建不同类型的对象
|
||||
ps = ps_job()
|
||||
it = IT_job()
|
||||
|
||||
# 调用同一个函数,表现出不同的行为
|
||||
job(ps)
|
||||
job(it)
|
||||
```
|
||||
|
||||
**案例:哮天犬**
|
||||
|
||||
**需求**
|
||||
|
||||
1. 在 `Dog` 类中封装方法 `game`
|
||||
2. 定义 `XiaoTianDog` 继承自 `Dog` ,并且重写 `game` 方法
|
||||
3. 定义 `Person` 类,并且封装一个和狗玩的方法
|
||||
|
||||

|
||||
|
||||
**实现**
|
||||
|
||||
```python
|
||||
class Dog(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def game(self):
|
||||
print("%s 蹦蹦跳跳的玩耍..." % self.name)
|
||||
|
||||
|
||||
class XiaoTianDog(Dog):
|
||||
|
||||
def game(self):
|
||||
print("%s 飞到天上去玩耍..." % self.name)
|
||||
|
||||
|
||||
class Person(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def game_with_dog(self, dog):
|
||||
|
||||
print("%s 和 %s 快乐的玩耍..." % (self.name, dog.name))
|
||||
|
||||
# 让狗玩耍
|
||||
dog.game()
|
||||
|
||||
|
||||
# 1. 创建一个狗对象
|
||||
wangcai = Dog("旺财")
|
||||
xiaotianquan = XiaoTianDog("飞天旺财")
|
||||
|
||||
# 2. 创建一个小明对象
|
||||
xiaoming = Person("小明")
|
||||
|
||||
# 3. 让小明调用和狗玩的方法
|
||||
xiaoming.game_with_dog(wangcai)
|
||||
xiaoming.game_with_dog(xiaotianquan)
|
||||
```
|
||||
|
||||
**案例小结**
|
||||
|
||||
- `Person` 类中只需要让狗对象调用 `game` 方法,而不关心具体是什么狗。
|
||||
- `game` 方法是在 `Dog` 父类中定义的。
|
||||
- 在程序执行时,传入不同的狗对象实参,就会产生不同的执行效果。
|
||||
|
||||
|
||||
# 鸭子类型
|
||||
|
||||
python 中有一句谚语说的好,你看起来像鸭子,那么你就是鸭子。
|
||||
|
||||
这句谚语是关于鸭子类型(Duck Typing)的一种表达方式。鸭子类型是一种动态类型的概念,它强调一个对象的特征和行为,而不是其具体的类型或继承关系。
|
||||
|
||||
在 Python 中,鸭子类型的概念可以简单地表述为:如果一个对象具有像鸭子一样的特征和行为,那么我们可以认为它是一个鸭子。这意味着我们关注对象是否具备特定的方法和属性,而不关心对象的具体类型。
|
||||
|
||||
这种思想在 Python 中经常被使用,特别是在函数参数传递和对象的使用上。如果一个函数接受一个参数,并假设该参数具有某些特定的方法或属性,那么只要传递的对象满足这些要求,它就可以正常工作,无论对象的具体类型是什么。
|
||||
|
||||
下面是一个简单的示例来说明鸭子类型的概念:
|
||||
|
||||
```python
|
||||
class Duck:
|
||||
def quack(self):
|
||||
print("嘎嘎叫!")
|
||||
|
||||
def fly(self):
|
||||
print("扑哧扑哧的飞!")
|
||||
|
||||
|
||||
class Person:
|
||||
def quack(self):
|
||||
print("我喜欢跟鸭子一样嘎嘎叫")
|
||||
|
||||
def fly(self):
|
||||
print("我也喜欢跟鸭子一样飞")
|
||||
|
||||
|
||||
def make_it_quack_and_fly(obj):
|
||||
obj.quack()
|
||||
obj.fly()
|
||||
|
||||
|
||||
duck = Duck()
|
||||
person = Person()
|
||||
|
||||
make_it_quack_and_fly(duck)
|
||||
make_it_quack_and_fly(person)
|
||||
```
|
||||
|
||||
在上述示例中,我们定义了一个 `Duck` 类和一个 `Person` 类,它们都具有 `quack` 和 `fly` 方法。然后,我们定义了一个函数 `make_it_quack_and_fly`,它接受一个参数 `obj`,并调用 `obj` 的 `quack` 和 `fly` 方法。
|
||||
|
||||
我们可以看到,无论是 `Duck` 对象还是 `Person` 对象,只要它们具有 `quack` 和 `fly` 方法,都可以作为参数传递给 `make_it_quack_and_fly` 函数,并成功执行相应的方法。
|
||||
|
||||
这正是鸭子类型的思想:如果一个对象具有像鸭子一样的特征和行为(即具有 `quack` 和 `fly` 方法),那么我们可以将其视为鸭子,而无需关心对象的具体类型。
|
||||
|
||||
# 类的约束
|
||||
|
||||
写一个支付功能
|
||||
|
||||
```python
|
||||
class QQpay:
|
||||
def pay(self,money):
|
||||
print('使用qq支付%s元' % money)
|
||||
|
||||
class Alipay:
|
||||
def pay(self,money):
|
||||
print('使用阿里支付%s元' % money)
|
||||
|
||||
a = Alipay()
|
||||
a.pay(100)
|
||||
|
||||
b = QQpay()
|
||||
b.pay(200)
|
||||
```
|
||||
|
||||
统一一下付款方式
|
||||
|
||||
```python
|
||||
class QQpay:
|
||||
def pay(self,money):
|
||||
print('使用qq支付%s元' % money)
|
||||
|
||||
class Alipay:
|
||||
def pay(self,money):
|
||||
print('使用阿里支付%s元' % money)
|
||||
|
||||
def pay(obj,money):
|
||||
obj.pay(money)
|
||||
|
||||
|
||||
a = Alipay()
|
||||
b = QQpay()
|
||||
|
||||
pay(a,100)
|
||||
pay(b,200)
|
||||
```
|
||||
|
||||
如果后期添加微信支付,但是没有统一标准,换个程序员就可能写成这样。
|
||||
|
||||
```python
|
||||
class QQpay:
|
||||
def pay(self,money):
|
||||
print('使用qq支付%s元' % money)
|
||||
|
||||
class Alipay:
|
||||
def pay(self,money):
|
||||
print('使用阿里支付%s元' % money)
|
||||
|
||||
class Wechatpay:
|
||||
def fuqian(self,money):
|
||||
print('使用微信支付%s元' % money)
|
||||
|
||||
def pay(obj,money):
|
||||
print("===============")
|
||||
obj.pay(money)
|
||||
|
||||
|
||||
a = Alipay()
|
||||
b = QQpay()
|
||||
|
||||
pay(a,100)
|
||||
pay(b,200)
|
||||
|
||||
c = Wechatpay()
|
||||
c.fuqian(300)
|
||||
```
|
||||
|
||||
所以此时我们要用到对类的约束,对类的约束有两种:
|
||||
|
||||
1. 提取父类,然后在父类中定义好⽅法,在这个方法中什么都不⽤⼲,就抛⼀个异常就可以了,这样所有的⼦类都必须重写这个⽅法,否则,访问的时候就会报错。
|
||||
|
||||
2. 使⽤元类来描述方类,在元类中给出⼀个抽象方法,这样子类就不得不给出抽象方法的具体实现,也可以起到约束的效果。(推荐)
|
||||
|
||||
|
||||
**方法1**
|
||||
|
||||
```python
|
||||
class Payment:
|
||||
"""
|
||||
此类什么都不做,就是制定一个标准,谁继承我,必须定义我里面的方法。
|
||||
"""
|
||||
def pay(self,money):
|
||||
raise Exception("你没有实现pay方法")
|
||||
|
||||
class QQpay(Payment):
|
||||
def pay(self,money):
|
||||
print('使用qq支付%s元' % money)
|
||||
|
||||
class Alipay(Payment):
|
||||
def pay(self,money):
|
||||
print('使用阿里支付%s元' % money)
|
||||
|
||||
class Wechatpay(Payment):
|
||||
def fuqian(self,money):
|
||||
print('使用微信支付%s元' % money)
|
||||
|
||||
|
||||
def pay(obj,money):
|
||||
obj.pay(money)
|
||||
|
||||
a = Alipay()
|
||||
b = QQpay()
|
||||
c = Wechatpay()
|
||||
pay(a,100)
|
||||
pay(b,200)
|
||||
pay(c,300)
|
||||
```
|
||||
|
||||
**方法2:引入抽象类的概念处理
|
||||
|
||||
```python
|
||||
from abc import ABCMeta,abstractmethod
|
||||
class Payment(metaclass=ABCMeta): # 抽象类 接口类 规范和约束 metaclass 指定的是一个元类
|
||||
@abstractmethod
|
||||
def pay(self):pass # 抽象方法
|
||||
|
||||
class Alipay(Payment):
|
||||
def pay(self,money):
|
||||
print('使用支付宝支付了%s元'%money)
|
||||
|
||||
class QQpay(Payment):
|
||||
def pay(self,money):
|
||||
print('使用qq支付了%s元'%money)
|
||||
|
||||
class Wechatpay(Payment):
|
||||
# def pay(self,money):
|
||||
# print('使用微信支付了%s元'%money)
|
||||
def recharge(self):pass
|
||||
|
||||
def pay(a,money):
|
||||
a.pay(money)
|
||||
|
||||
a = Alipay()
|
||||
a.pay(100)
|
||||
pay(a,100) # 归一化设计:不管是哪一个类的对象,都调用同一个函数去完成相似的功能
|
||||
q = QQpay()
|
||||
q.pay(100)
|
||||
pay(q,100)
|
||||
w = Wechatpay()
|
||||
pay(w,100) # 到用的时候才会报错
|
||||
|
||||
|
||||
|
||||
# 抽象类和接口类做的事情 :建立规范
|
||||
# 制定一个类的metaclass是ABCMeta,
|
||||
# 那么这个类就变成了一个抽象类(接口类)
|
||||
# 这个类的主要功能就是建立一个规范
|
||||
```
|
||||
|
||||
# `super()` 深入了解
|
||||
|
||||
super 是严格按照类的继承顺序执行。
|
||||
|
||||
**示例1**
|
||||
|
||||
```python
|
||||
class A:
|
||||
def f1(self):
|
||||
print('in A f1')
|
||||
|
||||
def f2(self):
|
||||
print('in A f2')
|
||||
|
||||
|
||||
class Foo(A):
|
||||
def f1(self):
|
||||
super().f2()
|
||||
print('in A Foo')
|
||||
|
||||
obj = Foo()
|
||||
obj.f1()
|
||||
```
|
||||
|
||||
**示例2**
|
||||
|
||||
```python
|
||||
class A:
|
||||
def f1(self):
|
||||
print('in A')
|
||||
|
||||
class Foo(A):
|
||||
def f1(self):
|
||||
super().f1()
|
||||
print('in Foo')
|
||||
|
||||
class Bar(A):
|
||||
def f1(self):
|
||||
print('in Bar')
|
||||
|
||||
class Info(Foo,Bar):
|
||||
def f1(self):
|
||||
super().f1()
|
||||
print('in Info f1')
|
||||
|
||||
obj = Info()
|
||||
obj.f1()
|
||||
|
||||
# super() 是严格按照当前类的继承顺序执行的,不会收到过程中其他类的影响
|
||||
print(Info.mro())
|
||||
|
||||
```
|
||||
|
||||
**示例3**
|
||||
|
||||
```python
|
||||
class A:
|
||||
def f1(self):
|
||||
print('in A')
|
||||
|
||||
class Foo(A):
|
||||
def f1(self):
|
||||
super().f1()
|
||||
print('in Foo')
|
||||
|
||||
class Bar(A):
|
||||
def f1(self):
|
||||
print('in Bar')
|
||||
|
||||
class Info(Foo,Bar):
|
||||
def f1(self):
|
||||
super(Foo,self).f1() # 这里的意思是绕过Foo,从Foo的位置开始寻找下一个
|
||||
print('in Info f1')
|
||||
|
||||
obj = Info()
|
||||
obj.f1()
|
||||
```
|
BIN
Python/Python面向对象/继承与多态/多态.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
Python/Python面向对象/继承与多态/多态示意图.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
Python/Python面向对象/继承与多态/多继承1.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
Python/Python面向对象/继承与多态/多继承2.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
Python/Python面向对象/继承与多态/新式类megre.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
Python/Python面向对象/继承与多态/父类的私有属性和私有方法.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
Python/Python面向对象/继承与多态/经典类多继承.png
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
Python/Python面向对象/继承与多态/继承对比图示.png
Normal file
After Width: | Height: | Size: 164 KiB |
95
Python/Python面向对象/设计模式.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# 单例模式
|
||||
|
||||
单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。
|
||||
|
||||
## 应用场景
|
||||
|
||||
1. 数据库连接池
|
||||
2. 配置管理器
|
||||
3. 日志记录器
|
||||
4. 线程池管理
|
||||
5. 缓存管理
|
||||
|
||||
## 实现方式
|
||||
|
||||
在Python中实现单例模式有多种方式:
|
||||
|
||||
1. 使用`__new__`方法(基本实现):
|
||||
|
||||
```python
|
||||
class Singleton:
|
||||
_instance = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if not cls._instance:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
```
|
||||
|
||||
2. 使用装饰器实现(更优雅的方式):
|
||||
|
||||
```python
|
||||
def singleton(cls):
|
||||
_instance = {}
|
||||
def inner(*args, **kwargs):
|
||||
if cls not in _instance:
|
||||
_instance[cls] = cls(*args, **kwargs)
|
||||
return _instance[cls]
|
||||
return inner
|
||||
|
||||
@singleton
|
||||
class Config:
|
||||
def __init__(self):
|
||||
self.config = {}
|
||||
```
|
||||
|
||||
3. 使用元类实现(更高级的方式):
|
||||
|
||||
```python
|
||||
class SingletonMeta(type):
|
||||
_instances = {}
|
||||
def __call__(cls, *args, **kwargs):
|
||||
if cls not in cls._instances:
|
||||
cls._instances[cls] = super().__call__(*args, **kwargs)
|
||||
return cls._instances[cls]
|
||||
|
||||
class Database(metaclass=SingletonMeta):
|
||||
def __init__(self):
|
||||
self.connection = None
|
||||
```
|
||||
|
||||
## 优点
|
||||
|
||||
1. 保证一个类只有一个实例,减少内存开销
|
||||
2. 避免对资源的多重占用
|
||||
3. 提供了对唯一实例的全局访问点
|
||||
4. 实现了对实例创建的控制
|
||||
|
||||
## 缺点
|
||||
|
||||
1. 单例模式可能隐藏了类之间的依赖关系
|
||||
2. 单例模式违反了单一职责原则
|
||||
3. 在并发环境下需要特殊处理
|
||||
4. 测试时可能会遇到困难
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. 优先考虑使用模块级别的变量(Python 模块天然是单例的)
|
||||
2. 如果需要类级别的单例,推荐使用装饰器方式
|
||||
3. 需要继承时,使用元类方式
|
||||
4. 在多线程环境下,需要添加线程锁保证线程安全
|
||||
|
||||
```python
|
||||
import threading
|
||||
|
||||
class ThreadSafeSingleton:
|
||||
_instance = None
|
||||
_lock = threading.Lock()
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
with cls._lock:
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
```
|