将问题块中的代码抽象为模块
Abstracting code from question blocks to modules
我有一个工作面试(耶!),我正在努力重构它。我正在尝试找出将我的 code
块移动到 .py
文件中的好方法。特别是,我担心维护 docassemble 所做的一些奇特的事情(例如,我正在考虑的特定 code
块被标记为 initial: True
)。
是否可以将 code
块正在执行的任何操作转换为函数,然后以使用这些函数的方式分配 docassemble
正在寻找的变量?大概这些函数的结果还需要在code
块中处理?
下面是大致正确的方法吗(下面的代码)?我假设:
- 尝试 运行
Bcode.func(A ...)
如下,如果 A
未定义,将触发必要的异常导致 docassemble
搜索设置 A
?
- 变量可以如下所示传递。
returning
函数的工作原理如下。
所有这些假设是否正确/不正确?
所以...
如果这是我的 questions.yml
文件:
---
question: A?
yesno: A
---
# code setting value of B
---
code: |
if A:
answer = "A"
elif B:
answer = "B"
else:
answer = "C"
---
为了抽象出代码,我想我可能会做这样的事情?
questions.yml
:
---
imports:
- Bcode
---
question: A?
yesno: A
---
initial: True
code: |
answer = Bcode.func(A, *args_to_set_value_of_B)
---
Bcode.py
:
---
def func(a, *args_to_set_value_of_b):
# code computing value of b
if a:
return "A"
elif b:
return "B"
else:
return "C"
---
如果你这样做
modules:
- .Bcode
然后 docassemble 将 运行(实际上):
exec("from docassemble.yourpackage.Bcode import *", user_dict)
其中 user_dict
是面试答案(YAML 文件中 Python 代码的命名空间)。这将使名称 func
在您的采访的命名空间中可用,以便它可以在 code
块、Mako 模板等中使用。
您的 initial
块将引发异常,因为 Bcode
不是面试答案中的名字。
只要 a
为真,您的函数 func()
将始终引发异常,因为 b
未在 func()
.
的命名空间中定义
Docassemble 的工作原理是捕获 NameError
、IndexError
和 AttributeError
异常,然后查找将定义任何变量的 question
或 code
块未定义。 NameError
异常适用于任何类型的变量,但 IndexError
和 AttributeError
异常仅适用于 DAObject
或其子类的实例。
当您将代码移动到模块文件中时,确保模块文件中的代码不会引发 NameError
异常非常重要,因为这些会导致混淆; docassemble 将尝试在面试答案命名空间中定义变量,但这永远不会解决模块内部的问题,因为无论如何该名称在模块内部都是未定义的。但是,模块文件中的代码在已从采访命名空间传递到模块命名空间的 DAObject
变量上引发 IndexError
和 AttributeError
错误是安全的,因为当 docassemble 定义这些面试答案命名空间中的变量,定义也将在模块内可用。
我会避免将代码移动到没有明确接口的模块中。在 YAML 中包含包含采访逻辑的代码块是完全合适的。我保留可重用的抽象代码模块。
我有一个工作面试(耶!),我正在努力重构它。我正在尝试找出将我的 code
块移动到 .py
文件中的好方法。特别是,我担心维护 docassemble 所做的一些奇特的事情(例如,我正在考虑的特定 code
块被标记为 initial: True
)。
是否可以将 code
块正在执行的任何操作转换为函数,然后以使用这些函数的方式分配 docassemble
正在寻找的变量?大概这些函数的结果还需要在code
块中处理?
下面是大致正确的方法吗(下面的代码)?我假设:
- 尝试 运行
Bcode.func(A ...)
如下,如果A
未定义,将触发必要的异常导致docassemble
搜索设置A
? - 变量可以如下所示传递。
returning
函数的工作原理如下。
所有这些假设是否正确/不正确?
所以...
如果这是我的 questions.yml
文件:
---
question: A?
yesno: A
---
# code setting value of B
---
code: |
if A:
answer = "A"
elif B:
answer = "B"
else:
answer = "C"
---
为了抽象出代码,我想我可能会做这样的事情?
questions.yml
:
---
imports:
- Bcode
---
question: A?
yesno: A
---
initial: True
code: |
answer = Bcode.func(A, *args_to_set_value_of_B)
---
Bcode.py
:
---
def func(a, *args_to_set_value_of_b):
# code computing value of b
if a:
return "A"
elif b:
return "B"
else:
return "C"
---
如果你这样做
modules:
- .Bcode
然后 docassemble 将 运行(实际上):
exec("from docassemble.yourpackage.Bcode import *", user_dict)
其中 user_dict
是面试答案(YAML 文件中 Python 代码的命名空间)。这将使名称 func
在您的采访的命名空间中可用,以便它可以在 code
块、Mako 模板等中使用。
您的 initial
块将引发异常,因为 Bcode
不是面试答案中的名字。
只要 a
为真,您的函数 func()
将始终引发异常,因为 b
未在 func()
.
Docassemble 的工作原理是捕获 NameError
、IndexError
和 AttributeError
异常,然后查找将定义任何变量的 question
或 code
块未定义。 NameError
异常适用于任何类型的变量,但 IndexError
和 AttributeError
异常仅适用于 DAObject
或其子类的实例。
当您将代码移动到模块文件中时,确保模块文件中的代码不会引发 NameError
异常非常重要,因为这些会导致混淆; docassemble 将尝试在面试答案命名空间中定义变量,但这永远不会解决模块内部的问题,因为无论如何该名称在模块内部都是未定义的。但是,模块文件中的代码在已从采访命名空间传递到模块命名空间的 DAObject
变量上引发 IndexError
和 AttributeError
错误是安全的,因为当 docassemble 定义这些面试答案命名空间中的变量,定义也将在模块内可用。
我会避免将代码移动到没有明确接口的模块中。在 YAML 中包含包含采访逻辑的代码块是完全合适的。我保留可重用的抽象代码模块。