وكيل فئة في بايثون
-
21-12-2019 - |
سؤال
أنا أستخدم وحدة python-mpd2 للتحكم في مشغل الوسائط على Raspberry Pi في تطبيق واجهة المستخدم الرسومية.وبالتالي، أرغب في التعامل مع أخطاء الاتصال والمهلات بأمان (يقوم اللاعب المعني بإسقاط اتصالات MPD بعد 60 ثانية) في الخلفية.ومع ذلك، لا تحتوي وحدة 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 قد نشر حلاً مشابهًا (على الرغم من وجود بعض الاختلافات).لا أعلم لماذا قام بحذفها..إذا تم إلغاء حذف هذه الإجابة، فسأمنحها بكل سرور الفضل الذي تستحقه.