Создание 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
Ответы
Ответ 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
>>>
Наслаждайтесь:)