L'operatore tilde in Python


200

A che serve l'operatore tilde in Python?

Una cosa a cui posso pensare è fare qualcosa su entrambi i lati di una stringa o di un elenco, ad esempio verificare se una stringa è palindromica o meno:

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

Qualche altro buon utilizzo?


11
Si noti che l'operatore di complemento unario ~implementato con il metodo speciale __invert__non è correlato notall'operatore, il che nega logicamente il valore restituito da __bool__(o __nonzero__in 2.x). Non è inoltre correlato -all'operatore di negazione unaria, implementato da __neg__. Ad esempio ~True == -2, che non è Falseo falso e -False == 0che è ancora falso.
Eryk Sun,

@eryksun, anche se quello che hai detto è giusto ( -False==0) È confuso, dal momento che stavi parlando del ~, e ~False == -1che non è falso.
Guilherme de Lazari,

3
@GuilhermedeLazari, il secondo esempio è stato il confronto con la negazione aritmetica ( __neg__). Probabilmente avrei dovuto continuare ad usare True, ad esempio -True == -1, che non è -2 o Falseo falso, il che lo collega più chiaramente al ~Truerisultato e anche che la negazione aritmetica di a boolè diversa dalla sua negazione logica. Non stavo cercando di essere profondo. Stavo solo mettendo in evidenza 3 operazioni e i metodi speciali sottostanti che a volte vengono confusi.
Eryk Sun,

Risposte:


192

È un operatore unario (prendendo un singolo argomento) che viene preso in prestito da C, dove tutti i tipi di dati sono solo modi diversi di interpretare i byte. È l'operazione "invert" o "complementare", in cui tutti i bit dei dati di input sono invertiti.

In Python, per gli interi, i bit della rappresentazione a complemento a due dell'intero sono invertiti (come in b <- b XOR 1ogni singolo bit) e il risultato interpretato nuovamente come intero a due complementi. Quindi per i numeri interi ~xè equivalente a (-x) - 1.

La forma reificata ~dell'operatore è fornita come operator.invert. Per supportare questo operatore nella tua classe, dagli un __invert__(self)metodo.

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

Qualsiasi classe in cui sia significativo avere un "complemento" o "inverso" di un'istanza che è anche un'istanza della stessa classe è un possibile candidato per l'operatore invertito. Tuttavia, il sovraccarico dell'operatore può causare confusione se usato in modo improprio, quindi assicurati che abbia davvero senso farlo prima di fornire un __invert__metodo alla tua classe. (Notare che le stringhe di byte [es: '\xff'] non supportano questo operatore, anche se è significativo invertire tutti i bit di una stringa di byte.)


16
Una buona spiegazione, ma un avvertimento - tutte le dichiarazioni di non responsabilità per il sovraccarico dell'operatore si applicano qui - non è una buona idea, a meno che non si adatti perfettamente al conto.
Eli Bendersky,

Il feedback di Eli è stato incorporato nella risposta nel paragrafo finale.
wberry,

91

~è l' operatore complemento bit per bit in Python che essenzialmente calcola-x - 1

Quindi un tavolo sarebbe simile

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

Quindi, per i = 0fare un confronto s[0]con s[len(s) - 1], per i = 1, s[1]con s[len(s) - 2].

Per quanto riguarda l'altra domanda, questo può essere utile per una serie di hack bit per bit .


26

Oltre ad essere un operatore di complemento bit a bit, ~può anche aiutare a ripristinare un valore booleano , anche se qui non è il booltipo convenzionale , piuttosto dovresti usare numpy.bool_.


Questo è spiegato in,

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

L'inversione del valore logico può essere utile a volte, ad esempio, l' ~operatore di seguito viene utilizzato per ripulire il set di dati e restituire una colonna senza 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()]

numpy.NaNsembra essere definito come numpy.float. Se provo ~numpy.NaN, Python si lamenta che l'operatore unario ~non è definito per il tipo numpy.float.
M.Herzkamp,

2
@ M.Herzkamp, ​​è corretto. NaN, + Inf e -Inf sono casi speciali di numeri in virgola mobile. L'inversione dei bit di un numero in virgola mobile produrrebbe un risultato senza senso, quindi Python non lo consente. Ecco perché devi prima chiamare .isnull () o np.isnan () sul tuo array di dati, quindi invertire i valori booleani risultanti.
Geofflee,

7
Si noti che ciò ~Truerisulta -2, mentre per i valori booleani intorpiditi si ~np.True_ottiene False.
Christian Herenz

Bel consiglio! L'ho visto qui usato per ordinare un set di dati: github.com/yu4u/age-gender-estimation/blob/master/create_db.py
mLstudent33

19

Si dovrebbe notare che nel caso dell'indicizzazione di array, ciò array[~i]equivale a reversed_array[i]. Può essere visto come indicizzazione a partire dalla fine dell'array:

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

2
Soprattutto perché il valore che ne deriva ~i(ovvero il valore negativo) funge da punto di partenza per l'indice di array che Python accetta felicemente facendo sì che l'indice si avvolga e selezioni da dietro.
grido

4

L'unica volta che l'ho mai usato in pratica è con numpy/pandas. Ad esempio, con il.isin() metodo dataframe .

Nei documenti mostrano questo esempio di base

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

E se invece volessi tutte le righe non in [0, 2]?

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

2

Stavo risolvendo questo problema con leetcode e mi sono imbattuto in questa bellissima soluzione da un utente di nome Zitao Wang .

Il problema si presenta in questo modo per ogni elemento nella matrice data trova il prodotto di tutti i numeri rimanenti senza usare divison e in O(n) tempo

La soluzione standard è:

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 

La sua soluzione utilizza solo uno per il loop utilizzando. Calcola il prodotto sinistro e il prodotto giusto al volo usando~

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

Questo è un uso minore è 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]

il codice sopra è tratto da "Hands On Machine Learning"

si utilizza tilde (segno ~) in alternativa a - segno indicatore indice

proprio come si usa meno - è per indice intero

ex)

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

è il samething come

print(array[~1])

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.