El operador tilde en Python

200

¿Cuál es el uso del operador tilde en Python?

Una cosa en la que puedo pensar es hacer algo en ambos lados de una cadena o lista, como verificar si una cadena es palindrómica o no:

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

¿Algún otro buen uso?

clwen
fuente
11
Tenga en cuenta que el operador de complemento unario ~implementado por el método especial __invert__no está relacionado con el notoperador, que lógicamente niega el valor devuelto por __bool__(o __nonzero__en 2.x). Tampoco está relacionado con el -operador de negación unario, implementado por __neg__. Por ejemplo ~True == -2, que no es Falseo falso, y -False == 0que sigue siendo falso.
Eryk Sun
@eryksun, aunque lo que dijiste es correcto ( -False==0) Es confuso, ya que estabas hablando sobre el ~, y ~False == -1que no es falso.
Guilherme de Lazari
3
@GuilhermedeLazari, el segundo ejemplo fue comparar con la negación aritmética ( __neg__). Probablemente debería haber seguido usando True, por ejemplo -True == -1, que no es -2 o Falsefalso, lo que lo vincula más claramente con el ~Trueresultado y también que la negación aritmética de a booles diferente de su negación lógica. No estaba tratando de ser profundo. Solo estaba destacando 3 operaciones y los métodos especiales subyacentes que a veces se confunden.
Eryk Sun

Respuestas:

192

Es un operador unario (tomando un solo argumento) que se toma prestado de C, donde todos los tipos de datos son simplemente diferentes formas de interpretar los bytes. Es la operación de "inversión" o "complemento", en la que todos los bits de los datos de entrada se invierten.

En Python, para los enteros, los bits de la representación de dos complementos del entero se invierten (como en b <- b XOR 1cada bit individual), y el resultado se interpreta nuevamente como un entero de dos complementos. Entonces, para enteros, ~xes equivalente a (-x) - 1.

La forma reificada del ~operador se proporciona como operator.invert. Para admitir este operador en su propia clase, dele un __invert__(self)método.

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

Cualquier clase en la que sea significativo tener un "complemento" o "inverso" de una instancia que también sea una instancia de la misma clase es un posible candidato para el operador invertido. Sin embargo, la sobrecarga del operador puede generar confusión si se usa incorrectamente, así que asegúrese de que realmente tenga sentido hacerlo antes de proporcionar un __invert__método a su clase. (Tenga en cuenta que las cadenas de bytes [ej .: '\xff'] no son compatibles con este operador, aunque sea significativo invertir todos los bits de una cadena de bytes).

fresa
fuente
16
Buena explicación, pero una advertencia: todas las advertencias de seguridad para la sobrecarga del operador se aplican aquí. No es una buena idea, a menos que se ajuste perfectamente a la factura.
Eli Bendersky
Los comentarios de Eli se han incorporado a la respuesta en el párrafo final.
wberry
91

~es el operador de complemento bit a bit en Python que esencialmente calcula-x - 1

Así se vería una mesa

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

Entonces, para i = 0compararlo s[0]con s[len(s) - 1], para i = 1, s[1]con s[len(s) - 2].

En cuanto a su otra pregunta, esto puede ser útil para una variedad de hacks bit a bit .

GWW
fuente
26

Además de ser un operador de complemento a nivel de bits, ~también puede ayudar a revertir un valor booleano , aunque no es el booltipo convencional aquí, más bien debería usarlo numpy.bool_.


Esto se explica en,

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

Revertir el valor lógico puede ser útil a veces, por ejemplo, el siguiente ~operador se utiliza para limpiar su conjunto de datos y devolverle una columna sin 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()]
Nicholas
fuente
numpy.NaNparece definirse como numpy.float. Si lo intento ~numpy.NaN, Python se queja, que el operador unario ~no está definido para el tipo numpy.float.
M.Herzkamp
2
@ M.Herzkamp, ​​eso es correcto. NaN, + Inf e -Inf son casos especiales de números de punto flotante. Invertir los bits de un número de coma flotante produciría un resultado sin sentido, por lo que Python no lo permite. Es por eso que primero debe llamar a .isnull () o np.isnan () en su matriz de datos, y luego invertir los valores booleanos resultantes.
geofflee
77
Tenga en cuenta que eso ~Trueresulta -2, mientras que para booleanos numpy ~np.True_resulta False.
Christian Herenz
¡Buen consejo! Vi que se usaba aquí para clasificar un conjunto de datos: github.com/yu4u/age-gender-estimation/blob/master/create_db.py
mLstudent33
19

Cabe señalar que en el caso de la indexación de matrices, array[~i]equivale a reversed_array[i]. Se puede ver como indexación a partir del final de la matriz:

[0, 1, 2, 3, 4, 5, 6, 7, 8]
    ^                 ^
    i                ~i
Le Frite
fuente
2
Esto se debe principalmente a que el valor que sale ~i(es decir, el valor negativo) actúa como un punto de partida para el índice de matriz que Python acepta felizmente, lo que hace que el índice se enrolle y elija desde la parte posterior.
chillido
4

La única vez que he usado esto en la práctica es con numpy/pandas. Por ejemplo, con el .isin() método del marco de datos .

En los documentos muestran este ejemplo básico

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

Pero, ¿y si en lugar de eso quisieras que todas las filas no estuvieran en [0, 2]?

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

Estaba resolviendo este problema de leetcode y encontré esta hermosa solución de un usuario llamado Zitao Wang .

El problema es el siguiente: para cada elemento de la matriz dada, encuentre el producto de todos los números restantes sin utilizar la división y a O(n)tiempo

La solución estándar es:

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 

Su solución usa solo uno para bucle haciendo uso de. Calcula el producto izquierdo y el producto derecho sobre la marcha 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
Stuxen
fuente
-2

Este es un uso menor es 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]

el código anterior es de "Hands On Machine Learning"

utiliza tilde (signo ~) como alternativa a - marcador de índice de signo

al igual que usa menos - es para el índice entero

ex)

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

es lo mismo que

print(array[~1])

hyukkyulee
fuente