在 Hy 中使用 let 进行动态绑定?
来自 Common Lisp,我尝试使用 let 动态地隐藏全局变量的值。
(setv glob 18)
(defn callee []
(print glob))
(defn nonl [x]
(callee)
(let [glob x]
(callee))
(callee))
(nonl 39)
=>
18
18
18
有没有办法让这项工作正常进行,以便对被调用者的第二次调用给出 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
感谢您的答案!
Comming from Common Lisp, I'm trying to use let to shadow the value of a global variable dynamically.
(setv glob 18)
(defn callee []
(print glob))
(defn nonl [x]
(callee)
(let [glob x]
(callee))
(callee))
(nonl 39)
=>
18
18
18
Is there a way to make this work so that the second call to callee gives 39?
[EDIT]
Based on gilch's response i wrote the following draft using 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
Thanks for the answers!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Python 中没有内置方法可以为全局变量提供动态范围的临时值(并且 Hy 不会为其添加宏或任何内容),因此您必须自己执行此操作:
为此的宏可能如下所示
:你可以这样使用它:
There's no built-in way to give a global variable a dynamically scoped temporary value in Python (and Hy doesn't add a macro for it or anything), so you have to do it yourself:
A macro for this might look like:
Then you could use it like this:
动态绑定最接近的 Python 等价物是
contextvars
和unittest.mock.patch
。patch
主要用于单元测试,几乎适用于任何东西,但不是线程安全的。如果您需要动态地重新绑定库代码中的某些内容,patch
可以做到。用作上下文管理器或装饰器。使用
contextvars
时,必须首先在模块顶层将其设置为ContextVar
,并且必须使用.get()< 显式取消引用/code> 调用,但它在线程和异步代码中正常工作。
如果您想在 Python 中声明自己的动态变量,最好在可以使用异步或线程时将其设为上下文变量。异步任务管理器将自动为每个任务交换新的上下文。您还可以使用
copy_context
手动执行此操作。 run() 方法接受一个可调用函数,因此它可以用作装饰器:这在 Hy 中可以以完全相同的方式工作,但宏可以使其更好。
The nearest Python equivalents of dynamic bindings are
contextvars
andunittest.mock.patch
.patch
is mainly intended for unit testing and works on pretty much anything, but is not thread safe. If you need to dynamically rebind something in library code,patch
can do it. Use as a context manager or decorator.With
contextvars
it has to be set up as aContextVar
in the first place at the module top level, and has to be explicitly dereferenced with.get()
calls, but it works properly in threaded and async code.If you want to declare your own dynamic variables in Python, it's best to make it a context var when async or threading is a possibility. An asyncio task manager will automatically swap in a new context for each task. You can also do this manually with
copy_context
. Therun()
method takes a callable, so it can work as a decorator:This can all work exactly the same way in Hy, but a macro could make it nicer.