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

Создание len() работы с методами экземпляра

Есть ли способ сделать len() работать с методами экземпляра без изменения класса?

Пример моей проблемы:

>>> class A(object):
...     pass
...
>>> a = A()
>>> a.__len__ = lambda: 2
>>> a.__len__()
2
>>> len(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'A' has no len()

Примечание:

  • разные экземпляры A будут иметь разные приемы __len__
  • Я не могу изменить класс A
4b9b3361

Ответ 1

Фактически это возможно без изменения класса на основе этого ответа от Alex Martelli:

class A(object):
    def __init__(self, words):
        self.words = words

    def get_words(self):
        return self.words


a = A("I am a")
b = A("I am b")

def make_meth(inst, _cls, meth, lm):
    inst.__class__ = type(_cls.__name__, (_cls,), {meth: lm})

make_meth(a, A, "__len__", lambda self: 12)
make_meth(b, A, "__len__", lambda self: 44)

print(len(b))
print(len(a))
print(a.get_words())
print(b.get_words())

Если мы запустим код:

In [15]: a = A("I am a")    
In [16]: b = A("I am b")    
In [17]: make_meth(a, A, "__len__", lambda self: 12)
In [18]: make_meth(b, A, "__len__", lambda self: 44) 
In [19]: print(len(b))
44    
In [20]: print(len(a))
12

In [21]: print(a.get_words())
I am a    
In [22]: print(b.get_words())
I an b

В соответствии с последней частью последней части связанного ответа вы можете добавлять любые методы на основе каждого экземпляра, используя inst.specialmethod после того, как вы использовали inst.__class__ = type(...:

In [34]: b.__class__.__str__ =  lambda self: "In b"

In [35]: print(str(a))
<__main__.A object at 0x7f37390ae128>

In [36]: print(str(b))
In b

Ответ 2

Нет. Python всегда ищет специальные методы через класс объектов. Для этого есть несколько веских причин: один из них: repr(A) должен использовать type(A).__repr__ вместо A.__repr__, который предназначен для обработки экземпляров A вместо самого класса A.

Если вы хотите, чтобы разные экземпляры A вычисляли их len по-другому, рассмотрите возможность делегирования __len__ другому методу:

class A(object):
    def __len__(self):
        return self._len()

a = A()
a._len = lambda: 2

Ответ 3

В классе должны быть определены специальные методы, такие как __len__ (double-underscore или "dunder" ). Они не будут работать, если они определены только в экземпляре.

В экземпляре можно определить методы non-dunder. Тем не менее, вы должны преобразовать свою функцию в метод экземпляра, добавив к ней обертку, с которой передается self. (Обычно это делается при доступе к методу, так как метод, определенный в классе, является дескриптором, который возвращает обертку.) Это можно сделать следующим образом:

a.len = (lambda self: 2).__get__(a, type(a))

Объединяя эти идеи, мы можем написать __len__() в классе, который делегирует len(), который мы можем определить в экземпляре:

class A(object):
     def __len__(self):
         return self.len()

a = A()
a.len = (lambda self: 2).__get__(a, type(a))

print(len(a))  # prints 2

Вы можете упростить это в своем случае, потому что вам не нужно self, чтобы вернуть константу 2. Поэтому вы можете просто назначить a.len = lambda: 2. Однако, если вам нужно self, тогда вам нужно создать оболочку метода.

Ответ 4

Вы должны определить специальные методы во время определения класса:

class A(object):
    def __len__(self):
        return self.get_len()

a = A()
a.get_len = lambda: 2
print len(a)

Ответ 5

Вы не указали, используете ли вы Python 2.x или 3.x, и в этом случае это зависит! В 3.x на ваш вопрос уже были ответы другие люди. В 2.x ваш вопрос на самом деле имеет простой ответ: прекратите получать объект! (Конечно, если вы будете следовать этому подходу, вы не сможете выполнить обновление до 3.x, пока не найдете другой способ сделать это, поэтому не делайте то, что я предлагаю, если у вас нет планов по обновлению в ближайшее время.)

Type "copyright", "credits" or "license()" for more information.
>>> class A:pass

>>> a=A()
>>> a.__len__ = lambda: 2
>>> len(a)
2
>>> 

Наслаждайтесь:)