Python ';非类型';对象没有属性';早期日期';

Python ';非类型';对象没有属性';早期日期';,python,python-multithreading,nonetype,concurrent.futures,pyttsx,Python,Python Multithreading,Nonetype,Concurrent.futures,Pyttsx,我刚刚开始学习Python,为此,我正在致力于实现一个简单的聊天机器人。这很好,直到我想实现一些文本到语音的功能,当它们出现在屏幕上时,它会说话。 为了实现这一点,我必须深入研究多线程,而这正是我所面临的困境: import concurrent.futures import pyttsx3 from time import sleep import sys # Settings engine = pyttsx3.init() voices = engine.getProperty('voic

我刚刚开始学习Python,为此,我正在致力于实现一个简单的聊天机器人。这很好,直到我想实现一些文本到语音的功能,当它们出现在屏幕上时,它会说话。 为了实现这一点,我必须深入研究多线程,而这正是我所面临的困境:

import concurrent.futures
import pyttsx3
from time import sleep
import sys

# Settings
engine = pyttsx3.init()
voices = engine.getProperty('voices')
engine.setProperty('voice', voices[0].id)
typing_delay=0.035

def textToSpeech(text):
    engine.say(text)
    engine.runAndWait()  
 
def typing(sentence):
    for char in sentence:
        sleep(typing_delay)
        sys.stdout.write(char)
        sys.stdout.flush()

# def parallel(string):
#     tasks = [lambda: textToSpeech(string), lambda: typing("\n> "+string+"\n\n")]
#     with ThreadPoolExecutor(max_workers=2) as executor:
#         futures = [executor.submit(task) for task in tasks]      
#         for future in futures:
#             try:
#                 future.result()
#             except Exception as e:
#                 print(e)
        
def parallel(text):
    with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
        future_tasks = {executor.submit(textToSpeech, text), executor.submit(typing, "\n> "+text+"\n\n")}
        for future in concurrent.futures.as_completed(future_tasks):
            try:
                data = future.result()
            except Exception as e:
                print(e)

# Test Sentence
parallel("Greetings Professor Falken")

上面的两个函数应该并行运行。我为parallel()函数尝试了两种不同的实现(其中一种被注释掉),但是这两种实现都会产生相同的结果。对于聊天机器人发出的第一行文本,我确实得到了文本和语音,但随后我得到了错误:
“非类型”对象没有属性“earlierDate”
在那之后,我只得到文本,没有更多的语音和错误:
run循环已经开始了

我假设
concurrent.futures
中的某个地方是属性
'earlierDate.
,并且我没有正确地处理它,因此文本到语音线程永远不会停止。但我不知道如何修复它

我希望这里有人有一个可能有用的想法。我已经将代码缩减到尽可能小的部分,但仍然可以运行和测试

附录:我在Python3.8上导入
pyttsx3
时遇到了问题,所以我降级到了Python3.7,在那里它似乎可以工作


更新: 所以我突然想到,当我专注于多线程时,问题可能一直是我的文本到语音实现

最明显的是我在全球范围内初始化了我的语音引擎。因此,我将设置移动到textToSpeech函数中:

def textToSpeech(text):
    engine = pyttsx3.init()
    voices = engine.getProperty('voices')
    engine.setProperty('voice', voices[0].id)
    engine.say(text)
    engine.runAndWait()
def runConsoleEventLoop(
    argv=None, installInterrupt=False, mode=NSDefaultRunLoopMode, maxTimeout=3.0
):
    if argv is None:
        argv = sys.argv
    if installInterrupt:
        installMachInterrupt()
    runLoop = NSRunLoop.currentRunLoop()
    stopper = PyObjCAppHelperRunLoopStopper.alloc().init()
    PyObjCAppHelperRunLoopStopper.addRunLoopStopper_toRunLoop_(stopper, runLoop)
    try:

        while stopper.shouldRun():
            nextfire = runLoop.limitDateForMode_(mode)
            if not stopper.shouldRun():
                break

            soon = NSDate.dateWithTimeIntervalSinceNow_(maxTimeout)
            nextfire = nextfire.earlierDate_(soon)
            if not runLoop.runMode_beforeDate_(mode, nextfire):
                stopper.stop()

    finally:
        PyObjCAppHelperRunLoopStopper.removeRunLoopStopperFromRunLoop_(runLoop)
运行循环已经启动
错误现在不会立即出现,我在前几次聊天机器人交互中都会收到文本和语音。 我仍然得到
'NoneType'对象没有属性“earlierDate”
错误,现在在每次聊天机器人输出之后,最终
运行循环已经开始了
错误再次出现,我失去了声音。尽管如此,我想还是更近了一步


更新2:

又挖了一天,我想我又近了一步。 这似乎是与多线程相关的Mac特定问题。我在人们遇到这个问题的不同领域发现了多个问题

我已经在PyObjCTools/AppHelper.py中找到了问题

在这里,我们具有以下功能:

def textToSpeech(text):
    engine = pyttsx3.init()
    voices = engine.getProperty('voices')
    engine.setProperty('voice', voices[0].id)
    engine.say(text)
    engine.runAndWait()
def runConsoleEventLoop(
    argv=None, installInterrupt=False, mode=NSDefaultRunLoopMode, maxTimeout=3.0
):
    if argv is None:
        argv = sys.argv
    if installInterrupt:
        installMachInterrupt()
    runLoop = NSRunLoop.currentRunLoop()
    stopper = PyObjCAppHelperRunLoopStopper.alloc().init()
    PyObjCAppHelperRunLoopStopper.addRunLoopStopper_toRunLoop_(stopper, runLoop)
    try:

        while stopper.shouldRun():
            nextfire = runLoop.limitDateForMode_(mode)
            if not stopper.shouldRun():
                break

            soon = NSDate.dateWithTimeIntervalSinceNow_(maxTimeout)
            nextfire = nextfire.earlierDate_(soon)
            if not runLoop.runMode_beforeDate_(mode, nextfire):
                stopper.stop()

    finally:
        PyObjCAppHelperRunLoopStopper.removeRunLoopStopperFromRunLoop_(runLoop)
靠近按钮的这一行就是罪魁祸首:
nextfire=nextfire.earlierDate\uuuu(很快)

对象
nextfire
似乎是一个日期。在Objective-C中,NSDate对象确实有一个
earlierDate()
方法,因此它应该可以工作。但是初始化有问题。当我
打印(nextfire)
时,我得到
。因此,一个非类型对象没有“earlierDate”属性也就不足为奇了。

因此,我已经解决了我的两个问题,但是,我以一种不令人满意的方式解决了这两个问题。有点黑。但这是我能做的最好的了,除了剖析
pyttsx3

1)首先,对于我的
运行循环已启动的问题
错误: 我已经将我的引擎初始化移回了
textToSpeech
函数之外的全局级别(就像我的初始代码片段一样)。 然后,每次调用
textToSpeech
函数之前,我都会输入以下代码:

try:
    engine.endLoop()
except Exception as e:
    pass
这样,当有一个循环已经在运行时,它会在新调用之前停止,从而防止错误的发生。如果没有循环运行,则什么也不会发生

2)我的主要问题是
'NoneType'对象没有属性'earlierDate'
错误更深一层。我看了各种来源,但我不完全明白那里发生了什么。 正如我在第二次更新中所写,错误源于
PyObjCTools/AppHelper.py
nextfire
使用
nsrunlop
中的
limitDateForMode
方法初始化,根据苹果文档,如果此模式没有输入源,则返回
nil

下一步是查看
pyttsx3/nsss.py
,从
PyObjCTools/AppHelper.py
中实例化此方法。但我并没有真正掌握该实例化是如何工作的,以及如何修复
nsrunlop
显然是在没有输入源的情况下实例化的,或者是否应该修复这一事实。 因此,我进行了一次卑鄙的黑客攻击,通过在
earlierDate\uuu
调用中切换
nextfire
soon
来更改
PyObjCTools/AppHelper.py

    try:

        while stopper.shouldRun():
            nextfire = runLoop.limitDateForMode_(mode)
            if not stopper.shouldRun():
                break

            soon = NSDate.dateWithTimeIntervalSinceNow_(maxTimeout)
            nextfire = soon.earlierDate_(nextfire)
            if not runLoop.runMode_beforeDate_(mode, nextfire):
                stopper.stop()

    finally:
        PyObjCAppHelperRunLoopStopper.removeRunLoopStopperFromRunLoop_(runLoop)
soon
变量始终正确地实例化为
NSDate
对象,因此方法
earlierDate
可用。显然,它也可以将
nil
用作参数


当然,如果在两个相关库中的一个库中有一个合适的修复程序,那就更好了。我的猜测是,
pyttsx3/nsss.py
需要一些工作,但我对
nsrunlops
了解得不够,无法得出明智的意见。

谢谢@thomin现在错误消失了,但我的程序在运行调用后立即终止。