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 < 2
nie jest w ogóle wartością logiczną; jest to tablica wartości logicznych reprezentujących, czy każdy element x
był mniejszy niż 2., x[x < 2] = 0
a następnie wybiera elementy, x
któ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
import
odrę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.
x
i 2
być „ uporządkowane konsekwentnie, ale dowolnie ” oraz że kolejność może się zmienić w różnych implementacjach Pythona.
x<2 == false
?
bool
nie jest konwertowane na liczbę całkowitą, a bool
w Pythonie jest liczbą całkowitą
bool
jest podklasą z int
.
Oryginalny kod w twoim pytaniu działa tylko w Pythonie 2. Jeśli x
jest list
w Pythonie 2, porównanie x < y
jest False
wtedy, gdy y
jest int
eger. 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] < 2
obliczana na, False
ponieważ 2
jest liczbą, a zatem „mniejszą” niż list
w 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 bool
jest 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śćTypeError
wyjątek, gdy argumenty nie mają znaczącego naturalną kolejność. W ten sposób wyrażenia podoba1 < ''
,0 > None
czylen <= len
nie są już ważne, a npNone < None
podbiciaTypeError
zamiast 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__
)
+False
Cześć Perl, hej JavaScript, jak się macie?
UNARY_POSITIVE
opcode, 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<d
ocenia do True
lub False
.
(a, b)
jest krotką.
Indeksowanie krotki działa jak indeksowanie listy: (3,5)[1]
== 5
.
True
jest równa 1
i False
równa się 0
.
(a,b)[c<d]
(a,b)[True]
(a,b)[1]
b
lub dla False
:
(a,b)[c<d]
(a,b)[False]
(a,b)[0]
a
W 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 x
dział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 x
jest list
albo numpy.ndarray
ale 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] = othervalue
jest (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.