08-27-周三_17-09-29

This commit is contained in:
2025-08-27 17:10:05 +08:00
commit 86df397d8f
12735 changed files with 1145479 additions and 0 deletions

View File

@@ -0,0 +1,443 @@
# 面向过程
面向过程编程Procedural Programming是一种以**​步骤和过程**​​为核心的编程范式,其核心思想是将复杂问题分解为一系列可顺序执行的函数或过程,通过逐步调用来实现整体功能
## 核心思想
- **​步骤分解**​​:将问题拆解为多个子任务,每个子任务由独立的**​​函数/过程​**​实现。例如,处理学生早上的活动可分解为“起床→穿衣→洗漱→去学校”等步骤,每个步骤对应一个函数。
- **顺序执行**​​:程序按代码的书写顺序从上到下执行,通过**​​条件语句​**如if**循环结构**如for控制流程
- **数据与操作分离​**​:数据存储在全局或局部变量中,函数通过参数接收数据并处理,结果通过返回值或修改变量传递。
## 典型特征
- **模块化函数**​​:功能封装为函数,例如计算两数之和的函数 `add()`,通过调用实现代码复用。
- **​​线性流程​**​:程序逻辑清晰,易于调试。例如读取文件数据→处理数据→输出结果的流程。
- **高效性​**​:适用于简单任务或对性能要求高的场景,因无需对象创建开销。
# 面向对象
面向对象编程Object-Oriented ProgrammingOOP是一种以**​对象​**​为核心的编程范式,通过模拟现实世界中事物的交互逻辑来构建程序。其核心思想是将数据与操作数据的方法封装成独立的对象,通过对象之间的协作实现复杂功能
## 核心概念
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. 实例化好的对象可以调用类中的方法或者是静态属性

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View 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)
```

View 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()
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View 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. 创建游戏对象,开始游戏
![img-方法综合案例](类的成员/方法综合案例.png)
```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 的子类。
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View 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)
```

View 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` 类创建出来的对象
- 最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现
## 父类的私有属性和私有方法
子类对象不能在自己的方法内部,直接访问父类的私有属性或私有方法,但可以通过父类的公有方法间接访问
- **私有属性、方法** 是对象的隐私,不对外公开,外界以及子类都不能直接访问
- **私有属性、方法** 通常用于做一些内部的事情
![img-父类的私有属性和私有方法](继承与多态/父类的私有属性和私有方法.png)
- `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()
```
# 多继承
**概念**
- **子类** 可以拥有 **多个父类**,并且具有 **所有父类****属性****方法**
- 例如:**孩子** 会继承自己 **父亲****母亲****特性**
![img-多继承1](继承与多态/多继承1.png)
**语法**
```python
class 子类名(父类名1, 父类名2...)
pass
```
**问题的提出**
- 如果 **不同的父类** 中存在 **同名的方法****子类对象** 在调用方法时,会调用 **哪一个父类中**的方法呢?
> 提示:**开发时,应该尽量避免这种容易产生混淆的情况!** —— 如果 **父类之间** 存在 **同名的属性或者方法**,应该 **尽量避免** 使用多继承
![img-多继承2](继承与多态/多继承2.png)
```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
```
示意图
![img-经典类多继承](继承与多态/经典类多继承.png)
在经典类中采⽤的是深度优先,遍历⽅案:优先遍历继承链的最左侧分支直至顶端,再向右查找其他分支。
类的 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]`
### 实践案例
![img-新式类megre](继承与多态/新式类megre.png)
计算 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__)
```
# 多态
同一方法在不同对象中呈现不同行为,增强代码灵活性。例如,"动物"类的"发声"方法在"狗"和"猫"对象中分别输出"汪汪"和"喵喵"。
- 多态可以增加代码的灵活度
- 以继承和重写父类方法为前提
- 是调用方法的技巧,不会影响到类的内部设计
![img-多态示意图](继承与多态/多态示意图.png)
```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` 类,并且封装一个和狗玩的方法
![img-多态](继承与多态/多态.png)
**实现**
```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()
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

View 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
```