Как я могу получить доступ к слоту неизвестного экземпляра, используя строку?

проблема

Учитывая пример, inst и строка attr содержащий имя слота, как я могу получить значение слота attr на inst?

Конечно, если attr были бы символом, а не строкой, я бы просто использовал (slot-value inst attr), но, кажется, мне нужна информация о пакете, чтобы правильно позвонить intern (увидеть ниже).

Минимальный пример

(defpackage :pack1
  (:use :common-lisp)
  (:export :*inst*))

(in-package :pack1)

(defclass temp-class ()
  ((temp-slot :initarg :temp-slot)))

(defvar *inst* (make-instance 'temp-class :temp-slot "value"))

(defpackage :pack2
  (:use :common-lisp :pack1)
  (:import-from :pack1 :temp-class))

(in-package :pack2)

(let ((inst *inst*)  ; In the real example, inst gets defined outside my control,
                     ; in yet another package
      (attr "temp-slot"))
  (format t "Given package name: ~S; "  ; prints fine
          (slot-value inst (intern (string-upcase attr) :pack1)))
  (format t "No package name: ~S; "  ; signals an error
          (slot-value inst (intern (string-upcase attr)))))

Предшествующий уровень техники

  • Из этого вопроса я понял, что моя проблема была в том, что intern создавал символы в пакете, отличном от того, в котором был определен класс.
  • Из этого вопроса видно, что я не могу извлечь информацию о пакете просто из экземпляра, поэтому мне придется найти другой способ (помимо использования intern попасть туда)

Фон

Я работаю на py-format Общий Лисп порт Python {} -formatting. Для реализации Python . оператор (getattr) Мне нужно преобразовать строку после точки в слот на объекте, предшествующем точке.

1 ответ

Решение

Учитывая экземпляр, inst и строку attr, содержащую имя слота, как я могу получить значение attr слота для inst?

Слоты имеют не строки как имена слотов, а символы. Поскольку имена слотов могут быть произвольными символами, не существует общего способа получить значение слота, если у вас есть только строка.

CL-USER 124 > (defclass foo ()
                ((s)             ; the slot-name is cl-user::s
                 (system::s)     ; the slot-name is  system::s
                 (#:s)))         ; the slot-name is        #:s
#<STANDARD-CLASS FOO 413054236B>

Последний слот-имя является непостоянным символом. Это не в упаковке. Таким образом, вы не можете найти его в любом случае, если вы не сохранили его где-то.

CL-USER 125 > (make-instance 'foo)
#<FOO 402013F043>

CL-USER 126 > (describe *)

#<FOO 402013F043> is a FOO
S      #<unbound slot>
S      #<unbound slot>
S      #<unbound slot>

Как вы видите выше, у него есть три слота. Каждый символ имеет название s, но это действительно другой символ.

Вы можете получить имена слотов через самоанализ:

CL-USER 127 > (mapcar #'slot-definition-name
                      (class-direct-slots (find-class 'foo)))
(S SYSTEM::S #:S)

Для переносимых функций см. CLOSER-MOP.

Другие вопросы по тегам