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

#python

Вопрос:

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

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

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

Есть еще какое-нибудь полезное применение?

Комментарии:

1. Обратите внимание, что оператор унарного дополнения ~ , реализованный специальным методом __invert__ , не связан с not оператором, что логически отрицает значение, возвращаемое __bool__ (или __nonzero__ в 2.x). Это также не связано с оператором - унарного отрицания, реализованным __neg__ . Например ~True == -2 , что не так False или ложно, и -False == 0 , что все еще ложно.

2. @eryksun, хотя то, что ты сказал, правильно ( -False==0 ) Это сбивает с толку, так как вы говорили о том ~ , и ~False == -1 это не ложь.

3. @Гильермеделазари, вторым примером было сравнение с арифметическим отрицанием ( __neg__ ). Вероятно , мне следовало бы продолжить использовать True , например -True == -1 , значение, которое не равно -2 или False или ложно, что более четко связывает его с ~True результатом, а также с тем, что арифметическое отрицание a bool отличается от его логического отрицания. Я не пытался быть глубоким. Я просто выделил 3 операции и лежащие в их основе специальные методы, которые иногда путаются.

4. См. также: tutorialspoint.com/python/python_basic_operators.htm — > Раздел «Побитовые операторы Python».

Ответ №1:

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

В 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__ метод вашему классу. (Обратите внимание, что байтовые строки [например: 'xff' ] не поддерживают этот оператор, хотя имеет смысл инвертировать все биты байтовой строки.)

Ответ №2:

~ является побитовым оператором дополнения в 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] .

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

Ответ №3:

Помимо того, что он является побитовым оператором дополнения, ~ он также может помочь вернуть логическое значение, хотя здесь это не обычный 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()]
 

Комментарии:

1. numpy.NaN кажется, это определяется как numpy.float . Если я попытаюсь ~numpy.NaN , python пожалуется, что унарный оператор ~ не определен для типа numpy.float .

2. @M. Херцкамп, это верно. NaN, Inf и-Inf являются частными случаями чисел с плавающей запятой. Инвертирование битов числа с плавающей запятой привело бы к бессмысленному результату, поэтому Python этого не позволяет. Вот почему вам нужно сначала вызвать .isnull() или np.isnan() в вашем массиве данных, а затем инвертировать полученные логические значения.

3. Обратите внимание, что ~True это приводит к -2 , в то время как для логических значений numpy ~np.True_ приводит к False .

4. хороший совет! Я видел, как он использовался здесь для сортировки набора данных: github.com/yu4u/age-gender-estimation/blob/master/create_db.py

Ответ №4:

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

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

Комментарии:

1. Это в основном потому, что значение, которое выходит ~i (т. Е. отрицательное значение), действует как отправная точка для индекса массива, который python с радостью принимает, заставляя индекс оборачиваться и выбирать с обратной стороны.

Ответ №5:

Единственный раз, когда я когда-либо использовал это на практике, — это с 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
 

Ответ №6:

Я решал эту проблему с кодом leet и наткнулся на это прекрасное решение пользователя по имени Цитао Ван.

Проблема заключается в следующем: для каждого элемента в данном массиве найдите произведение всех оставшихся чисел без использования деления и во 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
 

Ответ №7:

Это незначительное использование-тильда…

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