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


199

Яке використання оператора tilde в 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яке все ще є помилковим.
Ерік Нд

@eryksun, хоча те, що ви сказали, є правильним ( -False==0) Його заплутано, оскільки ви говорили про те ~, і ~False == -1що не є помилковим.
Гільгерме де Лазарі

3
@GuilhermedeLazari, другим прикладом було порівняння з арифметичним запереченням ( __neg__). Можливо, я мав би продовжувати використовувати True, наприклад -True == -1, який не є -2 або Falseпомилковим, що більш чітко пов'язує його з ~Trueрезультатом, а також те, що арифметичне заперечення a boolвідрізняється від його логічного заперечення. Я не намагався бути глибоким. Я лише виділяв 3 операції та основні спеціальні методи, які іноді плутаються.
Ерік Нд

Відповіді:


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


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
@ M.Herzkamp, ​​це правильно. NaN, + Inf та -Inf - це спеціальні випадки чисел з плаваючою комою. Інвертування бітів числа з плаваючою комою призведе до безглуздого результату, тому Python цього не дозволяє. Ось чому спочатку потрібно викликати .isnull () або np.isnan () у масиві даних, а потім інвертувати отримані булі значення.
геофле

7
Зверніть увагу, що це ~Trueпризводить до -2, в той час як для nummy booleans ~np.True_призводить до False.
Крістіан Геренц

хороша порада! Я бачив, як тут використовується для сортування набору даних: github.com/yu4u/age-gender-estimation/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() методом фрейму даних .

У документах вони показують цей основний приклад

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

Я вирішував цю проблему з кодом, і я натрапив на це прекрасне рішення від користувача на ім’я 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 

Його рішення використовує лише один для циклу, використовуючи. Він обчислює лівий продукт і правий продукт на льоту, використовуючи~

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]

код вище від "Hands On Machine Learning"

ви використовуєте tilde (~ знак) як альтернативу маркеру індексу знаків

так само, як ви використовуєте мінус - це для цілого індексу

колишній)

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

це те саме, що

print(array[~1])

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.