以产量为例外的双向交互作用 我编写C++ DLL,它是元交易者4应用程序(MT4)和Python脚本(嵌入Python到MT4)之间的门。MT4将请求发送到此DLL并等待指令执行(例如,字符串命令数组)。DLL将MT4请求的解析传递给Python脚本。Python脚本需要从MT4获得一些信息来解析每个请求。因此,MT4和Python具有双向通信
但MT4不支持双向通信,它只能解析自己的DLL请求的结果,并用新的参数对DLL进行新的调用。因此,我需要中断Python控制流,以便从Python向DLL(和MT4)临时返回部分结果,并等待来自MT4的新请求 如何以Python风格创建这个(丑陋的)双向交互?我需要一些功能性的、但仅在函数范围内的yield工作,当我需要类似yield的异常时:从调用堆栈的底部到顶部,并且能够通过调用顶部Python脚本中的main.next()将控制流返回到上一个屈服点 MT4伪代码:以产量为例外的双向交互作用 我编写C++ DLL,它是元交易者4应用程序(MT4)和Python脚本(嵌入Python到MT4)之间的门。MT4将请求发送到此DLL并等待指令执行(例如,字符串命令数组)。DLL将MT4请求的解析传递给Python脚本。Python脚本需要从MT4获得一些信息来解析每个请求。因此,MT4和Python具有双向通信,python,dll,python-3.3,Python,Dll,Python 3.3,但MT4不支持双向通信,它只能解析自己的DLL请求的结果,并用新的参数对DLL进行新的调用。因此,我需要中断Python控制流,以便从Python向DLL(和MT4)临时返回部分结果,并等待来自MT4的新请求 如何以Python风格创建这个(丑陋的)双向交互?我需要一些功能性的、但仅在函数范围内的yield工作,当我需要类似yield的异常时:从调用堆栈的底部到顶部,并且能够通过调用顶部Python脚本中的main.next()将控制流返回到上一个屈服点 MT4伪代码: new_args = .
new_args = ...
while (true) {
cmds = DLL_GetCommands(new_args);
if (! cmd) { // No commands from Python
break;
}
new_args = _parseCommand(cmds);
}
char* __declspec(dllexport) DLL_GetCommands(char* args) {
// Python already initialized.
// Some python script already executed
// and I have local scope of this execution.
PyObject var = ...search some object variable in Python...
// var is instance of Advert class
return PyObject_CallMethodObjArgs(var, "parse_tick", args, NULL);
}
DLL伪代码:
new_args = ...
while (true) {
cmds = DLL_GetCommands(new_args);
if (! cmd) { // No commands from Python
break;
}
new_args = _parseCommand(cmds);
}
char* __declspec(dllexport) DLL_GetCommands(char* args) {
// Python already initialized.
// Some python script already executed
// and I have local scope of this execution.
PyObject var = ...search some object variable in Python...
// var is instance of Advert class
return PyObject_CallMethodObjArgs(var, "parse_tick", args, NULL);
}
Python代码:
class Handler():
def some_cpp_request(self, a, b):
yield 'some_cpp_request'
# After second call to Advert.parse_tick() control flow should return here.
class Advert():
def __init__(self):
self.h = Handler()
# This method and all what it call should work as single generator
def parse_tick(self):
for i in range(2):
self._some_method(i, i)
def _some_method(self, a, b):
self.h.some_cpp_request(a, b)
[更新]在收到abarnert的建议后,我有了工作解决方案:
class Handler():
def __init__(self):
self.cmds = []
self.cmds_results = []
def some_cpp_request(self, a, b):
self.cmds.append(("SOME_CPP_REQUEST", a, b))
yield
# Here self.cmds_results contains MT4 response.
class Advert():
def __init__(self):
self.h = Handler()
def parse_tick(self):
for i in range(2):
yield from self._some_method(i, i)
return 'xxx'
def _some_method(self, a, b):
yield from self.h.some_cpp_request(a, b)
parser = Advert()
gen = parser.parse_tick()
# This loop should be written in DLL layer.
while True:
next(gen)
parser.h.cmds_results.clear()
for cmd in parser.h.cmds:
# Adding some results
parser.h.cmds_results.append((cmd, 'SOME RESULT'))
parser.h.cmds.clear()
p.S.更舒适的解决方案是创建并行线程(或进程),而不是调用和捕获收益率:这不需要替换所有收益率。两个线程可以通过两个阻塞队列进行通信。队列:
- 子线程:
- 将请求放入请求队列
- 在响应队列中阻止等待新元素
- 主线程:
- 无限阻塞等待请求队列中的新元素
- 将响应放入请求队列
如果请求为None,则为finishruntime:child和main threads中断无限循环。如果要在Python代码中使用
yield
,只需执行以下操作:
def parse_tick(self):
for i in range(2):
yield self._some_method(i, i)
无论是从Python还是从C语言调用Python生成器函数,都会得到一个迭代器。每次从迭代器获得next
值时,它都会在最后一个yield
点之后重新激活生成器函数
而且很容易。下面是一些伪代码(如您现有的伪代码、跳过错误处理、重新计数等):
例外情况如何?没问题。如果从Python中迭代的生成器函数引发异常,
next
会向调用方引发该异常。如果用C进行迭代,那么PyIter\u Next
返回与完成迭代器相同的NULL
,那么如何区分它们呢?通过检查
或者,您可以将回调函数传递到Python代码中,Python代码使用每个值调用该回调,而不是
yield
ing每个值。这就是嵌入Python的应用程序的传统做法。但是,如果你已经在用发电机思考问题,你就不需要回到过去的方式
首先,听起来您想重新调整异常的用途,以便
raise
ing一个异常自动地像yield
一样,可以从中恢复。那不行。非生成器函数的函数无法恢复,句号。即使是发电机功能,也只能在收益
后恢复,而不能在提高
(或返回
)后恢复。这是没有办法的;它将从根本上改变raise
(和return
)的语义
所以,你必须把你想要驱动的低级函数,像发电机一样,转换成实际的发电机。(或者将它们分解为更小的函数并按顺序调用,或者将它们转换为保持显式状态并继续执行\uuuuu call\uuuuu
的对象,或者其他比惯用方式更费事的操作。)
接下来,听起来您希望隐式嵌套生成器。Python生成器不是这样工作的。如果调用生成器函数,则返回迭代器。即使你自己也是一个生成函数,它也不能代表你产生值。如果要嵌套,必须使其显式,如下所示:
def _some_method(self, a, b):
yield a
yield b
def parse_tick(self):
for i in range(2):
yield from self._some_method(i, i)
请注意,调用方(无论是C还是Python)不必知道parse\u tick
实际上是委托给其他生成器来完成其工作,或者\u某些方法
挂起在某个地方。它只要求parse_tick
查找下一个值,它看到的只是返回0
,然后下一次0
,然后1
,然后完成
和<代码> PARSEGITICK 不必记住,它有一个挂起的< < >代码>方法> <代码>,因为它被悬挂在<代码>的中间。下次恢复时,它将自动恢复
\u some\u method
调用,或者,如果调用已用尽,则继续执行下一行代码
试图解释这种设计背后的基本原理,以及如何使用它,如果我没有说清楚的话。格雷格·尤因还使用了来自的收益率。即使我的解释能力只有他一半,我也不可能像他那样在一个如此简单的答案中解释得那么多
或者,您可以在生成器上使用send
方法。如果仔细观察,yield
和yield from
是表达式,带有值,而不是语句。如果通过调用next
来驱动生成器,则表达式的值仅为None
,这不是很有用。但是,通过在生成器上调用send
方法,您不仅可以要求它恢复,还可以给它一个值以恢复。(如果需要,您还可以调用throw
向生成器中引发异常。)这可以让您根据自顶向下的协程编写控制流,我认为