Operator tyldy w Pythonie

199

Jakie jest zastosowanie operatora tyldy w Pythonie?

Jedną rzeczą, o której mogę pomyśleć, jest zrobienie czegoś po obu stronach łańcucha lub listy, na przykład sprawdzenie, czy łańcuch jest palindromiczny czy nie:

def is_palindromic(s):
    return all(s[i] == s[~i] for i in range(len(s) / 2)) 

Jakieś inne dobre wykorzystanie?

clwen
źródło
11
Zauważ, że operator jednoargumentowego uzupełnienia ~implementowany metodą specjalną __invert__nie jest związany z notoperatorem, co logicznie neguje wartość zwracaną przez __bool__(lub __nonzero__w 2.x). Nie jest również związany z -operatorem jednoargumentowej negacji, implementowanym przez __neg__. Na przykład ~True == -2, który nie jest Falsefałszywy lub fałszywy, a -False == 0który nadal jest fałszywy.
Eryk Sun
@eryksun, chociaż to, co powiedziałeś, jest słuszne ( -False==0) To mylące, ponieważ mówiłeś o ~, a ~False == -1które nie jest fałszywe.
Guilherme de Lazari,
3
@GuilhermedeLazari, drugim przykładem było porównanie z negacją arytmetyczną ( __neg__). Prawdopodobnie powinienem był kontynuować True, np. -True == -1Który nie jest -2 lub Falsefałszywy, co bardziej wyraźnie łączy go z powrotem z ~Truewynikiem, a także, że negacja arytmetyczna a boolróżni się od negacji logicznej. Nie próbowałem być głęboki. Właśnie podkreślałem 3 operacje i podstawowe metody specjalne, które czasem się mylą.
Eryk Sun,

Odpowiedzi:

192

Jest to jednoargumentowy operator (biorąc pojedynczy argument) zapożyczony z C, gdzie wszystkie typy danych to po prostu różne sposoby interpretacji bajtów. Jest to operacja „odwracania” lub „uzupełniania”, w której wszystkie bity danych wejściowych są odwracane.

W Pythonie, w przypadku liczb całkowitych, bity reprezentacji uzupełnienia dwójkowego liczby całkowitej są odwrócone (jak w b <- b XOR 1przypadku każdego pojedynczego bitu), a wynik jest interpretowany ponownie jako liczba całkowita uzupełnienia dwójkowego. W przypadku liczb całkowitych ~xjest to równoważne z (-x) - 1.

Zmieniona forma ~operatora jest dostarczana jako operator.invert. Aby wesprzeć tego operatora we własnej klasie, podaj mu __invert__(self)metodę.

>>> import operator
>>> class Foo:
...   def __invert__(self):
...     print 'invert'
...
>>> x = Foo()
>>> operator.invert(x)
invert
>>> ~x
invert

Każda klasa, w której sensowne jest posiadanie „dopełnienia” lub „odwrotności” instancji, która jest również instancją tej samej klasy, jest potencjalnym kandydatem na operatora inwertowania. Jednak przeciążenie operatora może prowadzić do zamieszania w przypadku niewłaściwego użycia, więc upewnij się, że naprawdę ma sens, aby to zrobić przed dostarczeniem __invert__metody do twojej klasy. (Zauważ, że ciągi bajtów [np .: '\xff'] nie obsługują tego operatora, nawet jeśli odwrócenie wszystkich bitów ciągu bajtów ma sens.)

jagoda
źródło
16
Dobre wyjaśnienie, ale słowo ostrzeżenia - obowiązują tu wszystkie zastrzeżenia bezpieczeństwa dotyczące przeciążenia operatora - nie jest to dobry pomysł, chyba że idealnie pasuje do rachunku.
Eli Bendersky
Informacje zwrotne Eli zostały włączone do odpowiedzi w ostatnim akapicie.
wberry
91

~jest bitowym operatorem dopełniania w pythonie, który zasadniczo oblicza-x - 1

Tak wyglądałby stół

i  ~i  
0  -1
1  -2
2  -3
3  -4 
4  -5 
5  -6

Więc za i = 0to porównać s[0]z s[len(s) - 1], na i = 1, s[1]z s[len(s) - 2].

Jeśli chodzi o twoje inne pytanie, może to być przydatne w przypadku szeregu bitowych hacków .

GWW
źródło
26

Oprócz tego, że jest bitowym operatorem dopełniania, ~może również pomóc w przywróceniu wartości logicznej , chociaż nie jest to typowy booltutaj typ, raczej powinieneś go użyć numpy.bool_.


Wyjaśnia to

import numpy as np
assert ~np.True_ == np.False_

Odwracanie wartości logicznej może być czasem przydatne, np. ~Operator poniżej służy do czyszczenia zestawu danych i zwraca kolumnę bez NaN.

from numpy import NaN
import pandas as pd

matrix = pd.DataFrame([1,2,3,4,NaN], columns=['Number'], dtype='float64')
# Remove NaN in column 'Number'
matrix['Number'][~matrix['Number'].isnull()]
Mikołaj
źródło
numpy.NaNwydaje się być zdefiniowany jako numpy.float. Jeśli spróbuję ~numpy.NaN, python narzeka, że ​​operator jednoargumentowy ~nie jest zdefiniowany dla typu numpy.float.
M.Herzkamp
2
@ M.Herzkamp, ​​zgadza się. NaN, + Inf i -Inf to specjalne przypadki liczb zmiennoprzecinkowych. Odwrócenie bitów liczby zmiennoprzecinkowej dałoby nonsensowny wynik, więc Python nie pozwala na to. Dlatego najpierw musisz wywołać .isnull () lub np.isnan () w tablicy danych, a następnie odwrócić uzyskane wartości boolowskie.
geofflee
7
Zauważ, że to ~Truepowoduje -2, podczas gdy dla numpy booleans ~np.True_powoduje False.
Christian Herenz,
dobra wskazówka! Widziałem, jak użyto go tutaj do sortowania zestawu danych: github.com/yu4u/age-gender-estimation/blob/master/create_db.py
mLstudent33 13.09.19
19

Należy zauważyć, że w przypadku indeksowania tablic array[~i]wynosi reversed_array[i]. Można to postrzegać jako indeksowanie od końca tablicy:

[0, 1, 2, 3, 4, 5, 6, 7, 8]
    ^                 ^
    i                ~i
Le Frite
źródło
2
Dzieje się tak głównie dlatego, że wartość, która pochodzi ~i(tj. Wartość ujemna), działa jako punkt początkowy dla indeksu tablicowego, który Python chętnie przyjmuje, powodując zawijanie indeksu i wybieranie go od tyłu.
wrzask
4

Jedyny raz, kiedy użyłem tego w praktyce, to z numpy/pandas. Na przykład z .isin() metodą dataframe .

W dokumentach pokazują ten podstawowy przykład

>>> df.isin([0, 2])
        num_legs  num_wings
falcon      True       True
dog        False       True

Ale co jeśli zamiast tego chcesz, aby wszystkie wiersze nie były w [0, 2]?

>>> ~df.isin([0, 2])
        num_legs  num_wings
falcon     False       False
dog        True        False
Adam Hughes
źródło
2

Rozwiązałem problem z kodem Leet i natknąłem się na to piękne rozwiązanie autorstwa użytkownika Zitao Wang .

Problem przebiega w ten sposób, że każdy element w danej tablicy znajduje iloczyn wszystkich pozostałych liczb bez korzystania z podziału i na O(n)czas

Standardowe rozwiązanie to:

Pass 1: For all elements compute product of all the elements to the left of it
Pass 2: For all elements compute product of all the elements to the right of it
        and then multiplying them for the final answer 

Jego rozwiązanie używa tylko jednego dla pętli, wykorzystując. Oblicza lewy i prawy produkt w locie za pomocą~

def productExceptSelf(self, nums):
    res = [1]*len(nums)
    lprod = 1
    rprod = 1
    for i in range(len(nums)):
        res[i] *= lprod
        lprod *= nums[i]
        res[~i] *= rprod
        rprod *= nums[~i]
    return res
Stuxen
źródło
-2

To niewielkie użycie jest tyldą ...

def split_train_test_by_id(data, test_ratio, id_column):
    ids = data[id_column]
    in_test_set = ids.apply(lambda id_: test_set_check(id_, test_ratio)) 
    return data.loc[~in_test_set], data.loc[in_test_set]

powyższy kod pochodzi z „Hands On Machine Learning”

używasz tyldy (~ znak) jako alternatywy dla - znaku indeksu znaku

tak jak używasz minus - jest dla indeksu liczb całkowitych

dawny)

array = [1,2,3,4,5,6]
print(array[-1])

jest taki sam jak

print(array[~1])

hyukkyulee
źródło