类和对象

一切都是对象

在 Python 中,“一切皆对象”是一个核心理念。简单来说,Python 中的所有东西都是对象,无论是数值、字符串、函数、类,甚至是模块和代码本身。Python 对象有三个特征:身份类型

  • 身份:对象在内存中的地址,可以通过 id() 函数查看,例如 id(obj)
  • 类型:对象的类别,可以通过 type() 函数查看。例如,type(5) 返回 <class 'int'>
  • :对象的实际内容或数据,直接调用变量名即可查看。
1
2
3
4
i = 2
print(id(i)) # 输出 i 的存储地址
print(type(i)) # 输出 <class 'int'>
print(i) # 输出变量 i 的值

一切皆对象意味着我们可以将几乎任何东西赋值给变量或作为参数传递给函数。把任何函数传给一个变量。

类(Class)与对象(Object)是不同的,类是没初始化的模板,定义了结构和行为;对象是初始化了的实例,内存中有具体的值。

  • :类是对象的蓝图或模板,用来描述一类事物的通用结构和行为。定义类时,指定了该类对象的属性和方法。
  • 对象:对象是类的具体实例,表示某个具体的事物。

但是从更加一般的角度来说,类也是一种对象,也是有具体的值。

1
2
3
4
5
6
class MyClass:
pass

print(id(MyClass))
print(type(MyClass)) # 输出: <class 'type'>
print(MyClass) # <class '__main__.MyClass'>

重要的基类

在 Python 中,typeobject 是两个核心的基类:

  • object 是所有类的基类,所有类都继承自 object
  • type 是所有类的类型,也就是说,每个类都是 type 的实例,包括了 type 本身和 object。

总的来说,type 是所有类的类型:每个类都是 type 的实例,包括 MyClassint 等。

type 也是创建类的工厂type 是一种创建类对象的机制,相当于类的构造器。

type 是一个类,且继承自 object:它可以创建类,并且它自己也是一个类,具有类的一切特性。

img

类的常见属性

.__base__ 属性表示当前类的直接基类,即当前类继承的父类。如果一个类没有显式继承父类,则 .__base__object,因为所有类默认继承自 object

1
2
3
4
5
6
7
8
class Parent:
pass

class Child(Parent):
pass

print(Child.__base__) # 输出: <class '__main__.Parent'>
print(Parent.__base__) # 输出: <class 'object'>

在这里,Child.__base__Parent,而 Parent.__base__object,因为 object 是所有类的最终基类。

.__class__表示当前实例或类的类型,即这个对象是由哪个类创建的。对于类本身,.__class__ 通常会指向 type,因为类对象是 type 的实例。

1
2
3
4
5
6
7
class MyClass:
pass

obj = MyClass()

print(obj.__class__) # 输出: <class '__main__.MyClass'>
print(MyClass.__class__) # 输出: <class 'type'>

在这个示例中,obj.__class__ 表明 objMyClass 的实例,而 MyClass.__class__ 表明 MyClasstype 的实例。

.__dict__ 属性是一个字典,包含类或实例的所有属性。对于实例,.__dict__ 包含该实例的所有实例属性。对于类,.__dict__ 包含该类的所有类属性和方法。

1
2
3
4
5
6
7
8
9
10
class MyClass:
class_attr = "class attribute"

def __init__(self, value):
self.instance_attr = value

obj = MyClass("instance attribute")

print(obj.__dict__) # 输出: {'instance_attr': 'instance attribute'}
print(MyClass.__dict__) # 输出: 包含 class_attr, __init__ 等

在这个示例中,obj.__dict__ 仅包含实例属性,而 MyClass.__dict__ 包含类属性和方法。

.__subclasses__() 方法返回所有直接子类的列表。这在需要递归遍历继承树或找到所有子类时非常有用。

1
2
3
4
5
6
7
8
9
10
class Base:
pass

class Child1(Base):
pass

class Child2(Base):
pass

print(Base.__subclasses__()) # 输出: [<class '__main__.Child1'>, <class '__main__.Child2'>]

这里,Base.__subclasses__() 返回一个包含 Child1Child2 的列表。

.__name__ 是类的简单名称,返回类的名称字符串。.__qualname__ 是类的完全限定名(Qualified Name),包含嵌套类或作用域信息。

1
2
3
4
5
6
7
class Outer:
class Inner:
pass

print(Outer.__name__) # 输出: Outer
print(Outer.Inner.__name__) # 输出: Inner
print(Outer.Inner.__qualname__) # 输出: Outer.Inner

Inner.__qualname__ 以字符串形式描述了完整的嵌套路径信息。

__call__ 方法:类的可调用行为,使得类的实例可以像函数一样被调用。如果你在类中定义了 __call__ 方法,那么创建的实例可以被直接调用,并执行 __call__ 方法中的代码。

1
2
3
4
5
6
class CallableClass:
def __call__(self, *args, **kwargs):
print("Instance is called like a function")

obj = CallableClass()
obj() # 输出: Instance is called like a function

__del__ 方法:实例的销毁,用于对象销毁时执行清理工作。会在实例被垃圾回收时调用,通常用于关闭资源或执行清理操作。

1
2
3
4
5
6
class MyClass:
def __del__(self):
print("Instance is being destroyed")

obj = MyClass()
del obj # 输出: Instance is being destroyed

对象的初始化

类的初始化过程包括类的创建实例的初始化。当我们创建一个类的实例时,会依次调用 __new____init__ 等方法来完成实例化流程。理解这个过程不仅有助于掌握 Python 的类机制,还可以通过自定义这些方法,控制类的创建和初始化行为,实现更多高级功能(如单例模式池化对象等)。

当我们调用 MyClass() 创建类的实例时,Python 会经过以下几个主要步骤:

  1. __new__ 方法:创建实例。__new__ 是一个静态方法,负责创建实例,并分配内存空间,通常返回一个新的实例对象。

    • __new__ 的第一个参数是 cls,表示当前类,Python 会根据这个类来创建新实例。__new__(cls, name, bases, dct)

    • 如果 __new__ 不返回实例(例如返回 None 或其他类型),则不会继续调用 __init__,实例化会中止。

    需要注意,object 对象是一个空的对象,是所有对象的父类。所以 super 返回了一个新的空白的对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class MyClass:
    def __new__(cls, *args, **kwargs):
    print("Calling __new__")
    instance = super().__new__(cls)
    return instance

    def __init__(self, value):
    print("Calling __init__")
    self.value = value

    obj = MyClass(10)

  2. __init__ 方法:初始化实例。__init__ 的第一个参数是 self,表示新创建的实例对象。通常在 __init__ 中设置对象的属性,并完成实例的初始配置。

在类定义时,Python 实际上调用了**元类(MetaClass)**来创建类对象。默认情况下,Python 使用 type 作为元类,即 type 类是类的生成工厂。通过定义自定义的元类,可以在类创建时做更多的控制,例如在类生成前检查某些条件,或动态地添加方法和属性。

1
2
3
4
5
6
7
8
9
10
class MyMeta(type):
def __new__(cls, name, bases, dct):
print(f"Creating class {name}")
return super().__new__(cls, name, bases, dct)

class MyClass(metaclass=MyMeta):
pass

# 创建 MyClass 类
print(MyClass) # 输出: Creating class MyClass

元类编程

通过重写 __new__ 方法,可以确保类只有一个实例,实现单例模式

1
2
3
4
5
6
7
8
class Singleton:
_instance = None

def __new__(cls):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance

注意,元类的 __call__对象的 __call__ 是不一样的,它们的作用和用途是不同的。元类的 __call__ 方法用于控制类的实例化过程,返回一个对象。当使用 MyClass() 来实例化一个类时,Python 会调用元类的 __call__ 方法。在默认的实现中,元类的 __call__ 负责调用类的 __new____init__ 方法,以完成实例的创建和初始化。对象的 __call__ 方法是定义在类实例上的,它让实例像函数一样可以被调用

用元类来实现单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
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]

# 使用 SingletonMeta 作为元类
class Singleton(metaclass=SingletonMeta):
def __init__(self, value):
self.value = value

在多线程环境下,可能会出现多个线程同时创建实例的情况。通过在元类中加入线程锁,可以确保多线程下单例的唯一性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import threading

class ThreadSafeSingletonMeta(type):
_instances = {}
_lock = threading.Lock() # 线程锁,确保多线程安全

def __call__(cls, *args, **kwargs):
with cls._lock:
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]

class ThreadSafeSingleton(metaclass=ThreadSafeSingletonMeta):
def __init__(self, value):
self.value = value

ORM 映射:在 ORM 框架中,将数据库表结构映射为类结构,简化数据库操作。这个真的非常常见,而且 dct 字段还包括了传进来的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
class TableMeta(type):
def __new__(cls, name, bases, dct):
columns = {key: value for key, value in dct.items() if isinstance(value, str)}
dct['columns'] = columns # 自动将字符串字段识别为表的字段
return super().__new__(cls, name, bases, dct)

class User(metaclass=TableMeta):
id = 'INTEGER PRIMARY KEY'
name = 'TEXT'
email = 'TEXT'

print(User.columns) # 输出: {'id': 'INTEGER PRIMARY KEY', 'name': 'TEXT', 'email': 'TEXT'}

为什么 Python 是动态语言

Python 是一门典型的动态语言,这意味着它能够在运行时对对象的结构和行为进行修改,甚至可以动态地生成、修改和删除对象的属性和方法。这种动态性在开发业务逻辑密集型的应用时尤为高效,能够帮助程序员以更简洁、灵活的方式快速实现功能。

Python 对象通过 __dict__ 属性(属性字典)管理自身的所有可动态改变的属性,这让 Python 对象能够随时添加、修改或删除属性和方法。

1
2
3
4
5
6
7
8
9
10
11
12
class MyClass:
pass

# 为类添加新方法
def new_class_method(self):
print("New method for MyClass!")

MyClass.new_method = new_class_method

# 使用新方法
obj = MyClass()
obj.new_method() # 输出: New method for MyClass!

动态语言因其灵活性和开发效率而备受欢迎,尤其在业务逻辑密集的应用场景中,动态语言能够帮助程序员快速写出产品。比如说 Web 开发,数据科学和机器学习,自动化和脚本,核心在于业务逻辑。

动态语言的灵活性也带来了一个明显的缺点,即缺少静态类型。由于类型的缺失,动态语言的代码在复杂项目中可能变得难以理解和维护,尤其对于不熟悉项目或代码的新手来说。:许多 IDE 会依赖静态类型信息来提供智能提示、自动补全和类型检查。动态语言由于没有类型信息,IDE 很难准确地推断变量的类型,导致开发者得不到准确的智能提示。

所以建议多使用类型提示(Type Hints)。

面向对象

需求分析

我们的目标是构建一个基于代码的 RAG(Retrieval-Augmented Generation)系统,专注于代码辅助工作。这个系统的核心流程包括将项目源代码转化为可检索的知识库(codebase),利用检索器进行相关性查询,并基于对话系统与用户交互,利用 LLM(大型语言模型)提供回答。

根据需求,我们可以提取以下名词,每个名词可能对应一个类或对象:

  • 项目(Project):表示用户输入的项目源代码集合。
  • Codebase:代码知识库,用于存储和管理项目的源代码,使得其内容可被检索。
  • 检索器(Retriever):一个工具类,用于在 Codebase 中执行搜索和相关性检索。
  • 对话(Conversation):支持用户与 LLM 间的交互,对话会调用检索器在 Codebase 中查询答案。
  • LLM(大型语言模型):处理用户的自然语言问题并生成响应。与检索器结合以形成 RAG 系统。

核心需求:

  1. 输入项目的源代码并构建 Codebase

    • Project:作为源代码集合的抽象,负责加载、存储和管理项目文件。

    • Codebase:负责接收 Project 的源代码数据,并通过适当的解析和处理,将源代码构建成可查询的知识库。

  2. 基于问题检索相关的源代码

    • Retriever:通过索引和匹配算法在 Codebase 中查询并返回相关的代码片段。

    • Query(可选):用于封装用户的查询,允许对查询内容进行预处理或过滤(如提取代码相关的关键词)。

  3. 实现基于对话的交互,使用 Codebase 作为知识库

    • Conversation:表示与用户的交互,管理对话流程,并与 LLM 进行通信。负责在需要时调用 RetrieverCodebase 中获取信息,以增强 LLM 的回答。

类的设计

根据以上分析,可以划分出以下主要类及其职责:

类名 职责描述
Project 表示用户输入的源代码项目,负责加载和管理项目文件。
Codebase 作为知识库,负责存储和管理项目源代码,并提供索引、解析等操作,使之可被 Retriever 查询。
Retriever 提供在 Codebase 中执行检索的功能,通过索引和关键词匹配找到相关代码片段。
Query(可选) 封装用户的查询请求,提供查询预处理功能,如关键词提取。
Conversation 支持用户与 LLM 的交互,管理对话流并在需要时调用 RetrieverCodebase 中查询信息。
LLM 代表大型语言模型,负责接收用户的自然语言问题,结合 Retriever 的结果生成增强回答。

类间关系和交互

根据类的职责和需求,我们可以进一步细化类之间的关系:

  • Project -> CodebaseProject 提供源代码内容,Codebase 接收并存储这些内容。Codebase 需要从 Project 获取代码文件内容,解析后生成索引。
  • Codebase <-> RetrieverRetriever 需要访问 Codebase 中的内容,执行搜索操作以找到与查询相关的代码片段。Codebase 提供检索接口以支持 Retriever 进行查询。
  • Conversation -> RetrieverConversation 接收用户的自然语言问题,如果涉及代码查询,会将问题转化为 Query,并调用 RetrieverCodebase 检索答案。Retriever 的返回内容将传递给 LLM 以生成回答。
  • Conversation -> LLMConversationLLM 交互,将用户的问题及 Retriever 返回的代码上下文传递给 LLM,由 LLM 生成回答并回复给用户。

设计模式

工厂模式

用于创建 Project 实例,封装实例化细节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Project:
def __init__(self, name: str, source_files: List[str]):
self.name = name
self.source_files = source_files

def load_files(self):
# 加载项目源代码文件的逻辑
print(f"Loading files for project: {self.name}")

class ProjectFactory:
@staticmethod
def create_project(name: str, source_files: List[str]) -> Project:
# 根据需求,项目的创建逻辑可以在这里扩展
project = Project(name, source_files)
project.load_files()
return project

策略模式

Codebase 支持不同的存储方式(如内存或数据库),可使用策略模式动态选择存储方案。下面还可以看到我们抽象出了存储层当作接口,设计了两种存储方式,接着作为参数传入 codebase,实现不同策略的使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# Codebase类,支持不同存储策略(策略模式)

class StorageStrategy(ABC):
@abstractmethod
def save_codebase(self, code_data: Dict):
pass

@abstractmethod
def load_codebase(self) -> Dict:
pass

class InMemoryStorage(StorageStrategy):
def __init__(self):
self._data = {}

def save_codebase(self, code_data: Dict):
self._data = code_data
print("Codebase stored in memory.")

def load_codebase(self) -> Dict:
print("Loading codebase from memory.")
return self._data

class DatabaseStorage(StorageStrategy):
def __init__(self):
self._data = {} # 实际实现中可以是数据库连接等

def save_codebase(self, code_data: Dict):
# 实际数据库存储逻辑
self._data = code_data # 这里模拟数据库存储
print("Codebase stored in database.")

def load_codebase(self) -> Dict:
# 从数据库加载代码库
print("Loading codebase from database.")
return self._data

class Codebase:
def __init__(self, storage_strategy: StorageStrategy):
self.storage_strategy = storage_strategy
self.index = {}

def build_codebase(self, project: Project):
# 构建 codebase 逻辑,例如解析代码并构建索引
for file in project.source_files:
# 假设这里生成了文件的索引信息
self.index[file] = f"Indexed content of {file}"
self.storage_strategy.save_codebase(self.index)

def search(self, query: str) -> List[str]:
# 实现基本的检索功能,可以根据需要扩展或调用 Retriever 代理
results = [content for file, content in self.index.items() if query in content]
return results

适配器模式

适合不更改老系统,和新的设计保持一致性。假设我们的系统中引入了一个新组件 LegacyRetriever,这是一个旧的检索系统,用于检索代码片段,但它的接口设计不同,只有 search_code(query) 方法,而我们当前的系统需要通过 Retrieverretrieve(query) 方法来调用它。

那么设置一个适配器,包裹一层,那么 LegacyRetrieverAdapter 就满足了 Retriever 的接口要求了。甚至我们还能使用 self.__dict__.update(dict(execute=music_player.play)) 的方式动态地给类增加函数,那么更加灵活了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# LegacyRetriever 只提供了不兼容的 `search_code` 方法
class LegacyRetriever:
def __init__(self, codebase):
self.codebase = codebase

def search_code(self, query):
# 使用不同的查询方法
return [content for file, content in self.codebase.index.items() if query.lower() in content.lower()]

class LegacyRetrieverAdapter(Retriever):
def __init__(self, legacy_retriever):
self.legacy_retriever = legacy_retriever

def retrieve(self, query):
# 适配 `search_code` 为 `retrieve`
return self.legacy_retriever.search_code(query)

代理模式

在代理模式(Proxy Pattern)中,代理对象作为访问其他对象的接口,通常用于在不改变目标对象的前提下,为其提供控制访问懒加载缓存日志记录等附加功能。

假设我们希望增强 Retriever 的功能,可以使用代理模式来实现如下扩展:

  1. 访问控制:限制非授权用户的查询访问。
  2. 缓存:缓存上次查询的结果,避免重复计算或检索。
  3. 日志记录:记录每次查询的日志,便于后续查看或调试。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 访问控制的代理
class AccessControlProxyRetriever:
def __init__(self, retriever, authorized_users):
self.retriever = retriever
self.authorized_users = authorized_users

def retrieve(self, query, user):
if user not in self.authorized_users:
print(f"Access denied for user: {user}")
return []
print(f"Access granted for user: {user}")
return self.retriever.retrieve(query)
# 缓存的代理
class CachingProxyRetriever:
def __init__(self, retriever):
self.retriever = retriever
self.cache = {}

def retrieve(self, query):
if query in self.cache:
print(f"Returning cached result for query: {query}")
return self.cache[query]

result = self.retriever.retrieve(query)
self.cache[query] = result
return result
# 日志的代理
class LoggingProxyRetriever:
def __init__(self, retriever):
self.retriever = retriever
self.logs = []

def retrieve(self, query):
print(f"Logging query: {query}")
self.logs.append(query)
return self.retriever.retrieve(query)

def get_logs(self):
return self.logs

代理模式的牛逼之处,就是可以直接组合起来,因为这些类都实现了 retriever 函数。

1
2
3
4
5
6
7
8
9
10
11
12
# 创建项目和 Codebase
project = Project("MyProject", ["file1.py", "file2.py"])
codebase = Codebase(project)
retriever = Retriever(codebase)

# 包装多个代理
log_proxy = LoggingProxyRetriever(retriever)
cache_proxy = CachingProxyRetriever(log_proxy) # 使用日志代理作为缓存代理的底层
access_proxy = AccessControlProxyRetriever(cache_proxy, authorized_users)

# 授权访问检索并记录日志和缓存
print(access_proxy.retrieve("file1", "Alice"))

单例模式

参考元类编程。

观察者模式

观察者模式(Observer Pattern)是一种行为型模式,它定义了一种一对多的依赖关系,让多个观察者对象监听某一个主题对象。主题在状态发生变化时会通知所有观察者,观察者可以随时动态增删。这种设计使得观察者与主题解耦,提高了代码的灵活性和可维护性。

这种模式在事件订阅,pub-sub 系统非常常见,非常实用。pub 和 sub 可以非常干净的解耦。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Subject:
def __init__(self):
self.__observers = []

def register(self, observer):
self.__observers.append(observer)

def unregister(self, observer):
self.__observers.remove(observer)

def notifyAll(self, *args, **kwargs):
for observer in self.__observers:
observer.notify(self, *args, **kwargs)


class Observer:
def __init__(self, subject):
subject.register(self)

def notify(self, subject, *args, **kwargs):
print(f"{self} received {args} {kwargs} from {subject}")


# 使用示例
subject = Subject()
observer1 = Observer(subject)
observer2 = Observer(subject)

subject.notifyAll("First notification")
subject.unregister(observer1) # 动态移除 observer1
subject.notifyAll("Second notification") # 仅 observer2 收到

命令模式

命令模式(Command Pattern)是一种行为型设计模式,它的核心思想是将请求封装成对象,从而让你可以独立地传递、保存、撤销和重做请求。每一个命令对象代表一个请求或操作,比如“创建文件”、“删除文件”、“重命名文件”等等。

在命令模式中,通常包含以下几个角色:

  1. 命令(Command)接口:定义执行操作的接口,所有的命令类都应该实现这个接口。
  2. 具体命令(Concrete Command)类:实现命令接口,封装具体的操作行为,比如“创建文件”、“删除文件”等。
  3. 接收者(Receiver):执行具体操作的对象,命令对象通过接收者来完成操作。
  4. 调用者(Invoker):负责执行命令的类,通常会记录所有命令,并提供撤销、重做等功能。

定义命令接口:首先,我们定义 Command 接口,它包含了所有命令必须实现的 executeundo 方法。

1
2
3
4
5
6
class Command:
def execute(self):
raise NotImplementedError("Subclasses must implement 'execute' method")

def undo(self):
raise NotImplementedError("Subclasses must implement 'undo' method")

定义接收者(家电设备):接收者是实际执行命令的家电设备。我们以灯、音响和空调为例,给每个接收者定义具体的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Light:
def turn_on(self):
print("Light is turned ON")

def turn_off(self):
print("Light is turned OFF")


class Stereo:
def play_music(self):
print("Stereo is playing music")

def stop_music(self):
print("Stereo stopped playing music")


class AC:
def set_temperature(self, temperature):
print(f"AC temperature set to {temperature}°C")

def turn_off(self):
print("AC is turned OFF")

定义具体命令类:接下来,定义具体的命令类,每个命令类都会实现 Command 接口,并且封装特定设备的操作。每个命令对象会持有接收者的引用,以便在调用 executeundo 时,执行接收者的具体操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 控制灯的命令
class LightOnCommand(Command):
def __init__(self, light):
self.light = light

def execute(self):
self.light.turn_on()

def undo(self):
self.light.turn_off()


class LightOffCommand(Command):
def __init__(self, light):
self.light = light

def execute(self):
self.light.turn_off()

def undo(self):
self.light.turn_on()

# 控制音响的命令
class PlayMusicCommand(Command):
def __init__(self, stereo):
self.stereo = stereo

def execute(self):
self.stereo.play_music()

def undo(self):
self.stereo.stop_music()


class StopMusicCommand(Command):
def __init__(self, stereo):
self.stereo = stereo

def execute(self):
self.stereo.stop_music()

def undo(self):
self.stereo.play_music()


定义调用者(Invoker),调用者负责管理和执行命令对象,提供操作执行、撤销等功能。调用者类 RemoteControl 可以持有一组命令,并支持批量撤销操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
class RemoteControl:
def __init__(self):
self.history = [] # 保存已执行的命令历史

def press_button(self, command):
command.execute()
self.history.append(command)

def undo_last(self):
if self.history:
command = self.history.pop()
command.undo()

控制的主程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 创建设备对象
light = Light()
stereo = Stereo()
ac = AC()

# 创建具体的命令对象
light_on_cmd = LightOnCommand(light)
light_off_cmd = LightOffCommand(light)
play_music_cmd = PlayMusicCommand(stereo)
stop_music_cmd = StopMusicCommand(stereo)
set_ac_temp_cmd = SetACTemperatureCommand(ac, 22)

# 创建调用者
remote = RemoteControl()

# 执行命令
remote.press_button(light_on_cmd) # 输出: Light is turned ON
remote.press_button(play_music_cmd) # 输出: Stereo is playing music
remote.press_button(set_ac_temp_cmd) # 输出: AC temperature set to 22°C

# 撤销最近的命令
remote.undo_last() # 输出: AC is turned OFF
remote.undo_last() # 输出: Stereo stopped playing music

参考