267

What's the usage of the tilde operator in Python?

One thing I can think about is do something in both sides of a string or list, such as check if a string is palindromic or not:

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

Any other good usage?

clwen
  • 18,566
  • 30
  • 73
  • 92
  • 16
    Note that unary complement operator `~` implemented by the special method `__invert__` is unrelated to the `not` operator, which logically negates the value returned by `__bool__` (or `__nonzero__` in 2.x). It's also unrelated to the `-` unary negation operator, implemented by `__neg__`. For example `~True == -2`, which isn't `False` or false, and `-False == 0`, which is still false. – Eryk Sun Nov 29 '11 at 04:54
  • @eryksun, though what you said is right (`-False==0`) Its confusing, since you were talking about the `~`, and `~False == -1` which is not False. – Guilherme de Lazari Dec 06 '18 at 14:07
  • 3
    @GuilhermedeLazari, the second example was to compare with arithmetic negation (`__neg__`). Probably I should have continued using `True`, e.g. `-True == -1`, which isn't -2 or `False`or false, which more clearly links it back to the `~True` result and also that the arithmetic negation of a `bool` is different from its logical negation. I wasn't trying to be deep. I was just highlighting 3 operations and the underlying special methods that sometimes get confused. – Eryk Sun Dec 06 '18 at 15:39
  • See also: https://www.tutorialspoint.com/python/python_basic_operators.htm --> "Python Bitwise Operators" section. – Gabriel Staples Aug 28 '21 at 00:27

8 Answers8

245

It is a unary operator (taking a single argument) that is borrowed from C, where all data types are just different ways of interpreting bytes. It is the "invert" or "complement" operation, in which all the bits of the input data are reversed.

In Python, for integers, the bits of the twos-complement representation of the integer are reversed (as in b <- b XOR 1 for each individual bit), and the result interpreted again as a twos-complement integer. So for integers, ~x is equivalent to (-x) - 1.

The reified form of the ~ operator is provided as operator.invert. To support this operator in your own class, give it an __invert__(self) method.

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

Any class in which it is meaningful to have a "complement" or "inverse" of an instance that is also an instance of the same class is a possible candidate for the invert operator. However, operator overloading can lead to confusion if misused, so be sure that it really makes sense to do so before supplying an __invert__ method to your class. (Note that byte-strings [ex: '\xff'] do not support this operator, even though it is meaningful to invert all the bits of a byte-string.)

wberry
  • 17,347
  • 8
  • 51
  • 82
128

~ is the bitwise complement operator in python which essentially calculates -x - 1

So a table would look like

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

So for i = 0 it would compare s[0] with s[len(s) - 1], for i = 1, s[1] with s[len(s) - 2].

As for your other question, this can be useful for a range of bitwise hacks.

Gabriel Staples
  • 22,024
  • 5
  • 133
  • 166
GWW
  • 41,431
  • 11
  • 106
  • 104
36

Besides being a bitwise complement operator, ~ can also help revert a boolean value, though it is not the conventional bool type here, rather you should use numpy.bool_.


This is explained in,

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

Reversing logical value can be useful sometimes, e.g., below ~ operator is used to cleanse your dataset and return you a column without 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
  • 2,380
  • 2
  • 29
  • 54
  • `numpy.NaN` seems to be defined as `numpy.float`. If I try `~numpy.NaN`, python complains, that the unary operator `~` is not defined for type `numpy.float`. – M.Herzkamp Jun 14 '17 at 12:24
  • 2
    @M.Herzkamp, that's correct. NaN, +Inf, and -Inf are special cases of floating-point numbers. Inverting the bits of a floating point number would produce a nonsensical result, so Python does not allow it. That's why you need to call .isnull() or np.isnan() on your data array first, and then invert the resulting boolean values. – geofflee Nov 07 '17 at 05:35
  • 12
    Note, that `~True` results in `-2`, while for numpy booleans `~np.True_` results in `False`. – Christian Herenz Dec 14 '17 at 13:31
  • good tip! I saw it used here to sort through a dataset: https://github.com/yu4u/age-gender-estimation/blob/master/create_db.py – mLstudent33 Sep 13 '19 at 18:20
33

One should note that in the case of array indexing, array[~i] amounts to reversed_array[i]. It can be seen as indexing starting from the end of the array:

[0, 1, 2, 3, 4, 5, 6, 7, 8]
    ^                 ^
    i                ~i
Le Frite
  • 547
  • 6
  • 14
  • 3
    It's mostly because the value that comes out of `~i` (i.e. negative value) acts as a starting point for the array index which python happily accepts causing the index to wrap around and picking from the back. – shriek Jun 08 '19 at 19:25
12

The only time I've ever used this in practice is with numpy/pandas. For example, with the .isin() dataframe method.

In the docs they show this basic example

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

But what if instead you wanted all the rows not in [0, 2]?

>>> ~df.isin([0, 2])
        num_legs  num_wings
falcon     False       False
dog        True        False
Adam Hughes
  • 12,232
  • 8
  • 65
  • 106
4

I was solving this leetcode problem and I came across this beautiful solution by a user named Zitao Wang.

The problem goes like this for each element in the given array find the product of all the remaining numbers without making use of divison and in O(n) time

The standard solution is:

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 

His solution uses only one for loop by making use of. He computes the left product and right product on the fly using ~

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
  • 686
  • 7
  • 18
0

it's called Binary One’s Complement (~)

It returns the one’s complement of a number’s binary. It flips the bits. Binary for 2 is 00000010. Its one’s complement is 11111101.

This is binary for -3. So, this results in -3. Similarly, ~1 results in -2.

~-3

Output : 2

Again, one’s complement of -3 is 2.

Oubaid Gharbi
  • 31
  • 1
  • 6
  • This answer is mixing one's and two's compliments. For example, in 4-bits `1101` is -3 in *two's* compliment, but is -2 in one's compliment. Aside, the conversion examples are all over the place and do not maintain a logical thread throughout. – S3DEV Apr 13 '22 at 09:19
-3

This is minor usage is 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]

the code above is from "Hands On Machine Learning"

you use tilde (~ sign) as alternative to - sign index marker

just like you use minus - is for integer index

ex)

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

is the samething as

print(array[~1])

hyukkyulee
  • 664
  • 7
  • 15