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


200

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

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

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

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


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'] не поддерживают этот оператор, даже если имеет смысл инвертировать все биты строки байтов.)


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].

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


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

-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])

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.