python的__repr__和__str__的球拍等效项?

时间:2019-05-03 18:58:22

标签: oop racket

在python对象中,重写对象的方法__repr____str__可以分别提供对象的“明确”表示和“人类可读”表示。如何在球拍中实现类似的行为?

我遇到了printable<%>界面here,这似乎应该可以用于此目的,但是我还无法使其完全正常地工作。以文档中的标准“鱼”示例为基础:

(define fish%
  (class* object% (printable<%>)
    (init size) ; initialization argument
    (super-new) ; superclass initialization
    ;; Field
    (define current-size size)

    ;; Public methods
    (define/public (get-size)
      current-size)
    (define/public (grow amt)
      (set! current-size (+ amt current-size)))
    (define/public (eat other-fish)
      (grow (send other-fish get-size)))

    ;; implement printable interface
    (define/public (custom-print port quoting-depth)
      (print "Print called!"))
    (define/public (custom-write port)
      (print "Write called!"))
    (define/public (custom-display port)
      (print "Display called!"))))

这是我得到的输出:

> (define f (new fish% [size 10]))
> f
"Display called!""Display called!""Print called!"
> (print f)
"Write called!""Print called!"
> (display f)
"Display called!""Display called!"
> (write f)
"Write called!""Write called!"
> 

所以问题是三个方面:

  1. 为什么会这样运行,即在对象看起来很单一的情况下调用多个方法?

  2. custom-print,custom-write和custom-display方法应评估什么?他们应该只是返回一个字符串,还是应该视情况实际带来打印,书写或显示的副作用?例如。自定义写入方法应该在内部调用write函数吗?

  3. 这完全是用于此目的的正确结构吗?如果没有,那是什么/是什么?

2 个答案:

答案 0 :(得分:1)

  
      
  1. 为什么它会以这种方式运行,即在对象看起来很单一的情况下调用多个方法?
  2.   

您不小心在print中使用了write,因此写入值将首先打印该值。

(define/public (custom-write port)
   (print "Write called!"))

display中也存在类似的问题。

还请记住打印/写入/显示到正确的端口。

尝试

#lang racket
(define fish%
  (class* object% (printable<%>)
    (init size) ; initialization argument
    (super-new) ; superclass initialization
    ;; Field
    (define current-size size)

    ;; Public methods
    (define/public (get-size)
      current-size)
    (define/public (grow amt)
      (set! current-size (+ amt current-size)))
    (define/public (eat other-fish)
      (grow (send other-fish get-size)))

    ;; implement printable interface
    (define/public (custom-print port quoting-depth)
      (print (~a "Print " current-size "\n") port))
    (define/public (custom-write port)
      (write (~a "Write " current-size "\n") port))
    (define/public (custom-display port)
      (display (~a "Display " current-size "\n") port))))

在REPL中,您将看到:

> (define f (new fish% [size 10]))
> f
"Print 10\n"
> (display f)
Display 10
> (write f)
"Write 10\n"

答案 1 :(得分:1)

另一个答案已经帮助您在代码中发现问题-您需要使用给定的端口作为参数,而不是隐式(current-output-port)-但解释并不正确。以相反的顺序解决您的问题:

  
      
  1. 这完全是用于此目的的正确结构吗?如果没有,那是/它是什么?
  2.   

是的,printable<%>是用于自定义基于类的对象的打印的正确结构。更一般而言,不是类的结构类型可以通过gen:custom-write通用接口或用于实现prop:custom-write的低级printable<%>结构类型属性来自定义打印。

  
      
  1. custom-print,custom-write和custom-display方法应评估什么?他们应该只是返回一个字符串,还是应该视情况实际带来打印,书写或显示的副作用?例如。 custom-write方法应该在内部调用write函数吗?
  2.   

这些方法实际上应该在它们作为参数给出的端口上执行IO的副作用。他们应在内部使用相应的功能(例如,write使用custom-writeprint使用custom-print)以递归方式打印/写入/显示字段中的值。另一方面,当直接发出特定字符时,它们通常应使用诸如write-charwrite-stringprintf之类的功能。 The docs for gen:custom-write给出了一个打印为<1, 2, "a">的元组数据类型的示例:它对尖括号和逗号使用write-string,但是递归print / write / display表示元组的元素。

  
      
  1. 为什么它会以这种方式运行,即在对象看起来很单一的情况下调用多个方法?
  2.   

这是您问题中涉及最多的部分。可通过多个挂钩自定义在Racket中进行打印:有关一些示例,请参见current-printport-write-handlerglobal-port-print-handlermake-tentative-pretty-print-output-port。这些定制挂钩中有许多在产生输出的过程中使用中间端口。

不是 解释的一件事是您在实现中使用了print,特别是因为print被绑定到了普通的Racket函数,词法范围,而不是对象的方法。

作为说明,请考虑您的示例的以下修改,该示例向(current-output-port)报告作为该方法的参数给出的端口的标识:

#lang racket

(define report
  (let ([next-id 0]
        [id-cache (make-hash)])
    (λ (op port)
      (printf "~a ~a ~v\n"
              op
              (hash-ref id-cache
                        port
                        (λ ()
                          (define id next-id)
                          (hash-set! id-cache port id)
                          (set! next-id (add1 next-id))
                          id))
              port))))

(define fish%
  (class* object% (printable<%>)
    (super-new)
    ;; implement printable interface
    (define/public (custom-print port quoting-depth)
      (report "custom-print  " port))
    (define/public (custom-write port)
      (report "custom-write  " port))
    (define/public (custom-display port)
      (report "custom-display" port))))

(define f (new fish%))

f

(print f)
(newline)

(display f)
(newline)

(write f)

在DrRacket中,这将生成输出:

custom-display 0 #<output-port:null>
custom-display 1 #<output-port:null>
custom-print   2 #<printing-port>

custom-display 3 #<output-port:null>
custom-display 4 #<output-port:null>
custom-print   5 #<printing-port>

custom-display 6 #<output-port:null>
custom-display 7 #<printing-port>

custom-display 8 #<output-port:null>
custom-write   9 #<printing-port>

在命令行中,输出为:

$ racket demo.rkt 
custom-write   0 #<output-port:null>
custom-print   1 #<output-port:redirect>

custom-write   2 #<output-port:null>
custom-print   3 #<output-port:redirect>

custom-display 4 #<output-port:null>
custom-display 5 #<output-port:redirect>

custom-write   6 #<output-port:null>
custom-write   7 #<output-port:redirect>