Возможно ли изменить поведение len()?
Мне известно о создании настраиваемого метода __repr__
или __add__
(и т.д.), чтобы изменить поведение операторов и функций. Есть ли метод переопределения для len
?
Например:
class Foo:
def __repr__(self):
return "A wild Foo Class in its natural habitat."
foo = Foo()
print(foo) # A wild Foo Class in its natural habitat.
print(repr(foo)) # A wild Foo Class in its natural habitat.
Это можно сделать для len
, со списком? Обычно это будет выглядеть так:
foo = []
print(len(foo)) # 0
foo = [1, 2, 3]
print(len(foo)) # 3
Что делать, если я хочу оставить типы поиска вне счета? Вот так:
class Bar(list):
pass
foo = [Bar(), 1, '']
print(len(foo)) # 3
count = 0
for item in foo:
if not isinstance(item, Bar):
count += 1
print(count) # 2
Есть ли способ сделать это из подкласса list
?
Ответы
Ответ 1
Да, реализуем метод __len__
:
def __len__(self):
return 42
Демо:
>>> class Foo(object):
... def __len__(self):
... return 42
...
>>> len(Foo())
42
Из документации:
Вызывается для реализации встроенной функции len()
. Должен возвращать длину объекта, целое число >=
0. Кроме того, объект, который не определяет метод __bool__()
и метод __len__()
возвращает ноль, считается ложным в булевом контексте.
В вашем конкретном случае:
>>> class Bar(list):
... def __len__(self):
... return sum(1 for ob in self if not isinstance(ob, Bar))
...
>>> len(Bar([1, 2, 3]))
3
>>> len(Bar([1, 2, 3, Bar()]))
3
Ответ 2
Да, так же, как вы уже обнаружили, что вы можете переопределить поведение вызова функции repr()
, реализуя магический метод __repr__
, вы можете указать поведение из len()
вызов функции путем реализации (неожиданное удивление), затем __len__
magic:
>>> class Thing:
... def __len__(self):
... return 123
...
>>> len(Thing())
123
Педант может упомянуть, что вы не изменяете поведение len()
, вы изменяете поведение своего класса. len
делает то же самое, что и всегда, включая проверку атрибута __len__
в аргументе.
Ответ 3
Помните: Python динамически и Duck Typed.
Если он действует как то, что может иметь длину;
class MyCollection(object):
def __len__(self):
return 1234
Пример:
>>> obj = MyCollection()
>>> len(obj)
1234
если он не действует так, как будто он имеет длину; KABOOM!
class Foo(object):
def __repr___(self):
return "<Foo>"
Пример:
>>> try:
... obj = Foo()
... len(obj)
... except:
... raise
...
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
TypeError: object of type 'Foo' has no len()
От Typing:
Python использует утиную печать и набирает объекты, но нетипизированная переменная имена. Ограничения типа не проверяются во время компиляции; скорее, операции над объектом могут потерпеть неудачу, означая, что данный объект не подходящего типа. Несмотря на динамическую типизацию, Python строго типизированный, запрещающий операции, которые не определены четко (для например, добавление числа в строку), а не молча чтобы понять их.
Пример:
>>> x = 1234
>>> s = "1234"
>>> x + s
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Ответ 4
Вы можете просто добавить метод __len__
к вашему классу.
class Test:
def __len__(self):
return 2
a=Test()
len(a) # --> 2