محاكاة اختبار العضوية في بيثون: تفويض __contains__ إلى كائن محتوى بشكل صحيح

StackOverflow https://stackoverflow.com/questions/1022499

سؤال

أنا معتاد على أن بيثون يسمح لبعض الحيل الأنيقة بتفويض الوظائف إلى كائنات أخرى. مثال واحد هو التفويض إلى كائنات محتوية.

لكن تحظى بالحلقة ، عندما أرغب في تفويض __contains __:

class A(object):
    def __init__(self):
       self.mydict = {}
       self.__contains__ = self.mydict.__contains__

a = A()
1 in a

انا حصلت:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: argument of type 'A' is not iterable

ماذا أخطئ؟ عندما أتصل بـ A.__ يحتوي على __ (1) ، كل شيء يسير على نحو سلس. حتى أنني حاولت تحديد طريقة __iter __ في A لجعل مظهرًا أكبر ، لكنه لم يساعد. ماذا أفتقد هنا؟

هل كانت مفيدة؟

المحلول

طرق خاصة مثل __contains__ تكون خاصة فقط عند تعريفها على الفصل ، وليس على المثيل (باستثناء الفصول القديمة في Python 2 ، والتي يجب عليك ليس استخدام على أي حال).

لذا ، هل تفويضك على مستوى الفصل:

class A(object):
    def __init__(self):
       self.mydict = {}

    def __contains__(self, other):
       return self.mydict.__contains__(other)

أفضل في الواقع تهجئة الأخيرة return other in self.mydict, ، لكن هذه قضية طفيفة.

يحرر: إذا كان من الصعب تنفيذها وعندما "إعادة توجيه أساليب خاصة ديناميكية تمامًا" (مثل الفصول ذات الطراز القديم المعروضة) ، فليس من الصعب تنفيذها مع فصول جديدة: تحتاج فقط ملفوفة في فصلها الخاص. فمثلا:

class BlackMagic(object):
    def __init__(self):
        self.mydict = {}
        self.__class__ = type(self.__class__.__name__, (self.__class__,), {})
        self.__class__.__contains__ = self.mydict.__contains__

في الأساس ، بعد إعادة تعيين السحر الأسود self.__class__ إلى كائن فئة جديد (يتصرف تمامًا مثل الكائن السابق ولكن لديه قول فارغ ولا مثيلات أخرى باستثناء هذا self) ، في أي مكان في فصل قديم ستخصصه self.__magicname__, ، يسند إلى self.__class__.__magicname__ بدلاً من ذلك (وتأكد من أنها مدمجة أو staticmethod, ، ليست وظيفة Python عادية ، ما لم تكن بالطبع في بعض الحالات المختلفة تريد أن تتلقى self عند استدعاء مثيل).

بالمناسبة ، و in المشغل في حالة من هذا BlackMagic الفصل هو أسرع, ، كما يحدث ، أكثر من أي من الحلول المقترحة سابقًا - أو على الأقل ، فأنا أقيس مع موثوقي المعتاد -mtimeit (الذهاب مباشرة إلى built-in method, ، بدلاً من اتباع طرق البحث العادية التي تنطوي على الميراث والواصف ، يحلق القليل من النفقات العامة).

metaclass لأتمتة self.__class__-لن يكون من الصعب الكتابة على فكرة الفكرة القذرة (يمكن أن تقوم بالعمل القذر في الفصل الذي تم إنشاؤه __new__ الطريقة ، وربما أيضًا تعيين جميع الأسماء السحرية لتعيينها فعليًا في الفصل إذا تم تعيينها على المثيل ، إما عبر __setattr__ أو كثير ، عديدة الخصائص). ولكن لن يكون له ما يبرره إلا إذا كانت الحاجة إلى هذه الميزة واسعة الانتشار حقًا (على سبيل المثال ، يقوم بنقل مشروع Python 1.5.2 القديم الضخم الذي يستخدم بحرية "الأساليب الخاصة بالثبات" إلى Python الحديثة ، بما في ذلك Python 3).

هل أنا نوصي حلول "ذكي" أو "سحر أسود"؟ لا ، أنا لا: من الأفضل دائمًا القيام بالأشياء بطرق بسيطة ومباشرة. لكن "تقريبا" هي كلمة مهمة هنا ، ومن الجيد أن يكون هناك مثل هذه الخطافات المتقدمة في المواقف النادرة ، ولكن غير الموجودة ، حيث قد يكون هناك ما يبرر استخدامها بالفعل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top