How do I access an unknown instance's slot using a string?

邮差的信 提交于 2019-12-12 19:32:03

问题


Problem

Given an instance, inst and a string attr containing the name of a slot, how can I obtain the value of the slot attr on inst?

Of course, if attr were a symbol rather than a string, I would typically just use (slot-value inst attr), but it seems I need the package information to properly call intern (see below).

Minimal example

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

Prior art

  • From this question, I figured out that my problem was that intern was creating symbols in a different package than the one in which the class was defined.
  • It seems from this question, that I can't extract the package information simply from the instance, so I'll have to figure out another way (besides using intern to get there)

Background

I'm working on py-format a Common Lisp port of Python's {}-formatting. To implement the Python . operator (getattr) I need to convert the string following the dot into a slot on the object preceding the dot.


回答1:


Given an instance, inst and a string attr containing the name of a slot, how can I obtain the value of the slot attr on inst?

Slots don't have strings as slots names, but symbols. Since slot names can be arbitrary symbols, there is no general way to get a slot-value if all you have is a string.

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>

The last slot-name is an uninterned symbol. It is in no package. Thus you can't look it up in any way, if you haven't stored it somewhere.

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>

As you see above, it has three slots. Each symbol has the name s, but is really a different symbol.

You can get the slot names via introspection:

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

For portable functions see CLOSER-MOP.



来源:https://stackoverflow.com/questions/26245105/how-do-i-access-an-unknown-instances-slot-using-a-string

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!