在 Hy 中使用 let 进行动态绑定?
Dynamic bindings with let in Hy?
来自 Common Lisp,我正在尝试使用 let 动态隐藏全局变量的值。
(setv glob 18)
(defn callee []
(print glob))
(defn nonl [x]
(callee)
(let [glob x]
(callee))
(callee))
(nonl 39)
=>
18
18
18
有没有办法让这个工作正常进行,以便第二次调用 callee 时返回 39?
[编辑]
根据 gilch 的回复,我使用 contextvars 编写了以下草稿:
(import contextvars :as cv)
(defmacro defparam [symbol value]
(let [command (. f"{symbol} = cv.ContextVar('{symbol}')")]
`(do (exec ~command)
(.set ~symbol ~value))))
(defmacro parameterize [symbol value #* body]
`(. (cv.copy-context)
(run (fn [] (do (.set ~symbol ~value)
~@body)))))
(defn callee []
(glob.get))
;;;;;;;;;
(defparam glob 18)
(callee) => 18
(parameterize glob 39
(callee)) => 39
(callee) => 18
感谢您的回答!
在 Python 中没有 built-in 方法可以给全局变量一个动态范围的临时值(而且 Hy 没有为它添加宏或任何东西),所以你必须这样做你自己:
(setv glob 18)
(defn callee []
(print glob))
(defn nonl [x]
(callee)
(global glob)
(setv old-glob glob)
(try
(setv glob x)
(callee)
(finally
(setv glob old-glob)))
(callee))
(nonl 39)
这个宏可能如下所示:
(defmacro dyn-rebind-global [symbol new-value #* body]
(setv temp (hy.gensym))
`(do
(global ~symbol)
(setv ~temp ~symbol)
(try
(setv ~symbol ~new-value)
~@body
(finally
(setv ~symbol ~temp)))))
那么你可以这样使用它:
(setv glob 18)
(defn callee []
(print glob))
(defn nonl [x]
(callee)
(dyn-rebind-global glob x
(callee))
(callee))
(nonl 39)
最接近的 Python 等价动态绑定是 contextvars
and unittest.mock.patch
。
patch
主要用于单元测试,几乎适用于任何东西,但不是线程安全的。如果你需要在库代码中动态重新绑定某些东西,patch
可以做到。用作上下文管理器或装饰器。
>>> from unittest.mock import patch
>>> name = 'Alice'
>>> def greet(): print("Hi", name)
...
>>> greet()
Hi Alice
>>> with patch('__main__.name', 'Bob'):
... greet()
...
Hi Bob
>>> greet()
Hi Alice
使用 contextvars
它必须首先在模块顶层设置为 ContextVar
,并且必须使用 .get()
调用显式取消引用,但它在线程和异步代码中正常工作。
如果你想在 Python 中声明你自己的动态变量,最好在可能使用异步或线程时将其设为上下文变量。 asyncio 任务管理器将自动为每个任务交换新的上下文。您也可以使用 copy_context
手动执行此操作。 run()
方法接受一个可调用对象,因此它可以用作装饰器:
>>> import contextvars as cv
>>> name = cv.ContextVar('name', default='Alice')
>>> def greet(): print('Hi', name.get())
...
>>> greet()
Hi Alice
>>> @cv.copy_context().run
... def _():
... name.set('Bob')
... greet()
...
Hi Bob
>>> greet()
Hi Alice
这在 Hy 中可以以完全相同的方式工作,但宏可以使它更好。
来自 Common Lisp,我正在尝试使用 let 动态隐藏全局变量的值。
(setv glob 18)
(defn callee []
(print glob))
(defn nonl [x]
(callee)
(let [glob x]
(callee))
(callee))
(nonl 39)
=>
18
18
18
有没有办法让这个工作正常进行,以便第二次调用 callee 时返回 39?
[编辑]
根据 gilch 的回复,我使用 contextvars 编写了以下草稿:
(import contextvars :as cv)
(defmacro defparam [symbol value]
(let [command (. f"{symbol} = cv.ContextVar('{symbol}')")]
`(do (exec ~command)
(.set ~symbol ~value))))
(defmacro parameterize [symbol value #* body]
`(. (cv.copy-context)
(run (fn [] (do (.set ~symbol ~value)
~@body)))))
(defn callee []
(glob.get))
;;;;;;;;;
(defparam glob 18)
(callee) => 18
(parameterize glob 39
(callee)) => 39
(callee) => 18
感谢您的回答!
在 Python 中没有 built-in 方法可以给全局变量一个动态范围的临时值(而且 Hy 没有为它添加宏或任何东西),所以你必须这样做你自己:
(setv glob 18)
(defn callee []
(print glob))
(defn nonl [x]
(callee)
(global glob)
(setv old-glob glob)
(try
(setv glob x)
(callee)
(finally
(setv glob old-glob)))
(callee))
(nonl 39)
这个宏可能如下所示:
(defmacro dyn-rebind-global [symbol new-value #* body]
(setv temp (hy.gensym))
`(do
(global ~symbol)
(setv ~temp ~symbol)
(try
(setv ~symbol ~new-value)
~@body
(finally
(setv ~symbol ~temp)))))
那么你可以这样使用它:
(setv glob 18)
(defn callee []
(print glob))
(defn nonl [x]
(callee)
(dyn-rebind-global glob x
(callee))
(callee))
(nonl 39)
最接近的 Python 等价动态绑定是 contextvars
and unittest.mock.patch
。
patch
主要用于单元测试,几乎适用于任何东西,但不是线程安全的。如果你需要在库代码中动态重新绑定某些东西,patch
可以做到。用作上下文管理器或装饰器。
>>> from unittest.mock import patch
>>> name = 'Alice'
>>> def greet(): print("Hi", name)
...
>>> greet()
Hi Alice
>>> with patch('__main__.name', 'Bob'):
... greet()
...
Hi Bob
>>> greet()
Hi Alice
使用 contextvars
它必须首先在模块顶层设置为 ContextVar
,并且必须使用 .get()
调用显式取消引用,但它在线程和异步代码中正常工作。
如果你想在 Python 中声明你自己的动态变量,最好在可能使用异步或线程时将其设为上下文变量。 asyncio 任务管理器将自动为每个任务交换新的上下文。您也可以使用 copy_context
手动执行此操作。 run()
方法接受一个可调用对象,因此它可以用作装饰器:
>>> import contextvars as cv
>>> name = cv.ContextVar('name', default='Alice')
>>> def greet(): print('Hi', name.get())
...
>>> greet()
Hi Alice
>>> @cv.copy_context().run
... def _():
... name.set('Bob')
... greet()
...
Hi Bob
>>> greet()
Hi Alice
这在 Hy 中可以以完全相同的方式工作,但宏可以使它更好。