Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/346.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python中代理类_Python_Metaprogramming_Proxy Classes_Mpd - Fatal编程技术网

在Python中代理类

在Python中代理类,python,metaprogramming,proxy-classes,mpd,Python,Metaprogramming,Proxy Classes,Mpd,我正在使用python-mpd2模块控制GUI应用程序中Raspberry Pi上的媒体播放器。因此,我想在后台优雅地处理连接错误和超时(有问题的播放器在60秒后会断开MPD连接)。然而,MPD模块没有一个单一的入口点,通过它可以发送所有命令或检索我可以修补的信息 我想要一个类,它允许访问与mpd.MPDClient相同的所有方法,但让我添加自己的错误处理。换句话说,如果我这样做: client.play() 如果抛出一个connectione错误,我希望捕获它并重新发送相同的命令。除了由于必

我正在使用python-mpd2模块控制GUI应用程序中Raspberry Pi上的媒体播放器。因此,我想在后台优雅地处理连接错误和超时(有问题的播放器在60秒后会断开MPD连接)。然而,MPD模块没有一个单一的入口点,通过它可以发送所有命令或检索我可以修补的信息

我想要一个类,它允许访问与mpd.MPDClient相同的所有方法,但让我添加自己的错误处理。换句话说,如果我这样做:

client.play()
如果抛出一个connectione错误,我希望捕获它并重新发送相同的命令。除了由于必须重新连接到服务器而导致的小延迟之外,用户不应该注意到有任何问题

到目前为止,我已经想出了一个解决方案。它在我的应用程序中起作用,但并没有真正实现我的目标

from functools import partial
from mpd import MPDClient, ConnectionError

class PersistentMPDClient(object):
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.client = MPDClient()
        self.client.connect(self.host, self.port)

    def command(self, cmd, *args, **kwargs):
        command_callable = partial(self.client.__getattribute__(cmd), *args, **kwargs)
        try:
            return command_callable()
        except ConnectionError:
            # Mopidy drops our connection after a while, so reconnect to send the command
            self.client._soc = None
            self.client.connect(self.host, self.port)
            return command_callable()
我可以为每个MPD命令在此类中添加一个方法,例如:

def play(self):
    return self.command("play")

但这似乎远不是实现这一目标的最佳方法。

如果您不介意创建一个包含构成命令名的所有91个字符串的列表,那么您可以按照以下方法来做一些事情。我相信这种方法有很多优点,因为它涉及的魔法更少

91岁的奥托真是太多了。下面是一个自动解决方案,使用自定义的
\uuuu getattr\uuuu
,它返回一个包装器:

from functools import partial
import types

class DummyClient(object):
    def connect(self, *a, **kw): print 'connecting %r %r' % (a, kw)
    def play(self): print 'playing'
    def stop(self): print 'stopping'

class PersistentMPDClient(object):
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.client = DummyClient()
        self.client.connect(self.host, self.port)

    def __getattr__(self, attr, *args):
        cmd = getattr(self.client, attr, *args)
        if isinstance(cmd, types.MethodType):
            # a method -- wrap
            return lambda *a, **kw: self.command(attr, *a, **kw)
        else:
            # anything else -- return unchanged
            return cmd

    def command(self, cmd, *args, **kwargs):
        command_callable = partial(self.client.__getattribute__(cmd), *args, **kwargs)
        try:
            return command_callable()
        except ConnectionError:
            # Mopidy drops our connection after a while, so reconnect to send the command
            self.client._soc = None
            self.client.connect(self.host, self.port)
            return command_callable()

c = PersistentMPDClient(hostname, port)
c.play()
c.stop()

在我写这篇文章的时候,我注意到@MatToufoutu发布了一个类似的解决方案(尽管存在一些差异)。我不知道为什么他/她删除了它。。。如果这个答案不被删除,我很乐意给它应得的荣誉。

有多少个命令需要处理?有91个命令。并非所有这些都是必需的,也不会在我的应用程序中使用,但它们中有很大一部分是必需的。如果您不介意创建一个包含构成命令名的所有91个字符串的列表,您可以按照以下方法做些事情。我相信这种方法有很多优点,因为它涉及的魔法更少。OTOH,91确实很多,因此基于
\uuu getattr\uuuu
的更神奇的解决方案可能更合适。事实上,我删除了我的答案,因为这种方法不允许在调用
命令
方法时执行错误处理(OP希望这样做)。使错误处理成为可能会使事情变得更复杂,但另一个答案即将出现:)我的错误,搞错了(星期六,你知道^^^),不过,你的答案比我之前发布的更聪明(类型检查和参数处理),我将把它删除;)@MatToufoutu如您所愿python-mpd2模块包含一个我可以导入和使用的命令名字典,但是您提出的解决方案目前正在发挥作用。非常感谢。