我在Python中编写了一个基本上执行以下操作的小程序:
我想添加通过网络执行命令的功能,如下所示(并学会在途中使用Twisted):
注意:应该可以在本地(使用下面的代码)和远程输入命令。
经过一番思考后,我无法想出任何其他方法来实现这个:
由于我没有那么多线程经验,也没有网络编程经验,我想不出任何其他对我有意义的方案。
上述方案是否过于复杂? 在尝试以这种方式实现之前,我将不胜感激。
python程序的代码(没有客户端)是:
main(这是start()方法):
class Controller:
def __init__(self,listener, executor):
self.listener = listener
self.executor = executor
def start(self):
while True:
text = self.listener.listen_for_hotword()
if self.executor.is_hotword(text):
text = self.listener.listen_for_command()
if self.executor.has_matching_command(text):
self.executor.execute_command(text)
else:
tts.say("No command found. Please try again")
监听器(从用户那里获得输入):
class TextListener(Listener):
def listen_for_hotword(self):
text = raw_input("Hotword: ")
text =' '.join(text.split()).lower()
return text
def listen_for_command(self):
text = raw_input("What would you like me to do: ")
text = ' '.join(text.split()).lower()
return text
执行程序(执行给定命令的类):
class Executor:
#TODO: Define default path
def __init__(self,parser, audio_path='../Misc/audio.wav'):
self.command_parser = parser
self.audio_path = audio_path
def is_hotword(self,hotword):
return self.command_parser.is_hotword(hotword)
def has_matching_command(self,command):
return self.command_parser.has_matching_command(command)
def execute_command(self,command):
val = self.command_parser.getCommand(command)
print val
val = os.system(val) #So we don't see the return value of the command
命令文件解析器:
class KeyNotFoundException(Exception):
pass
class YAMLParser:
THRESHOLD = 0.6
def __init__(self,path='Configurations/commands.yaml'):
with open(path,'r') as f:
self.parsed_yaml = yaml.load(f)
def getCommand(self,key):
try:
matching_command = self.find_matching_command(key)
return self.parsed_yaml["Commands"][matching_command]
except KeyError:
raise KeyNotFoundException("No key matching {}".format(key))
def has_matching_command(self,key):
try:
for command in self.parsed_yaml["Commands"]:
if jellyfish.jaro_distance(command,key) >=self.THRESHOLD:
return True
except KeyError:
return False
def find_matching_command(self,key):
for command in self.parsed_yaml["Commands"]:
if jellyfish.jaro_distance(command,key) >=0.5:
return command
def is_hotword(self,hotword):
return jellyfish.jaro_distance(self.parsed_yaml["Hotword"],hotword)>=self.THRESHOLD
配置文件示例:
Commands:
echo : echo hello
Hotword: start
答案 0 :(得分:4)
我发现在你的问题中找到背景非常困难,但我会自己去解决问题。
正如你在问题中所提到的,写一个"步行和嚼口香糖的典型方法" style app是以线程或事件循环样式设计代码。
鉴于你在谈论线程和扭曲(这是事件循环风格),我担心你可能会考虑混合两者。
我认为它们是根本不同的编程风格(每个都有他们擅长的地方),混合它们通常是通往地狱的道路。
让我给你一些解释背景
如何思考这个概念:
我需要同时做多件事,并且我希望我的操作系统能够计算运行这些单独任务的方式和时间。
加号:
' 的'让一个程序同时使用多个处理器核心的方法
在posix世界中, only 让一个进程同时在多个CPU核心上运行的方式是通过线程(典型的理想线程数不再是给定的核心)机)
更容易入手
您运行内联的相同代码通常无需重新设计即可被抛入线程中(如果没有GIL,则需要进行一些锁定,但稍后会更多)
更容易使用会占用你可以给他们的所有CPU的任务
即。在大多数情况下,使用线程解决方案然后使用事件/异步框架,数学方式更容易处理
劣势:
Python对线程有一个特殊问题
在CPython中,global interpreter lock(GIL)可以否定线程执行多任务的能力(使线程几乎无用)。 Avoiding the GIL很麻烦,可以撤消在线程中使用的所有易用性
当您添加线程(和锁定)时,事情变得复杂,请参阅此SO:Threading Best Practices
非常适合IO /用户交互任务
虽然线程非常擅长处理少量想要使用大量CPU的任务(理想情况下每个核心一个线程),但它们在处理大部分时间等待的大量任务时却不太理想。
最好用:
计算成本高昂的东西。
如果你想要同时运行大量的数学运算,那么你就不太可能比操作系统更智能地调度CPU利用率。
(考虑到CPythons GIL问题,线程不应该手动用于数学,而应该使用内部线程的库(如NumPy))
如何思考这个概念:
我需要同时做多件事,但我(程序员)希望直接控制/直接暗示我的子任务运行的方式和时间
您应该如何考虑代码:
将所有子任务想象成一个交织在一起的整体,你的思想应该总是想到"这段代码运行得足够快,以至于它不会搞砸其他子任务我&#34 39;管理"
加号:
网络/ IO / UI连接非常高效,包括大量连接
事件循环风格程序是解决c10k problem的关键技术之一。像Twisted这样的框架可以在小型机器上运行的一个python进程中处理数万个连接。
随着其他连接/任务的添加,可预测的(小的)复杂性增加
虽然存在相当陡峭的学习曲线(特别是在扭曲中),但一旦了解了基础知识,就可以将新的连接类型添加到项目中,同时最大限度地提高整体复杂性。从提供键盘接口的程序转移到提供键盘/ telnet / web / ssl / ssh连接的程序可能只是每个接口的几行胶水代码(...这是由框架高度可变,但无论如何框架事件循环复杂性更可预测,然后线程随着连接数的增加而增加)
劣势:
开始更难。
事件/异步编程需要与第一行代码不同的设计风格(尽管您会发现设计风格在某种程度上可以从语言移植到语言)
一个事件循环,一个核心
虽然事件循环可以让您同时处理大量的IO连接,但它们自身并不能同时在多个核心上运行。解决这个问题的传统方法是编写程序,使程序的多个实例可以同时运行,每个核心一个(此技术绕过GIL问题)
多路复用高CPU任务可能很困难
事件编程需要将高CPU任务分成几部分,以使每个部分占用(理想情况下)少量的CPU,否则每当高CPU任务运行时事件系统就会停止多任务。
最好用:
基于IO的内容
虽然您的应用程序似乎不是基于IO的,但它似乎都不是基于CPU的(看起来您通过system
调用当前播放的音频,system
旋转每次调用它都是一个独立的进程,所以它的工作不会烧掉你的进程CPU - 尽管system
阻塞,所以它是一个禁止扭曲的 - 你必须在扭曲中使用不同的调用。)
您的问题也并不表示您担心最大限度地使用多个核心。
因此,鉴于您特别谈到Twisted,并且事件循环解决方案似乎是您的应用程序的最佳匹配,我建议您查看Twisted和-not-threads。
鉴于'最佳用途'上面列出的你可能会想到混合扭曲和线程是得到的方法,但是如果你做任何事情,甚至稍微错误,你将失去事件循环的优点(你会阻止)和线程化(GIL不会让线程多任务)并且拥有超级复杂的东西,这对你没有任何好处。
&#39>计划'你给的是:
经过一番思考后我无法想出任何其他方式来实现这个目的:
- 将上述程序作为进程#1运行(在我开始编写的本地运行程序)。
- Twisted客户端将作为进程#2运行,并从远程客户端接收命令。每当收到命令时,Twisted客户端将初始化一个线程,该线程将解析命令,检查其有效性并在其有效时执行。
醇>由于我没有那么多线程经验,也没有网络编程经验,我无法想到任何其他对我有意义的方案。
回答"计划是否......过于复杂",我几乎肯定会说是因为你谈论扭曲的和线程。 (见tr; dr)
鉴于我对你想要构建的东西的肯定不完整(和困惑)的理解,我会想象一个扭曲的解决方案,你会看起来像:
如果你在问题中说明,你真的需要一台服务器,你可以编写第二个扭曲的程序来提供(你可以在krondo指南中看到所有这些的例子)。虽然我猜你什么时候理解扭曲的图书馆支持,但你会发现你不必构建任何额外的服务器,你可以在你的基本代码中包含你需要的任何协议。