O operador til em Python


199

Qual é o uso do operador til no Python?

Uma coisa que posso pensar é fazer algo nos dois lados de uma string ou lista, como verificar se uma string é palindrômica ou não:

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

Algum outro bom uso?


11
Observe que o operador de complemento unário ~implementado pelo método especial __invert__não está relacionado ao notoperador, o que nega logicamente o valor retornado por __bool__(ou __nonzero__em 2.x). Também não está relacionado ao -operador de negação unário, implementado por __neg__. Por exemplo ~True == -2, o que não é Falseou falso, e o -False == 0que ainda é falso.
Eryk Sun

@eryksun, embora o que você disse é certo ( -False==0) Sua confuso, já que você estava falando sobre o ~, e ~False == -1que não é falso.
Guilherme de Lazari

3
@GuilhermedeLazari, o segundo exemplo foi comparar com negação aritmética ( __neg__). Provavelmente eu deveria ter continuado usando True, por exemplo -True == -1, que não é -2 ou Falseou falso, o que o vincula mais claramente ao ~Trueresultado e também que a negação aritmética de a boolé diferente de sua negação lógica. Eu não estava tentando ser profundo. Eu estava apenas destacando três operações e os métodos especiais subjacentes que às vezes ficam confusos.
Eryk Sun

Respostas:


192

É um operador unário (usando um único argumento) que é emprestado de C, onde todos os tipos de dados são apenas maneiras diferentes de interpretar bytes. É a operação "inverter" ou "complementar", na qual todos os bits dos dados de entrada são revertidos.

Em Python, para números inteiros, os bits da representação de dois complementos do número inteiro são invertidos (como em b <- b XOR 1cada bit individual), e o resultado interpretado novamente como um número inteiro de dois complementos. Portanto, para números inteiros, ~xé equivalente a (-x) - 1.

O formulário reificado do ~operador é fornecido como operator.invert. Para oferecer suporte a esse operador em sua própria classe, forneça um __invert__(self)método.

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

Qualquer classe na qual seja significativo ter um "complemento" ou "inverso" de uma instância que também seja uma instância da mesma classe é um possível candidato ao operador invertido. No entanto, a sobrecarga do operador pode causar confusão se for mal utilizada; portanto, faça sentido antes de fornecer um __invert__método para sua classe. (Observe que as cadeias de bytes [ex: '\xff'] não suportam esse operador, mesmo que seja significativo inverter todos os bits de uma cadeia de bytes.)


16
Boa explicação, mas uma palavra de cautela - todas as isenções de segurança relacionadas à sobrecarga do operador se aplicam aqui - não é uma boa idéia, a menos que atenda perfeitamente à conta.
Eli Bendersky

O feedback de Eli foi incorporado à resposta no parágrafo final.
Wr #

91

~é o operador de complemento bit a bit em python que essencialmente calcula-x - 1

Então, uma tabela se pareceria

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

Então, para i = 0comparar s[0]com s[len(s) - 1], para i = 1,s[1] com s[len(s) - 2].

Quanto à sua outra pergunta, isso pode ser útil para uma variedade de hacks bit a bit .


26

Além de ser um operador de complemento bit a bit, ~também pode ajudar a reverter um valor booleano , embora não seja o booltipo convencional aqui, você deve usar numpy.bool_.


Isso é explicado em,

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

A reversão do valor lógico pode ser útil algumas vezes, por exemplo, o ~operador abaixo é usado para limpar seu conjunto de dados e retornar uma coluna sem 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.NaNparece ser definido como numpy.float. Se eu tentar ~numpy.NaN, o python reclama, que o operador unário ~não está definido para o tipo numpy.float.
M.Herzkamp

2
@ M.Herzkamp, ​​está correto. NaN, + Inf e -Inf são casos especiais de números de ponto flutuante. A inversão dos bits de um número de ponto flutuante produziria um resultado sem sentido, portanto, o Python não permite. É por isso que você precisa chamar .isnull () ou np.isnan () em sua matriz de dados primeiro e depois inverter os valores booleanos resultantes.
geofflee

7
Observe que isso ~Trueresulta em -2, enquanto que para numerosos booleanos ~np.True_resulta em False.
Christian Herenz

boa dica! Eu vi isso aqui utilizada para classificar através de um conjunto de dados: github.com/yu4u/age-gender-estimation/blob/master/create_db.py
mLstudent33

19

Deve-se notar que, no caso de indexação de array, isso array[~i]equivale a reversed_array[i]. Pode ser visto como indexação a partir do final da matriz:

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

2
Isso ocorre principalmente porque o valor que sai ~i(ou seja, valor negativo) atua como um ponto de partida para o índice de array que o python aceita alegremente, fazendo com que o índice seja contornado e escolhido pelas costas.
grito

4

A única vez que usei isso na prática é com numpy/pandas. Por exemplo, com o .isin() método dataframe .

Nos documentos, eles mostram este exemplo básico

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

Mas e se você não quisesse todas as linhas em [0, 2]?

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

2

Eu estava resolvendo esse problema do leetcode e me deparei com essa bela solução de um usuário chamado Zitao Wang .

O problema é o seguinte: para cada elemento na matriz fornecida, encontre o produto de todos os números restantes sem fazer uso da divisão e no O(n)tempo

A solução padrão é:

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 

Sua solução usa apenas um loop para fazer uso de. Ele calcula o produto esquerdo e o produto certo em tempo real 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

Este é um uso menor é til ...

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]

o código acima é de "Hands On Machine Learning"

você usa til (~ sign) como alternativa para - assinar o marcador de índice

assim como você usa menos - é para índice inteiro

ex)

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

é a mesma coisa que

print(array[~1])

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.