Natknąłem się na kod z linią podobną do
x[x<2]=0
Bawiąc się wariacjami, wciąż tkwię w tym, co robi ta składnia.
Przykłady:
>>> x = [1,2,3,4,5]
>>> x[x<2]
1
>>> x[x<3]
1
>>> x[x>2]
2
>>> x[x<2]=0
>>> x
[0, 2, 3, 4, 5]
Natknąłem się na kod z linią podobną do
x[x<2]=0
Bawiąc się wariacjami, wciąż tkwię w tym, co robi ta składnia.
Przykłady:
>>> x = [1,2,3,4,5]
>>> x[x<2]
1
>>> x[x<3]
1
>>> x[x>2]
2
>>> x[x<2]=0
>>> x
[0, 2, 3, 4, 5]
TypeError: unorderable types: list() < int().
Odpowiedzi:
Ma to sens tylko w przypadku tablic NumPy . Zachowanie z listami jest bezużyteczne i specyficzne dla Pythona 2 (nie Pythona 3). Możesz chcieć dwukrotnie sprawdzić, czy oryginalny obiekt rzeczywiście był tablicą NumPy (patrz poniżej), a nie listą.
Ale w twoim kodzie tutaj x jest prostą listą.
Od
x < 2
jest fałszywe, czyli 0, dlatego
x[x<2] jest x[0]
x[0] zmienia się.
Odwrotnie, x[x>2]jest x[True]lubx[1]
Więc x[1]zmienia się.
Dlaczego to się dzieje?
Zasady porównania to:
Kiedy zamawiasz dwa ciągi lub dwa typy liczbowe, porządkowanie odbywa się w oczekiwany sposób (porządek leksykograficzny dla łańcuchów, porządek numeryczny dla liczb całkowitych).
Kiedy zamawiasz typ numeryczny i nieliczbowy, typ numeryczny jest pierwszy.
Kiedy zamawiasz dwa niezgodne typy, w których żaden nie jest numeryczny, są one sortowane według kolejności alfabetycznej ich nazw typów:
Mamy więc następującą kolejność
numeryczne <lista <ciąg <krotka
Zobacz zaakceptowaną odpowiedź na pytanie Jak Python porównuje ciąg i int? .
Jeśli x jest tablicą NumPy , składnia ma większy sens ze względu na indeksowanie tablic logicznych . W takim przypadku x < 2nie jest w ogóle wartością logiczną; jest to tablica wartości logicznych reprezentujących, czy każdy element xbył mniejszy niż 2., x[x < 2] = 0a następnie wybiera elementy, xktórych były mniejsze niż 2 i ustawia te komórki na 0. Zobacz Indeksowanie .
>>> x = np.array([1., -1., -2., 3])
>>> x < 0
array([False, True, True, False], dtype=bool)
>>> x[x < 0] += 20 # All elements < 0 get increased by 20
>>> x
array([ 1., 19., 18., 3.]) # Only elements < 0 are affected
importodrętwienie.
[0 if i < 2 else i for i in x],.) A może jest to zachęcający styl w Numpy?
x[x<2]zwróci tablicę numpy, podczas gdy [0 if i<2 else i for i in x]zwróci listę. Dzieje się tak, ponieważ x[x<2]jest to operacja indeksowania (określana w numpy / scipy / pandas jako operacja dzielenia ze względu na możliwość maskowania danych), podczas gdy rozumienie list jest nową definicją obiektu. Zobacz indeksowanie NumPy
>>> x = [1,2,3,4,5]
>>> x<2
False
>>> x[False]
1
>>> x[True]
2
Wartość bool jest po prostu konwertowana na liczbę całkowitą. Indeks wynosi 0 lub 1.
xi 2być „ uporządkowane konsekwentnie, ale dowolnie ” oraz że kolejność może się zmienić w różnych implementacjach Pythona.
x<2 == false?
boolnie jest konwertowane na liczbę całkowitą, a boolw Pythonie jest liczbą całkowitą
bool jest podklasą z int.
Oryginalny kod w twoim pytaniu działa tylko w Pythonie 2. Jeśli xjest listw Pythonie 2, porównanie x < yjest Falsewtedy, gdy yjest integer. Dzieje się tak, ponieważ nie ma sensu porównywanie listy z liczbą całkowitą. Jednak w Pythonie 2, jeśli operandy nie są porównywalne, porównanie opiera się w języku CPython na kolejności alfabetycznej nazw typów ; dodatkowo wszystkie liczby zajmują pierwsze miejsce w porównaniach typu mieszanego . Nie jest to nawet opisane w dokumentacji CPythona 2, a różne implementacje Pythona 2 mogą dawać różne wyniki. Wartość ta jest [1, 2, 3, 4, 5] < 2obliczana na, Falseponieważ 2jest liczbą, a zatem „mniejszą” niż listw CPythonie. Ostatecznie doszło do tego mieszanego porównaniauznana za zbyt mało znaną funkcję i została usunięta w Pythonie 3.0.
Teraz wynik <jest bool; i booljest podklasą zint :
>>> isinstance(False, int)
True
>>> isinstance(True, int)
True
>>> False == 0
True
>>> True == 1
True
>>> False + 5
5
>>> True + 5
6
Więc w zasadzie bierzesz element 0 lub 1 w zależności od tego, czy porównanie jest prawdziwe, czy fałszywe.
Jeśli wypróbujesz powyższy kod w Pythonie 3, otrzymasz TypeError: unorderable types: list() < int()ze względu na zmianę w Pythonie 3.0 :
Porównania zamówień
Python 3.0 uprościł zasady porządkowania porównań:
Operatorzy porównania zamawiania (
<,<=,>=,>) podnieśćTypeErrorwyjątek, gdy argumenty nie mają znaczącego naturalną kolejność. W ten sposób wyrażenia podoba1 < '',0 > Noneczylen <= lennie są już ważne, a npNone < NonepodbiciaTypeErrorzamiast wrócićFalse. Konsekwencją jest to, że sortowanie niejednorodnej listy nie ma już sensu - wszystkie elementy muszą być ze sobą porównywalne. Zauważ, że nie dotyczy to operatorów==i!=: obiekty różnych nieporównywalnych typów zawsze porównują ze sobą nierówne.
Istnieje wiele typów danych, które przeciążają operatory porównania, aby zrobić coś innego (ramki danych z pand, tablice numpy). Jeśli kod, którego używałeś, zrobił coś innego, to dlatego, że niex był alist , ale instancją innej klasy z <przesłoniętym operatorem w celu zwrócenia wartości, która nie jest bool; i ta wartość była następnie obsługiwana specjalnie przez x[](aka __getitem__/ __setitem__)
+FalseCześć Perl, hej JavaScript, jak się macie?
UNARY_POSITIVEopcode, który wywołuje__pos__
__setitem__zamiast __getitem__w swojej ostatniej sekcji. Mam również nadzieję, że nie masz nic przeciwko temu, że moja odpowiedź została zainspirowana tą częścią Twojej odpowiedzi.
__getitem__choć równie dobrze mogło być __setitem__i__delitem__
Ma to jeszcze jedno zastosowanie: golf kodowy. Code golf to sztuka pisania programów, które rozwiązują jakiś problem w jak najmniejszej liczbie bajtów kodu źródłowego.
return(a,b)[c<d]
jest w przybliżeniu odpowiednikiem
if c < d:
return b
else:
return a
z wyjątkiem tego, że zarówno a, jak i b są oceniane w pierwszej wersji, ale nie w drugiej.
c<docenia do Truelub False.
(a, b)jest krotką.
Indeksowanie krotki działa jak indeksowanie listy: (3,5)[1]== 5.
Truejest równa 1i Falserówna się 0.
(a,b)[c<d](a,b)[True](a,b)[1]blub dla False:
(a,b)[c<d](a,b)[False](a,b)[0]aW sieci wymiany stosów znajduje się dobra lista wielu paskudnych rzeczy, które możesz zrobić w Pythonie, aby zaoszczędzić kilka bajtów. /codegolf/54/tips-for-golfing-in-python
Chociaż w normalnym kodzie nigdy nie powinno to być używane, aw twoim przypadku oznaczałoby to, że xdziała zarówno jako coś, co można porównać do liczby całkowitej, jak i jako kontener obsługujący cięcie na plasterki, co jest bardzo nietypową kombinacją. To prawdopodobnie kod Numpy, jak zauważyli inni.
Code Golf is the art of writing programs: ')
Ogólnie może to oznaczać wszystko . Było to już wyjaśniono, co to znaczy, jeśli xjest listalbo numpy.ndarrayale w ogóle to zależy tylko od tego, jak operatorzy porównania ( <, >...), a także w jaki sposób get / set-item ( [...]-syntax) są realizowane.
x.__getitem__(x.__lt__(2)) # this is what x[x < 2] means!
x.__setitem__(x.__lt__(2), 0) # this is what x[x < 2] = 0 means!
Dlatego:
x < value jest równa x.__lt__(value)x[value] jest (w przybliżeniu) odpowiednikiem x.__getitem__(value) x[value] = othervaluejest (również w przybliżeniu) równoważne z x.__setitem__(value, othervalue).Można to dostosować, aby robić wszystko, co chcesz. Na przykład (naśladuje nieco numpys-boolean indeksowanie):
class Test:
def __init__(self, value):
self.value = value
def __lt__(self, other):
# You could do anything in here. For example create a new list indicating if that
# element is less than the other value
res = [item < other for item in self.value]
return self.__class__(res)
def __repr__(self):
return '{0} ({1})'.format(self.__class__.__name__, self.value)
def __getitem__(self, item):
# If you index with an instance of this class use "boolean-indexing"
if isinstance(item, Test):
res = self.__class__([i for i, index in zip(self.value, item) if index])
return res
# Something else was given just try to use it on the value
return self.value[item]
def __setitem__(self, item, value):
if isinstance(item, Test):
self.value = [i if not index else value for i, index in zip(self.value, item)]
else:
self.value[item] = value
Zobaczmy teraz, co się stanie, jeśli go użyjesz:
>>> a = Test([1,2,3])
>>> a
Test ([1, 2, 3])
>>> a < 2 # calls __lt__
Test ([True, False, False])
>>> a[Test([True, False, False])] # calls __getitem__
Test ([1])
>>> a[a < 2] # or short form
Test ([1])
>>> a[a < 2] = 0 # calls __setitem__
>>> a
Test ([0, 2, 3])
Zauważ, że to tylko jedna możliwość. Możesz wdrożyć prawie wszystko, co chcesz.