Inne puste obiekty w Pythonie oceniają jako false - jak mogę to zrobić iteratory / generatory, aby to zrobić?

16
Ethan Furman 2 listopad 2011, 09:40

3 odpowiedzi

Najlepsza odpowiedź

Domyślnie wszystkie obiekty w Pythonie oceniają jako True. W celu wsparcia False oceny klasa obiektu musi mieć metodę __len__ (0 - & gt; {x4}}) lub {x5}} metoda (__nonzero__) X6}} - & gt; False). Uwaga: __nonzero__ == & gt; __bool__ w Pythonie 3.x.

Ponieważ protokół Iteratora jest celowo utrzymywany prosty, a ponieważ istnieje wiele typów iteratorów / generatorów, które nie są w stanie wiedzieć, czy istnieje więcej wartości do wytworzenia przed próbą ich wyprodukowania, {x1}} / False / False Ocena nie jest częścią protokołu Iteratora.

Jeśli naprawdę chcesz tego zachowania, musisz to zapewnić. Jednym ze sposobów jest owinięcie generatora / iteratora w klasie, która zapewnia brakującą funkcjonalność.

Należy zauważyć, że ten kod ocenia tylko False po StopIteration został podniesiony.

Jako bonus ten kod działa dla Pythonów 2.4+

try:
    next
except NameError:       # doesn't show up until python 2.6
    def next(iter):
        return iter.next()

Empty = object()

class Boolean_Iterator(object):
    """Adds the abilities
    True/False tests:  True means there /may/ be items still remaining to be used
    """
    def __init__(self, iterator):
        self._iter = iter(iterator)
        self._alive = True
    def __iter__(self):
        return self
    def __next__(self):
        try:
            result = next(self._iter)
        except StopIteration:
            self._alive = False
            raise
        return result
    next = __next__                     # python 2.x
    def __bool__(self):
        return self._alive
    __nonzero__ = __bool__              # python 2.x

Jeśli chcesz również zachowanie wyglądu (lub Peek), kod ten zrobi sztuczkę (ocenia False przed StopIteration zostanie podniesiony):

try:
    next
except NameError:       # doesn't show up until python 2.6
    def next(iter):
        return iter.next()

Empty = object()

class Iterator(object):
    """Adds the abilities
    True/False tests:  True means there are items still remaining to be used
    peek(): get the next item without removing it from the sequence
    """
    def __init__(self, iterator):
        self._iter = iter(iterator)
        self._peek = Empty
        self.peek()
    def __next__(self):
        peek, self._peek = self._peek, Empty
        self.peek()
        if peek is not Empty:
            return peek
        raise StopIteration
    next = __next__                     # python 2.x
    def __bool__(self):
        return self._peek is not Empty
    __nonzero__ = __bool__              # python 2.x
    def peek(self):
        if self._peek is not Empty:
            return self._peek
        self._peek = next(self._iter, Empty)
        return self._peek

Należy pamiętać, że zachowanie Peek nie jest właściwe, gdy czas leczenia podstawowego iteratora / generatora jest istotny dla jego wytworzonych wartości.

Należy również pamiętać, że kodeks strony trzecich i prawdopodobnie STDLIB może polegać na Iteratorach / generatorach, zawsze oceniających do True. Jeśli chcesz zaglądać bez bool, usuń metody __nonzero__ i __bool__.

7
Ethan Furman 25 marzec 2016, 22:22

Guido nie chce generatorów i ieratorów, aby zachowywać się w ten sposób.

Domyślnie obiekty są prawdziwe. Mogą być fałszywe tylko wtedy, gdy zdefiniują __Len__, które zwracają zero lub __nonzero__, że zwraca false (ten ostatni jest nazywany __bool__ w Py3.x).

Możesz dodać jedną z tych metod do niestandardowego iteratora, ale nie pasuje do intencji Guido. Odrzucił dodanie __Len__ do iteratory, w których znana jest nadchodząca długość. Tak ma zamiast tego mamy __lgth_hint__.

Więc jedynym sposobem, aby powiedzieć, czy iterator jest pusty, jest zadzwonić następny () na nim i sprawdź, czy podnosi stopitetację .

W ASPN wierzę, że istnieją pewne przepisy dotyczące tej techniki do opakowania Lookhead. Jeśli wartość zostanie pobrana, jest zapisywana na nadchodzącym następnym () połączeniu.

10
Raymond Hettinger 2 listopad 2011, 06:03

"Pusta rzecz" nie jest automatycznie iteratorem. Pojemniki mogą być puste lub nie, a można uzyskać iteratory na pojemnikach, ale te ieratorzy nie są fałszywe po wyczerpaniu.

Dobrym przykładem, dlaczego Iteratory nie stają się fałszywą, to sys.stdin. Problem z tworzeniem sys.stdin fałsz, gdy osiągnie koniec wejścia, jest to, że nie ma sposobu na rzeczywiście wiedząc, czy osiągnąłeś koniec takiego strumienia bez próby zużywania danych z niego. Głównym powodem, dla którego chce, aby iterator był fałszywy byłby "peek", aby zobaczyć, czy uzyskiwanie następnego przedmiotu byłoby ważne; Ale dla sys.stdin, to oczywiście nie jest praktyczne.

Oto kolejny przykład

(x for x in xrange(1000) if random.randrange(0, 2))

Nie ma sposobu, aby wiedzieć, czy ten generator zwróci więcej liczb bez pracy z pracy, musisz dowiedzieć się, jaka będzie kolejna wartość.

Rozwiązaniem jest po prostu uzyskać następną wartość z Iteratora. Jeśli jest puste, pętla wyjdzie, albo otrzymasz wyjątek StopIteration, jeśli nie jesteś w pętli.

4
SingleNegationElimination 2 listopad 2011, 06:06