Skip to content

关于copy() 浅拷贝 ​

假设我们有一个List[]列表的数据类型,我们再方法返回时直接返回数据对象本身(例如构建大模型消息类型时的history)

python
class Agent:
    def __init__(self):
        self._history = ["消息1", "消息2"]
    
    def get_history(self):
        return self._history.copy()

若是直接返回,外部可以对数据进行修改,包括数据的结构,以及数据内容。 直接返回是直接把容器的地址返回给调用者,所以调用者可以对容器的内容进行随意修改,任何操作都可以。

若是使用copy()进行返回

python
class Agent:
    def __init__(self):
        self._history = ["消息1", "消息2"]
    
    def get_history(self):
        return self._history.copy()

这里调用copy()之后,返回的是另一个地址空间,该地址中保存了原本容器中的所有元素的原始地址,这样做的好处如下:

  • 对于容器清空、插入新数据、添加数据这类操作都不会影响原始数据,因为拿到的是新创建的容器地址,容器中存的只是原始元素的地址,上述操作都是再新容器地址空间中进行操作的,原始地址空间中的内容不会发生变化。
  • 对于直接修改已有的数据的内容可以实现,因为新容器中使用的是原始元素的地址,指向的是同一个位置,所以修改后也是直接修改的原容器中的数据。
  • 实现了对数据的保护,用户不能随意的篡改结构方面的内容,不过仍能修改信息,但是对于大模型返回的消息一般都是查看,使用深拷贝太过于耗时复杂,使用浅拷贝在提升安全性的同时,性能又好,所以使用浅拷贝返回大模型的会话历史这类信息。

关于inspect.getmembers方法 ​

  • inspect.getmembers(self, ...):这是 Python 内置 inspect 模块提供的反射函数。它会遍历当前实例(self)的所有属性(包括方法、成员变量等)
  • predicate=inspect.ismethod:这是一个过滤条件(断言)。它告诉 getmembers:我只要那些是“实例方法”的属性,过滤掉普通字段、类方法、静态方法或属性(property)
  • inspect.ismethod 筛选的是实例方法(类中的普通方法,非静态、类方法)。简单说,它抓取的是定义时第一个参数必须写 self 的那些函数

关于hasattr()方法 ​

检查某个对象身上是否存在某个指定的“属性”或“方法名”。存在返回 True,不存在返回 False

  • 语法:hasattr(对象, "属性名字符串")
python
if hasattr(method, '_is_tool_action'):

for name,method in xxx方法 ​

从目标中提取指定属性的内容

python
for name,method in inspect.getmembers(self, predicate=inspect.ismethod)

这里inspect.getmember返回的是一个列表,列表中每一个属性都是一个元组:固定格式是 (属性名, 属性值)。这里只过滤得到实例方法,所以是(方法名,方法本体)

  • 假设当前实例 self 有一个方法叫 _add_memory。
  • inspect.getmembers 就会生成一个元组:('_add_memory', '方法本体')

for 循环的拆包(解构):

  • Python 的 for name, method in ... 语法,就是自动把元组里的两个值分别拿出来。
  • name:接收元组里的第一个值,它是一个字符串(比如 "_add_memory"),代表属性的名字。
  • method:接收元组里的第二个值,它就是该方法对象本身(可以直接被调用),代表属性的值。

简单类比:

  • 如果有一本通讯录,记录了 [("张三", "13800138000"), ("李四", "13900139000")]。 当你写 for name, phone in 通讯录: 时:

  • name 就是提取出来的 "张三"(名字)。

  • phone 就是提取出来的 "13800138000"(号码)。

列表推导式和生成器表达式 ​

语法都一致,都是生成得值 for循环 过滤条件,不同的点是:

  • 列表推导式是先生成一个列表再一下返回,并且是被[]包裹、
  • 生成器表达式是边生成边返回,被()包裹

method.__name__.lstrip('_')方法 ​

  • method.name:这是 Python 函数/方法的内置属性,用来获取方法名的字符串。比如你定义 def _add_memory(self):,那么 method.name 的值就是 "_add_memory"。
  • .lstrip(''):这是 Python 字符串的方法,只去除字符串开头(左侧)的指定字符。这里传了 '',意思就是把开头的下划线全部切掉。

提取方法注释内容生成方法名给大模型 ​

python
# 从方法的文档字符串(docstring)里提取第一句作为工具的描述
    def _extract_description_from_docstring(self) -> str:
        """从 docstring 提取描述"""
        doc = inspect.getdoc(self.method)
        if not doc:
            return f"执行 {self.method.__name__}"

        # 提取第一行作为描述
        lines = doc.split('\n')
        for line in lines:
            line = line.strip()
            if line and not line.startswith('Args:') and not line.startswith('Returns:'):
                return line

        return f"执行 {self.method.__name__}"
  1. doc = inspect.getdoc(self.method)

    • inspect.getdoc 会帮你获取方法的 doc 属性,并自动处理缩进(把缩进统一去掉),如果没写文档字符串,就返回 None。
  2. if not doc: return f"执行 {self.method.__name__}"

    • 如果没写文档,就不硬去解析了,直接返回一个朴素的默认描述:“执行 xxx”。
  3. lines = doc.split('\n')

    • 如果有文档,按换行符拆分成多行。
  4. for line in lines: 遍历,并执行 line.strip()

    • 把每一行的首尾空白字符(空格、制表符)去掉,得到干净的内容。
  5. if line and not line.startswith('Args:') and not line.startswith('Returns:'):

    • 这个条件是筛选规则:

      • line:排除空行。

      • not line.startswith('Args:'):排除以 Args: 开头的行(参数说明部分)。

      • not line.startswith('Returns:'):排除以 Returns: 开头的行(返回值说明部分)。

    • 这样过滤之后,剩下的通常就是文档字符串的第一句摘要。

  6. 找到第一个符合条件的行,立即 return line。

    • 如果遍历完所有行都没找到符合条件的(比如文档只写了 Args: 没写摘要),就返回默认描述

示例如下 ​

python
"""添加一条新的记忆

Args:
    content: 记忆的内容
Returns:
    操作结果字符串
"""

第一行 "添加一条新的记忆" → 去掉首尾空格后,不是空行,不以 Args: 或 Returns: 开头 → 立即返回 "添加一条新的记忆"

关于方法签名的内容 ​

官方定义:方法签名(Signature)是 Python 中用于描述“如何调用这个方法”的元数据。它只关注输入(参数)和输出(返回值)的结构定义。

具体包含什么:

  • 参数的名字(如 content, importance)
  • 参数的类型注解(如 : str, : float)
  • 参数的默认值(如 = 0.5)
  • 返回值的类型注解(如 -> str)
  • 绝对不包含什么:它不包含方法的文档字符串("""文档内容..."""),也不包含方法的名字(__name__ 是单独的属性)。

假设你的方法是 def _add_memory(self, content: str, importance: float = 0.5) -> str:,打印出来的签名对象(Signature)显示为:

(self, content: str, importance: float = 0.5) -> str

在代码里,它是一个 inspect.Signature 对象。遍历它的 .parameters 属性,你会得到一个个 inspect.Parameter 对象,每个对象都有 .name(名字)、.default(默认值)、.annotation(类型注解)等属性。

基于 VitePress 构建