2017-08-14 56 views
39

reversed 's typ to "typ":Dlaczego są odwrotnie i sortowane różne typy w Pythonie?

>>> type(reversed) 
<class 'type'> 

sorted' s typ jest "wbudowana funkcja lub metoda":

>>> type(sorted) 
<class 'builtin_function_or_method'> 

jednak zdają taki sam charakter. Pomijając oczywistą różnicę w funkcjonalności (sekwencje odwracania i sortowania), jaki jest powód tej różnicy w implementacji?

+1

Są różne, ponieważ robią różne rzeczy. Wynik końcowy może być postrzegany jako podobny, ale droga do osiągnięcia tego wyniku jest zupełnie inna. –

+1

Niewyjaśnione spekulacje: 'odwrócony' jest typem, ponieważ zwraca wartość iterowalną, która implementuje metodę [' __length_hint__'] (https://docs.python.org/3/reference/datamodel.html#object.__length_hint__). Ponieważ ta metoda musi być zdefiniowana w klasie, ma ona sens dla samego "odwróconego", aby była tą klasą. (Problem z tą teorią polega na tym, że '__length_hint__' istnieje tylko od python 3.4) –

+2

Pozwólcie, że zadam odwrotne pytanie: dlaczego oczekujecie, że będą tacy sami? – jpmc26

Odpowiedz

50

Różnica polega na tym, że reversed jest iteratorem (jest także leniwą wyceną), a sorted to funkcja, która działa "z zapałem".

Wszystkie wbudowane iteratory (przynajmniej w python-3.x) jak map, zip, filter, reversed ... są implementowane jako klas. Podczas gdy wbudowane moduły są bardzo przydatne, są to: funkcje, np. min, max, any, all i sorted.

>>> a = [1,2,3,4] 
>>> r = reversed(a) 
<list_reverseiterator at 0x2187afa0240> 

rzeczywiście trzeba „konsumować” iteracyjnej w celu uzyskania wartości (np list):

>>> list(r) 
[4, 3, 2, 1] 

Z drugiej strony ta „zużywa” część nie jest potrzebny do funkcji jak sorted:

>>> s = sorted(a) 
[1, 2, 3, 4] 

I W komentarzach zadano , dlaczego są one implementowane jako klasy zamiast funkcji. Odpowiedź na to pytanie nie jest łatwa, ale postaram się jak najlepiej:

Korzystanie z operacji leniwej oceny ma jedną ogromną zaletę: są one bardzo wydajne pod względem pamięci, gdy są powiązane.Nie muszą tworzyć pośrednich list, chyba że są wyraźnie "wymagane". Z tego powodu zmieniono map,i filter z gorliwie działających funkcji (python-2.x) na leniwy klasy operacyjne (python-3.x).

Generalnie istnieją dwa sposoby, w Pythonie, aby utworzyć iteratorów:

  • klas, które return self w ich sposobie __iter__
  • generator funkcji - Funkcje, które zawierają yield

natomiast (przynajmniej CPython) implementuje wszystkie wbudowane (i kilka standardowych modułów bibliotecznych) w C. Tworzenie klas iteratora w C jest bardzo łatwe, ale nie znalazłem sensownego sposobu tworzenia funkcji generatora jony oparte na C-API Pythona. Zatem powodem, dla którego te iteratory są implementowane jako klasy (w CPython) może być po prostu wygoda lub brak (szybkich lub możliwych do wdrożenia) alternatyw.

Istnieje dodatkowy powód, aby używać klas zamiast generatorów: Można implementować specjalne metody dla klas, ale nie można ich implementować w funkcjach generatora. To może nie brzmi imponująco, ale ma określone zalety. Na przykład większość iteratorów może być pickled (przynajmniej na Python-3.x) przy użyciu metod __reduce__ i __setstate__. Oznacza to, że można je przechowywać na dysku i umożliwia ich kopiowanie. Od wersji Python-3.4 niektóre iteratory również implementują __length_hint__, co sprawia, że ​​konsumowanie tych iteratorów za pomocą list (i podobnych) jest znacznie szybsze.


Zauważ, że reversed może być łatwo zaimplementowane jako fabrycznych funkcji (jak iter), ale w przeciwieństwie do iter, która może powrócić dwa unikalne klas reversed może jedynie powrócić jeden unikalny klasę.

Aby zilustrować możliwe (i wyjątkowe) zajęcia trzeba wziąć pod uwagę klasę, która nie ma __iter__ i nie __reversed__ metody, ale są iterable i reverse-iterable (poprzez wdrożenie __getitem__ i __len__):

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

    def __len__(self): 
     return len(self.vals) 

    def __getitem__(self, idx): 
     return self.vals[idx] 

I gdy ma to sens, aby dodać warstwę abstrakcji (funkcja fabryczne) w przypadku iter - bo wrócił klasa jest w zależności od liczby argumentów wejściowych:

>>> iter(A([1,2,3])) 
<iterator at 0x2187afaed68> 
>>> iter(min, 0) # actually this is a useless example, just here to see what it returns 
<callable_iterator at 0x1333879bdd8> 

To rozumowanie nie ma zastosowania do reversed:

>>> reversed(A([1,2,3])) 
<reversed at 0x2187afaec50> 
+0

mimo to +1, Metody 'itertools.' są również implementowane jako klasy, zgodnie z tą samą logiką, jak myślę, chociaż nadal nie jest jasne, dlaczego –

+0

zaktualizowałem nieco odpowiedź, aby podać jeden powód, dla którego są to klasy w CPython. Nie można jednak w znaczący sposób odpowiedzieć na "odwrócone", ponieważ może to być z łatwością funkcja fabryczna (np. 'Iter'). Ale jak 'iter' zwracana wartość' odwróconej' zawsze będzie funkcją klasy iteratora lub generatora. – MSeifert

+1

Dodawanie czegoś tutaj: pamiętaj, że przy leniwej ocenie wymagany jest jakiś obiekt kontenera do przechowywania informacji o bieżącym stanie. Można to zrobić tylko poprzez obiekt. A więc klasa. –

4

Jaka jest różnica między reversed i sorted?

Co ciekawe, reversed nie jest funkcją, natomiast sorted jest.

otworzyć sesję REPL i rodzaj help(reversed):

class reversed(object) 
| reversed(sequence) -> reverse iterator over values of the sequence 
| 
| Return a reverse iterator 

To jest rzeczywiście klasa, która służy do powrotu odwrotny iterator.

OK, więc reversed nie jest funkcją. Ale dlaczego nie?

To trochę trudno odpowiedzieć. Jednym z wyjaśnień jest to, że iteratory mają leniwą ocenę. Wymaga to jakiegoś kontenera do przechowywania informacji o bieżącym stanie iteratora w danym momencie. Najlepiej zrobić to za pomocą obiektu, a zatem, class.

+3

Jeśli dobrze rozumiem pytanie OP, pytają, dlaczego "odwrotnie" nie jest funkcją. (Albo dlaczego "posortowane" nie jest typem.) –

+0

Dokumenty mogą wydawać się nieco mylące, z "odwróconym" (i innymi klasami) wymienionymi pod "Wbudowanymi funkcjami" https://docs.python.org/3/library/functions.html –

Powiązane problemy