Подтвердить что ты не робот

Есть ли способ реализовать такие методы, как __len__ или __eq__ как classmethods?

В Python довольно легко реализовать метод __len__(self), чтобы он обрабатывал вызовы len(inst), подобные этому:

class A(object):

  def __len__(self):
    return 7

a = A()
len(a) # gives us 7

И есть множество одинаковых методов, которые вы можете определить (__eq__, __str__, __repr__ и т.д.). Я знаю, что классы Python также являются объектами.

Мой вопрос: могу ли я как-то определить, например, __len__, чтобы следующее работало:

len(A) # makes sense and gives some predictable result
4b9b3361

Ответ 1

То, что вы ищете, называется "метаклассом"... так же, как a является экземпляром класса a, a является экземпляром класса, также называемым метаклассом. По умолчанию классы Python являются экземплярами класса type (единственное исключение относится к Python 2, у которого есть некоторые устаревшие классы "старого стиля", которые являются теми, которые не наследуются от object). Вы можете проверить это, выполнив type(A)... он должен вернуть type сам (да, этот объект был перегружен немного).

Метаклассы являются мощными и извращенными мозгами, чтобы заслужить больше, чем быстрое объяснение, которое я собирался написать... хорошей отправной точкой был бы этот вопрос stackoverflow: Что такое Metaclass.

Для вашего конкретного вопроса, для Python 2, создается метаклас, который псевдонимы len(A) вызывает метод класса в A:

class LengthMetaclass(type):

    def __len__(self):
        return self.clslength()

class A(object):
    __metaclass__ = LengthMetaclass

    @classmethod
    def clslength(cls):
        return 7

print len(A)

Python 3 работает аналогично, но вы использовали бы class A(object, metaclass=LengthMetaclass): вместо __metaclass__ = LengthMetaclass.

Причина LengthMetaclass.__len__ не влияет на экземпляры a, так это то, что разрешение атрибута в Python сначала проверяет экземпляр dict, затем проходит иерархию классов [A, object], но он никогда не обращается к метаклассам. Если для доступа к A.__len__ сначала обращается внимание на экземпляр a, тогда идет его иерархия классов, состоящая из [LengthMetaclass, type].

Ответ 2

Поскольку класс является экземпляром метакласса, одним из способов является использование настраиваемого метакласса:

>>> Meta = type('Meta', (type,), {'__repr__': lambda cls: 'class A'})
>>> A = Meta('A', (object,), {'__repr__': lambda self: 'instance of class A'})
>>> A
class A
>>> A()
instance of class A

Ответ 3

попробуйте следующее:

class Lengthy:
    x = 5
    @classmethod
    def __len__(cls):
        return cls.x

@classmethod позволяет вызывать его непосредственно в классе, но ваша реализация len не сможет зависеть от каких-либо переменных экземпляра:

a = Lengthy()
len(a)