· 原发布于 muspimerol.site

如何让一个实例知晓自己的变量名

这段时间在写一个做提示工程的库,其中顺便实现了一套模板语言,其中一个模板实例的 repr 格式化时想了一种方式获得一个实例的变量名。如下所示:

我创建了一个 GitHub Gist


这段代码定义了一个名为 AutoNaming 的类,该类的作用是自动获取并绑定实例的名称。

  1. __new__ 方法:在创建新实例时,首先调用 __new__ 方法,它创建了一个新的实例,并调用 _bind_frame 方法。
  2. _bind_frame 方法:获取当前的堆栈帧,并将其存储在实例的 _frame 属性中。
  3. _name 方法:这是一个缓存属性,它试图从 _frame 属性中找到实例的名称。如果找到,就返回这个名称。
  4. class_name 属性:返回实例的类名。
  5. fallback_name 属性:如果无法获取实例的名称,就返回类名作为备选名称。
  6. name 属性:返回实例的名称,如果 _name 属性不存在,就返回 fallback_name
  7. name 属性的 setter 方法:允许用户手动设置实例的名称,并将 _frame 属性设置为 None
  8. name 属性的 deleter 方法:删除 _name 属性。
  9. __repr__ 方法:返回实例的字符串表示形式,包含类名和实例名。
  10. __str__ 方法:返回实例名。

总的来说,这个类的目的是在创建实例时自动获取并绑定实例的名称,同时也支持手动设置和删除实例名。

# copied from `promplate.prompt.utils`

from functools import cached_property
from inspect import currentframe


class AutoNaming:
    def __new__(cls, *args, **kwargs):
        obj = super().__new__(cls)
        obj._bind_frame()
        return obj

    def _bind_frame(self) -> None:
        self._frame = currentframe()

    @cached_property
    def _name(self):
        f = self._frame
        if f and f.f_back and (frame := f.f_back.f_back):
            for name, var in frame.f_locals.items():
                if var is self:
                    return name

    @property
    def class_name(self):
        return self.__class__.__name__

    fallback_name = class_name

    @property
    def name(self):
        return self._name or self.fallback_name

    @name.setter
    def name(self, name):
        self._name = name
        self._frame = None

    @name.deleter
    def name(self):
        del self._name

    def __repr__(self):
        if self._name:
            return f"<{self.class_name} {self.name}>"
        else:
            return f"<{self.class_name}>"

    def __str__(self):
        return f"<{self.name}>"