Python에서 클래스 프록시
-
21-12-2019 - |
문제
저는 python-mpd2 모듈을 사용하여 GUI 애플리케이션에서 Raspberry Pi의 미디어 플레이어를 제어하고 있습니다.따라서 나는 백그라운드에서 연결 오류 및 시간 초과(문제의 플레이어가 60초 후에 MPD 연결을 끊음)를 적절하게 처리하고 싶습니다.그러나 MPD 모듈에는 패치할 수 있는 모든 명령이 전송되거나 정보가 검색되는 단일 진입점이 없습니다.
나는 mpd.MPDClient와 동일한 모든 메소드에 대한 액세스를 허용하는 클래스를 원하지만 내 자신의 오류 처리 기능을 추가하겠습니다.즉, 다음과 같이 하면 됩니다.
client.play()
그리고 연결 오류가 발생하면 이를 포착하여 동일한 명령을 다시 보내고 싶습니다.서버에 다시 연결해야 하기 때문에 약간의 지연이 발생하는 것 외에는 사용자가 잘못된 점을 알아차려서는 안 됩니다.
지금까지 제가 생각해낸 해결책은 다음과 같습니다.내 응용 프로그램에서 작동하지만 실제로 내 목표를 달성하지 못합니다.
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개의 문자열을 모두 포함하는 목록을 만들어도 괜찮다면 명령 이름, 당신은 다음과 같은 내용을 따라 뭔가를 할 수 있습니다 이 답변.나는 이 접근 방식이 마법을 덜 사용하기 때문에 많은 장점이 있다고 생각합니다.
OTOH, 91은 정말 많습니다.여기에 사용자 정의를 활용하는 자동 마법 솔루션이 있습니다. __getattr__
, 래퍼를 반환합니다.
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가 비슷한 솔루션을 게시한 것을 발견했습니다(그러나 약간의 차이점이 있습니다).그 사람이 왜 삭제했는지는 모르겠지만...해당 답변이 삭제 취소되지 않으면 기꺼이 그 답변에 합당한 공로를 인정하겠습니다.