我正在用C编写游戏引擎,可以选择在每一帧调用嵌入式Python解释器,从而可以修改游戏状态。它启动一个Python函数,该函数循环,等待用户通过命名的Unix管道来输入,并eval
输入。如果输入“ break”,它将退出循环并恢复C程序。一切都很好,但我希望能够调用一个advance_frame()
函数,该函数暂停python程序,并在同一位置的下一帧继续执行,因此我使该函数由C程序调用生成器,并调用其send
方法,而不是调用函数本身。
我知道“ yield from”可以创建一种函数堆栈,当遇到“ yield”时这些函数都将被挂起,因此在输入循环中我尝试了yield from eval(input_string, locals())
。问题是我想输入"advance_frame()"
,它会yield
,但我也想打电话,说"print("hello world")"
,这不会产生(这是很好,因为我不希望它将控制权从python传递出去。但是,如果对eval的调用以yield from
开头,则调用print
之类的普通函数就是TypeError: 'NoneType' object is not iterable
有没有解决的办法?基本上我需要像yield from <expression> if <expression calls a generator function> else <expression>
我知道对于函数调用,我可以做类似
的操作import inspect
if inspect.isgeneratorfunction(myfunction):
yield from myfunction()
else:
myfunction()
但是问题是我正在使用用户输入的表达式(可能包括函数调用),而不是函数对象。我能看到的唯一解决方案是使用yield from
,并捕获并忽略如果表达式不屈服而引发的TypeError
,但这似乎是错误的事情。
整个代码如下:
def while_eval():
# Get game state from caller in C:
state_ptr = yield
ctypes.pythonapi.PyCapsule_GetPointer.argtypes=[ctypes.py_object, ctypes.c_char_p]
ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.POINTER(structdef.struct_status_struct)
state_ptr=ctypes.pythonapi.PyCapsule_GetPointer(capsule,ctypes.c_char_p(None))
print("Handing control to the python interpreter, reading from the pipe pypipe")
try:
pipe = open('pypipe')
except Exception as e:
print(e)
return
while True:
try:
instr = pipe.readline()
if instr == 'break\n':
print("Leaving exec loop")
# Return to C program:
yield
else:
#yield from eval(instr,locals()) #Doesn't work for non yielding functions or statements like state_ptr=0
#eval(instr,locals()) # Doesn't work for yielding functions or statements like state_ptr=0
exec(instr,locals()) # Doesn't work for yielding functions
except Exception as e: print(e)