Der Tilde-Operator in Python


199

Was ist die Verwendung des Tilde-Operators in Python?

Eine Sache, über die ich nachdenken kann, ist, auf beiden Seiten einer Zeichenfolge oder Liste etwas zu tun, z. B. zu überprüfen, ob eine Zeichenfolge palindrom ist oder nicht:

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

Irgendeine andere gute Verwendung?


11
Beachten Sie, dass der ~durch die spezielle Methode implementierte unäre Komplementoperator __invert__nicht mit dem notOperator zusammenhängt, der den von __bool__(oder __nonzero__in 2.x) zurückgegebenen Wert logisch negiert . Es hat auch nichts mit dem -unären Negationsoperator zu tun, der von implementiert wird __neg__. Zum Beispiel ~True == -2, was nicht Falseoder falsch ist und -False == 0was immer noch falsch ist.
Eryk Sun

@eryksun, obwohl das, was du gesagt hast, richtig ist ( -False==0) Es ist verwirrend, da du über das gesprochen hast ~und ~False == -1was nicht falsch ist.
Guilherme de Lazari

3
@GuilhermedeLazari, das zweite Beispiel war der Vergleich mit der arithmetischen Negation ( __neg__). Wahrscheinlich hätte ich weiter verwenden sollen True, z. B. -True == -1was nicht -2 oder Falseoder falsch ist, was es deutlicher mit dem ~TrueErgebnis verknüpft und auch, dass sich die arithmetische Negation von a boolvon seiner logischen Negation unterscheidet. Ich habe nicht versucht, tief zu sein. Ich habe nur 3 Operationen und die zugrunde liegenden speziellen Methoden hervorgehoben, die manchmal verwirrt werden.
Eryk Sun

Antworten:


192

Es ist ein unärer Operator (mit einem einzigen Argument), der aus C entlehnt ist, wobei alle Datentypen nur unterschiedliche Arten der Interpretation von Bytes sind. Es ist die "Invert" - oder "Komplement" -Operation, bei der alle Bits der Eingabedaten umgekehrt werden.

In Python werden für Ganzzahlen die Bits der Zweierkomplementdarstellung der Ganzzahl umgekehrt (wie b <- b XOR 1für jedes einzelne Bit), und das Ergebnis wird erneut als Zweierkomplement-Ganzzahl interpretiert. Also für ganze Zahlen ~xist äquivalent zu (-x) - 1.

Die einheitliche Form des ~Betreibers wird bereitgestellt als operator.invert. Geben Sie eine __invert__(self)Methode an, um diesen Operator in Ihrer eigenen Klasse zu unterstützen .

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

Jede Klasse, in der es sinnvoll ist, ein "Komplement" oder "Inverses" einer Instanz zu haben, die auch eine Instanz derselben Klasse ist, ist ein möglicher Kandidat für den Invertierungsoperator. Das Überladen von Operatoren kann jedoch bei Missbrauch zu Verwirrung führen. Stellen Sie daher sicher, dass dies wirklich sinnvoll ist, bevor Sie __invert__Ihrer Klasse eine Methode zur Verfügung stellen. (Beachten Sie, dass Byte-Strings [Beispiel: '\xff'] diesen Operator nicht unterstützen, obwohl es sinnvoll ist, alle Bits eines Byte-Strings zu invertieren.)


16
Gute Erklärung, aber ein Wort der Vorsicht - hier gelten alle Sicherheitsausschlüsse für die Überlastung des Bedieners - es ist keine gute Idee, es sei denn, es passt genau zur Rechnung.
Eli Bendersky

Elis Feedback wurde in die Antwort im letzten Absatz aufgenommen.
Wberry

91

~ist der bitweise Komplementoperator in Python, der im Wesentlichen berechnet-x - 1

So würde ein Tisch aussehen

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

Also für i = 0es würde s[0]mit s[len(s) - 1], für i = 1, s[1]mit vergleichen s[len(s) - 2].

Was Ihre andere Frage betrifft, kann dies für eine Reihe von bitweisen Hacks nützlich sein .


26

Sie können nicht nur ein bitweiser Komplementoperator sein , sondern ~auch einen booleschen Wert zurücksetzen , obwohl dies hier nicht der herkömmliche boolTyp ist, sondern Sie ihn verwenden sollten numpy.bool_.


Dies wird erklärt in,

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

Das Umkehren des logischen Werts kann manchmal nützlich sein, z. B. wird der folgende ~Operator verwendet, um Ihr Dataset zu bereinigen und Ihnen eine Spalte ohne NaN zurückzugeben.

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()]

numpy.NaNscheint definiert zu sein als numpy.float. Wenn ich es versuche ~numpy.NaN, beschwert sich Python, dass der unäre Operator ~nicht für den Typ definiert ist numpy.float.
M.Herzkamp

2
@ M.Herzkamp, ​​das stimmt. NaN, + Inf und -Inf sind Sonderfälle von Gleitkommazahlen. Das Invertieren der Bits einer Gleitkommazahl würde zu einem unsinnigen Ergebnis führen, sodass Python dies nicht zulässt. Aus diesem Grund müssen Sie zuerst .isnull () oder np.isnan () in Ihrem Datenarray aufrufen und dann die resultierenden booleschen Werte invertieren.
Geofflee

7
Beachten Sie, dass dies zu ~Trueführt -2, während für numpy Boolesche Werte ~np.True_ergibt False.
Christian Herenz

guter Tipp! Ich habe gesehen, dass es hier verwendet wurde, um einen Datensatz zu sortieren: github.com/yu4u/age-gender-estimation/blob/master/create_db.py
mLstudent33

19

Man sollte im Fall von Array - Indizierung beachten Sie, dass, array[~i]beträgt reversed_array[i]. Es kann als Indizierung ab dem Ende des Arrays angesehen werden:

[0, 1, 2, 3, 4, 5, 6, 7, 8]
    ^                 ^
    i                ~i

2
Dies liegt hauptsächlich daran, dass der Wert, der aus ~i(dh dem negativen Wert) hervorgeht, als Ausgangspunkt für den Array-Index dient, den Python gerne akzeptiert, wodurch der Index umbrochen und von hinten ausgewählt wird.
Schrei

4

Das einzige Mal, dass ich dies jemals in der Praxis verwendet habe, ist mit numpy/pandas. Zum Beispiel mit der .isin() Datenrahmenmethode .

In den Dokumenten zeigen sie dieses grundlegende Beispiel

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

Aber was ist, wenn Sie stattdessen alle Zeilen nicht in [0, 2] haben möchten?

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

2

Ich habe dieses Leetcode-Problem gelöst und bin auf diese schöne Lösung eines Benutzers namens Zitao Wang gestoßen .

Das Problem geht so, dass für jedes Element im angegebenen Array das Produkt aller verbleibenden Zahlen ohne Verwendung von Divison und in der O(n)Zeit gefunden wird

Die Standardlösung lautet:

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 

Seine Lösung verwendet nur eine for-Schleife, indem sie verwendet. Er berechnet das linke und das rechte Produkt im laufenden Betrieb mit~

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

-2

Dies ist eine geringfügige Verwendung ist Tilde ...

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]

Der obige Code stammt aus "Hands On Machine Learning".

Sie verwenden Tilde (~ Zeichen) als Alternative zum Indexzeichen - Zeichen

Genau wie Sie Minus verwenden - steht für Integer Index

Ex)

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

ist das samething als

print(array[~1])

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.