advice 和 decorater

突然脑洞了一下,想和大家讨论一下。python 的 decorater 和 elisp 的 advice 有可比性吗?谁更强大?

我看到 Python 装饰器也想到了「不就跟 Emacs Advice 的作用差不多吗!」,函数在 Python 和 Emacs Lisp 中都可以当作值来使用,Python 装饰器函数就是传入一个函数,返回一个函数,比如:

def uppercase(func):
    def wrapper():
        original_result = func()
        modified_result = original_result.upper()
        return modified_result
    return wrapper

def hello():
    return "Hello"

print(uppercase(hello)())
# => "HELLO"

等价的 Emacs Lisp 代码:

(defun hello ()
  '"Hello")

(defun uppercase (func)
  (lambda ()
    (upcase (funcall func))))

(funcall (uppercase #'hello))
;; => "HELLO"

Python 还提供了一种特殊语法 @ 来使用装饰器:

@uppercase
def hello2():
    return "Hello"

print(hello2())
# => "HELLO"

Emacs 没加额外的语法,advice-add 是一个普通函数:

(advice-add 'hello :around #'uppercase)
(funcall (hello))
;; => "HELLO"

当然我还没有了解过 Emacs Advice 是如何工作的,以上是我目前的认识。

1 个赞

add-function和add-advice的区别又是什么?为什么add-function看起来还能修饰一个变量?

因为add-function接受一个广义变量作为参数,修饰目标广义变量处的函数或macro

1 个赞

当然不一样,一个是主动调用一个是被动调用,我倒觉得add-advice和java的aop很像。

1 个赞

就是aop, 不过aop不是java 独有的 https://en.wikipedia.org/wiki/Aspect-oriented_programming#cite_note-29 虽然这个链接的内容似乎不存在了…(还可以看archived)

1 个赞

advice--make-1 可以看出,advice 会在原来的函数前面插入一段代码:

(defun advice--make-1 (byte-code stack-depth function main props)
  "Build a function value that adds FUNCTION to MAIN."
  (let ((adv-sig (gethash main advertised-signature-table))
        (advice
         (apply #'make-byte-code 128 byte-code
                (vector #'apply function main props) stack-depth nil
                (and (or (commandp function) (commandp main))
                     (list (advice--make-interactive-form
                            function main))))))
    (when adv-sig (puthash advice adv-sig advertised-signature-table))
    advice))

把原函数做为 advice 函数的参数:

(with-emacs "~/.local/bin/emacs"
  (byte-compile (defun foo () (message "foo"))))
;; => "#[nil \"ÀÁ!‡\" [message \"foo\"] 2]"

(with-emacs "~/.local/bin/emacs"
  (defun foo () (message "foo"))
  (defun foo@before () (message "foo-before"))
  (advice-add 'foo :before 'foo@before)
  (byte-compile 'foo))
;; => "#[128 \"ÀÁ\\\"ˆÀÂ\\\"‡\" [apply foo@before (lambda nil (message \"foo\")) nil] 4 nil]"

1 个赞

:after 会在后面

:around 会将原函数作为一个参数

M-x disassemble 可以查看汇编代码

Python 的 Wiki 上声称 decorator 就是 decorator pattern,而 decorator pattern 就是一种 AOP

1 个赞