Оператор тильды в Python

200

Какая польза от оператора тильды в Python?

Одна вещь, о которой я могу подумать, это сделать что-то с обеих сторон строки или списка, например, проверить, является ли строка палиндромной или нет:

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

Любое другое хорошее использование?

clwen
источник
11
Обратите внимание, что унарный оператор дополнения, ~реализованный специальным методом, __invert__не связан с notоператором, который логически сводит на нет значение, возвращаемое __bool__(или __nonzero__в 2.x). Это также не связано с -унарным оператором отрицания, реализованным __neg__. Например ~True == -2, который не является Falseили ложным, и -False == 0, который все еще ложным.
Eryk Sun
@eryksun, хотя то, что вы сказали, правильно ( -False==0) Это сбивает с толку, так как вы говорили о ~, и ~False == -1это не Ложь.
Гильерме де Лазари
3
@GuilhermedeLazari, второй пример - сравнение с арифметическим отрицанием ( __neg__). Вероятно, мне следовало бы продолжать использовать True, например -True == -1, значение «-2» или « Falseложь», которое более четко связывает его с ~Trueрезультатом, а также то, что арифметическое отрицание a boolотличается от его логического отрицания. Я не пытался быть глубоким. Я просто выделил 3 операции и основные специальные методы, которые иногда путают.
Eryk Sun

Ответы:

192

Это унарный оператор (с одним аргументом), заимствованный из C, где все типы данных - это просто разные способы интерпретации байтов. Это операция «инвертировать» или «дополнить», в которой все биты входных данных инвертируются.

В Python для целых чисел биты представления целого числа, дополняющего двойку, инвертируются (как b <- b XOR 1для каждого отдельного бита), и результат снова интерпретируется как целое число, дополняющее двойку. Так что для целых чисел, ~xэквивалентно (-x) - 1.

Уточненная форма ~оператора предоставляется в виде operator.invert. Чтобы поддержать этот оператор в вашем собственном классе, дайте ему __invert__(self)метод.

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

Любой класс, в котором имеет смысл иметь «дополнение» или «инверсию» экземпляра, который также является экземпляром того же класса, является возможным кандидатом на оператор инвертирования. Однако перегрузка операторов может привести к путанице при неправильном использовании, поэтому убедитесь, что это действительно имеет смысл, прежде чем предоставлять __invert__метод вашему классу. (Обратите внимание, что строки байтов [ex: '\xff'] не поддерживают этот оператор, даже если имеет смысл инвертировать все биты строки байтов.)

wberry
источник
16
Хорошее объяснение, но слово предостережения - здесь применяются все заявления об отказе от ответственности за перегрузку оператора - это не очень хорошая идея, если только она не отвечает всем требованиям.
Эли Бендерский
Отзывы Эли были включены в ответ в последнем абзаце.
wberry
91

~является побитовым оператором дополнения в Python, который по существу вычисляет-x - 1

Так будет выглядеть таблица

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

Так что для i = 0этого будет по сравнению s[0]с s[len(s) - 1], для i = 1, s[1]с s[len(s) - 2].

Что касается вашего другого вопроса, это может быть полезно для целого ряда побитовых хаков .

GWW
источник
26

Помимо того, что он является побитовым оператором дополнения, он ~также может помочь вернуть логическое значение, хотя здесь это не обычный boolтип, скорее, вы должны использовать numpy.bool_.


Это объясняется в

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

Изменение логического значения иногда может быть полезно, например, ~оператор ниже используется для очистки вашего набора данных и возврата вам столбца без 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.NaNкажется, определяется как numpy.float. Если я пытаюсь ~numpy.NaN, python жалуется, что унарный оператор ~не определен для типа numpy.float.
М.Херцкамп,
2
@ М.Херцкамп, это правильно. NaN, + Inf и -Inf являются частными случаями чисел с плавающей точкой. Инвертирование битов числа с плавающей запятой приведет к бессмысленному результату, поэтому Python не позволяет этого. Вот почему вам нужно сначала вызвать .isnull () или np.isnan () в вашем массиве данных, а затем инвертировать полученные логические значения.
Джеффли
7
Обратите внимание, что это ~Trueприводит к -2, в то время как для NumPy Booleans ~np.True_приводит к False.
Кристиан Херенц
хороший совет! Я видел его здесь для сортировки набора данных: github.com/yu4u/age-gender-estima/blob/master/create_db.py
mLstudent33
19

Следует отметить, что в случае индексации массивов, array[~i]составляет reversed_array[i]. Это можно рассматривать как индексирование, начиная с конца массива:

[0, 1, 2, 3, 4, 5, 6, 7, 8]
    ^                 ^
    i                ~i
Ле Фрайт
источник
2
Это происходит главным образом потому, что значение, полученное из ~i(т. Е. Отрицательное значение), действует как отправная точка для индекса массива, который Python с радостью принимает, заставляя индекс обернуться и выбрать его сзади.
визг
4

Единственный раз, когда я использовал это на практике, это с numpy/pandas. Например, с помощью .isin() метода dataframe .

В документах они показывают этот основной пример

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

Но что если вместо этого вы хотите, чтобы все строки отсутствовали в [0, 2]?

>>> ~df.isin([0, 2])
        num_legs  num_wings
falcon     False       False
dog        True        False
Адам Хьюз
источник
2

Я решал эту проблему с помощью кода leetcode и наткнулся на это прекрасное решение от пользователя по имени Zitao Wang .

Проблема идет так: для каждого элемента в данном массиве найти произведение всех оставшихся чисел без использования деления и O(n)времени

Стандартное решение:

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 

Его решение использует только один цикл for, используя. Он вычисляет левый продукт и правый продукт на лету, используя~

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
источник
-2

Это незначительное использование это тильда ...

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]

приведенный выше код взят из "Практического обучения машин"

вы используете тильду (~ знак) в качестве альтернативы - подписать маркер индекса

так же, как вы используете минус - для целого индекса

ех)

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

это то же самое, что

print(array[~1])

hyukkyulee
источник