Неверное поведение реализации str.join? - PullRequest
0 голосов
/ 18 ноября 2018

Рассмотрим следующий код:

class A(object):
    def __init__(self):
        self.a = '123'

    def __len__(self):
        print('len')
        return 2

    def __getitem__(self, pos):
        print('get pos', pos)
        return self.a[pos]

a = A()
print(''.join(a))

Мой ожидаемый результат:

> len
> get pos 0
> get pos 1
> 12

Реальный результат:

> len
> get pos 0
> get pos 1
> get pos 2
> get pos 3
> 123

Попробуйте сами. Я не могу поверить, что здесь происходит.

Как я правильно понимаю поведение, str.join () вызывает __ len __ , но игнорирует значение и вызывает __ getItem __ пока исключение индекса вне диапазона.

Я должен что-то упустить из виду, потому что реализация объединения выглядит иначе:

https://github.com/python/cpython/blob/3.6/Objects/stringlib/join.h

Мой текущий обходной путьэто:

def __getitem__(self, pos):
    if pos >= len(self):
      raise IndexError()
return self.a[pos]

Это смешно.

Я тестировал его с Python 3.6 и 3.7 (CPython).

1 Ответ

0 голосов
/ 18 ноября 2018

Как str.join работает (из анализа исходного кода)

Сначала он проверяет, является ли объект итеративным, и создает последовательность из него, если это необходимо

seq = PySequence_Fast(iterable, "can only join an iterable");

Если объект является list или tuple, он просто возвращает сам объект, итерировать не нужно.

Если это не так, то выполняется итерация для создания list. Вот где объект полностью повторяется.

Оттуда используется только копия list. iterable был повторен и теперь бесполезен, если это не было list или tuple.

(Я не мог отследить вызов до len, потребовался бы сеанс отладки, чтобы найти его в вызове PySequence_Fast, но это кажется бесполезным. У вашей итерации есть метод __len__, хорошо, но так как это не list или tuple, возвращаемое значение не используется)

...