L'opérateur tilde en Python

200

Quelle est l'utilisation de l'opérateur tilde en Python?

Une chose à laquelle je peux penser est de faire quelque chose des deux côtés d'une chaîne ou d'une liste, comme vérifier si une chaîne est palindromique ou non:

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

Tout autre bon usage?

clwen
la source
11
Notez que l'opérateur de complément unaire ~implémenté par la méthode spéciale __invert__n'est pas lié à l' notopérateur, ce qui annule logiquement la valeur retournée par __bool__(ou __nonzero__dans 2.x). Il n'est pas non plus lié à l' -opérateur de négation unaire, implémenté par __neg__. Par exemple ~True == -2, ce qui n'est pas Falseou faux, et -False == 0ce qui est toujours faux.
Eryk Sun
@eryksun, bien que ce que vous avez dit soit juste ( -False==0) C'est déroutant, puisque vous parliez de la ~, et ~False == -1ce n'est pas faux.
Guilherme de Lazari
3
@GuilhermedeLazari, le deuxième exemple était de comparer avec la négation arithmétique ( __neg__). J'aurais probablement dû continuer à utiliser True, par exemple -True == -1, ce qui n'est ni -2 ni Falsefaux, ce qui le relie plus clairement au ~Truerésultat et aussi que la négation arithmétique de a boolest différente de sa négation logique. Je n'essayais pas d'être profond. Je mettais juste en évidence 3 opérations et les méthodes spéciales sous-jacentes qui se confondent parfois.
Eryk Sun

Réponses:

192

C'est un opérateur unaire (prenant un seul argument) qui est emprunté à C, où tous les types de données ne sont que des façons différentes d'interpréter les octets. C'est l'opération "inverser" ou "complément", dans laquelle tous les bits des données d'entrée sont inversés.

En Python, pour les entiers, les bits de la représentation à deux compléments de l'entier sont inversés (comme b <- b XOR 1pour chaque bit individuel), et le résultat est à nouveau interprété comme un entier à deux compléments. Donc, pour les entiers, ~xest équivalent à (-x) - 1.

La forme réifiée de l' ~opérateur est fournie en tant que operator.invert. Pour prendre en charge cet opérateur dans votre propre classe, donnez-lui une __invert__(self)méthode.

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

Toute classe dans laquelle il est significatif d'avoir un "complément" ou "inverse" d'une instance qui est également une instance de la même classe est un candidat possible pour l'opérateur inversé. Cependant, la surcharge de l'opérateur peut entraîner de la confusion si elle est mal utilisée, alors assurez-vous qu'il est vraiment logique de le faire avant de fournir une __invert__méthode à votre classe. (Notez que les chaînes d'octets [ex: '\xff'] ne prennent pas en charge cet opérateur, même s'il est utile d'inverser tous les bits d'une chaîne d'octets.)

wberry
la source
16
Bonne explication, mais une mise en garde - tous les avertissements de sécurité pour la surcharge de l'opérateur s'appliquent ici - ce n'est pas une bonne idée, à moins que cela ne corresponde parfaitement à la facture.
Eli Bendersky
Les commentaires d'Eli ont été intégrés à la réponse dans le dernier paragraphe.
wberry
91

~est l' opérateur de complément au niveau du bit en python qui calcule essentiellement-x - 1

Donc, une table ressemblerait

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

Donc, i = 0cela se comparerait s[0]avec s[len(s) - 1], car i = 1, s[1]avec s[len(s) - 2].

Quant à votre autre question, cela peut être utile pour une gamme de hacks au niveau du bit .

GWW
la source
26

En plus d'être un opérateur de complément au niveau du bit, il ~peut également aider à rétablir une valeur booléenne , bien que ce ne soit pas le booltype conventionnel ici, vous devez plutôt l'utiliser numpy.bool_.


Ceci est expliqué dans,

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

Inverser la valeur logique peut parfois être utile, par exemple, l' ~opérateur ci-dessous est utilisé pour nettoyer votre jeu de données et vous renvoyer une colonne sans 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()]
Nicolas
la source
numpy.NaNsemble être défini comme numpy.float. Si j'essaye ~numpy.NaN, python se plaint, que l'opérateur unaire ~n'est pas défini pour le type numpy.float.
M.Herzkamp
2
@ M.Herzkamp, ​​c'est exact. NaN, + Inf et -Inf sont des cas spéciaux de nombres à virgule flottante. Inverser les bits d'un nombre à virgule flottante produirait un résultat insensé, donc Python ne le permet pas. C'est pourquoi vous devez d'abord appeler .isnull () ou np.isnan () sur votre tableau de données, puis inverser les valeurs booléennes résultantes.
geofflee
7
Notez que cela se ~Truetraduit par -2, tandis que pour les booléens numpy, cela se ~np.True_traduit par False.
Christian Herenz
bon conseil! Je l'ai vu utilisé ici pour trier un ensemble de données: github.com/yu4u/age-gender-estimation/blob/master/create_db.py
mLstudent33
19

Il convient de noter que dans le cas de l'indexation de tableaux, array[~i]équivaut à reversed_array[i]. Il peut être vu comme une indexation à partir de la fin du tableau:

[0, 1, 2, 3, 4, 5, 6, 7, 8]
    ^                 ^
    i                ~i
Le Frite
la source
2
C'est principalement parce que la valeur qui en résulte ~i(c'est-à-dire une valeur négative) agit comme un point de départ pour l'index du tableau que python accepte avec plaisir, ce qui fait que l'index s'enroule et se ramasse à l'arrière.
cri
4

La seule fois où je l'ai utilisé en pratique, c'est avec numpy/pandas. Par exemple, avec la .isin() méthode dataframe .

Dans les documents, ils montrent cet exemple de base

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

Mais que se passe-t-il si à la place vous vouliez que toutes les lignes ne soient pas dans [0, 2]?

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

Je résolvais ce problème de leetcode et je suis tombé sur cette belle solution par un utilisateur nommé Zitao Wang .

Le problème va comme ceci pour chaque élément dans le tableau donné trouver le produit de tous les nombres restants sans faire usage de la division et dans le O(n)temps

La solution standard est:

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 

Sa solution n'en utilise qu'une pour la boucle en faisant usage de. Il calcule le produit gauche et le produit droit à la volée en utilisant~

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
la source
-2

Ceci est une utilisation mineure est 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]

le code ci-dessus provient de "Hands On Machine Learning"

vous utilisez le tilde (signe ~) comme alternative au marqueur d'index de signe

tout comme vous utilisez moins - est pour l'index entier

ex)

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

est la même chose que

print(array[~1])

hyukkyulee
la source