Эмуляция теста членства в Python:правильное делегирование __contains__ содержащемуся объекту

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

Вопрос

Я привык к тому, что Python позволяет использовать некоторые хитрости для делегирования функциональности другим объектам.Одним из примеров является делегирование содержащимся объектам.

Но, похоже, мне не повезло, когда я хочу делегировать __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

Что я делаю неправильно?Когда я вызываю .__contains __(1), все проходит гладко.Я даже пытался определить метод __iter__ в A, чтобы 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, вместо следования обычным маршрутам поиска, включающим наследование и дескрипторы, это немного сокращает накладные расходы).

Метакласс для автоматизации self.__class__Идею -per-instance не составит труда написать (она может выполнить грязную работу в сгенерированном классе). __new__ и, возможно, также установить все магические имена для фактического назначения классу, если они назначены на экземпляре, либо через __setattr__ или многие, много характеристики).Но это было бы оправдано только в том случае, если бы потребность в этой функции была действительно широко распространенной (например,перенос огромного древнего проекта Python 1.5.2, в котором широко используются «специальные методы для каждого экземпляра», на современный Python, включая Python 3).

Я рекомендовать «умные» или «черные магические» решения?Нет, я не:почти всегда лучше делать что-то простым и понятным способом.Но «почти» здесь является важным словом, и приятно иметь под рукой такие продвинутые «крючки» для редких, но не несуществующих ситуаций, когда их использование действительно может быть оправдано.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top