Я наткнулся на код со строкой, похожей на
x[x<2]=0
Играя с вариациями, я до сих пор не могу понять, что делает этот синтаксис.
Примеры:
>>> x = [1,2,3,4,5]
>>> x[x<2]
1
>>> x[x<3]
1
>>> x[x>2]
2
>>> x[x<2]=0
>>> x
[0, 2, 3, 4, 5]
Я наткнулся на код со строкой, похожей на
x[x<2]=0
Играя с вариациями, я до сих пор не могу понять, что делает этот синтаксис.
Примеры:
>>> x = [1,2,3,4,5]
>>> x[x<2]
1
>>> x[x<3]
1
>>> x[x>2]
2
>>> x[x<2]=0
>>> x
[0, 2, 3, 4, 5]
TypeError: unorderable types: list() < int()
.
Ответы:
Это имеет смысл только с массивами NumPy . Поведение со списками бесполезно и специфично для Python 2 (не Python 3). Вы можете дважды проверить, действительно ли исходный объект был массивом NumPy (см. Ниже), а не списком.
Но в вашем коде x - это простой список.
поскольку
x < 2
ложно, т.е. 0, поэтому
x[x<2]
является x[0]
x[0]
меняется.
И наоборот, x[x>2]
есть x[True]
илиx[1]
Итак, x[1]
меняется.
Почему это происходит?
Правила для сравнения:
Когда вы заказываете две строки или два числовых типа, упорядочение выполняется ожидаемым образом (лексикографический порядок для строки, числовой порядок для целых чисел).
Когда вы заказываете числовой и нечисловой типы, числовой тип идет первым.
Когда вы заказываете два несовместимых типа, где ни один из них не является числовым, они упорядочиваются в алфавитном порядке их имен типов:
Итак, имеем следующий порядок
числовой <список <строка <кортеж
См. Принятый ответ на вопрос, как Python сравнивает строку и int? .
Если x является массивом NumPy , то синтаксис имеет больше смысла из-за логической индексации массива . В этом случае x < 2
это вообще не логическое значение; это массив логических значений, представляющих, был ли каждый элемент x
меньше 2, x[x < 2] = 0
затем выбираются элементы, x
которые были меньше 2, и устанавливаются эти ячейки в 0. См. Индексирование .
>>> x = np.array([1., -1., -2., 3])
>>> x < 0
array([False, True, True, False], dtype=bool)
>>> x[x < 0] += 20 # All elements < 0 get increased by 20
>>> x
array([ 1., 19., 18., 3.]) # Only elements < 0 are affected
import
numpy.
[0 if i < 2 else i for i in x]
.) Или это поощряемый стиль в Numpy?
x[x<2]
вернет массив numpy, тогда как [0 if i<2 else i for i in x]
возвращает список. Это связано с тем, что x[x<2]
это операция индексации (называемая в numpy / scipy / pandas операцией нарезки из-за способности маскировать данные), тогда как понимание списка - это новое определение объекта. См. Индексацию NumPy
>>> x = [1,2,3,4,5]
>>> x<2
False
>>> x[False]
1
>>> x[True]
2
Bool просто преобразуется в целое число. Индекс равен 0 или 1.
x
и 2
они « упорядочены последовательно, но произвольно » и что порядок может меняться в разных реализациях Python.
x<2 == false
?
bool
не преобразуется в целое число, a bool
в Python - это целое число
bool
является подклассом из int
.
Оригинальный код в ваш вопрос работает только в Python 2. Если x
это list
в Python 2, сравнение x < y
является , False
если y
это int
Эгер. Это потому, что нет смысла сравнивать список с целым числом. Однако в Python 2, если операнды не сопоставимы, в CPython сравнение основывается на алфавитном порядке имен типов ; кроме того, все числа идут первыми при сравнении смешанного типа . Это даже не прописано в документации CPython 2, и разные реализации Python 2 могут давать разные результаты. То есть имеет [1, 2, 3, 4, 5] < 2
значение , False
потому что 2
это число и , таким образом , «меньше» , чем list
в CPython. Это смешанное сравнение было в конечном итогеэта функция была сочтена слишком непонятной и была удалена в Python 3.0.
Теперь результат <
: a bool
; и bool
это подкласс изint
:
>>> isinstance(False, int)
True
>>> isinstance(True, int)
True
>>> False == 0
True
>>> True == 1
True
>>> False + 5
5
>>> True + 5
6
Итак, в основном вы берете элемент 0 или 1 в зависимости от того, истинно ли сравнение или ложно.
Если вы попробуете приведенный выше код в Python 3, вы получите TypeError: unorderable types: list() < int()
из- за изменения в Python 3.0 :
Сравнение заказов
Python 3.0 упростил правила упорядочивания сравнений:
Операторы сравнения упорядоченности (
<
,<=
,>=
,>
) поднятьTypeError
исключение , если операнды не имеют содержательный естественный порядок. Таким образом, выражения вроде1 < ''
,0 > None
илиlen <= len
больше недействительны, например, вместо возвратаNone < None
поднимаются . Как следствие, сортировка разнородного списка больше не имеет смысла - все элементы должны быть сопоставимы друг с другом. Обратите внимание , что это не относится к и операторам: объекты различных несравнимых типов всегда сравнивать неравны друг с другом.TypeError
False
==
!=
Есть много типов данных, которые перегружают операторы сравнения, чтобы делать что-то другое (фреймы данных из панд, массивы numpy). Если код , который вы использовали сделал что - то еще, это было потому , что x
был неlist
, но с экземпляром некоторого другого класса с оператором <
переопределены вернуть значение, которое не является bool
; и это значение затем было обработано специально x[]
(также известное как __getitem__
/ __setitem__
)
+False
Привет, Perl, привет, JavaScript, как дела?
UNARY_POSITIVE
код операции, который вызывает__pos__
__setitem__
а не __getitem__
в последнем разделе. Также я надеюсь, что вы не возражаете, что мой ответ был вдохновлен этой частью вашего ответа.
__getitem__
хотя в равной степени мог бы быть __setitem__
и__delitem__
У этого есть еще одно применение: кодировать гольф. Кодовый гольф - это искусство написания программ, которые решают некоторые проблемы с использованием как можно меньшего количества байтов исходного кода.
return(a,b)[c<d]
примерно эквивалентен
if c < d:
return b
else:
return a
за исключением того, что и a, и b оцениваются в первой версии, но не во второй версии.
c<d
оценивается как True
или False
.
(a, b)
это кортеж.
Индексирование кортежа похоже на индексацию списка: (3,5)[1]
== 5
.
True
равно 1
и False
равно 0
.
(a,b)[c<d]
(a,b)[True]
(a,b)[1]
b
или для False
:
(a,b)[c<d]
(a,b)[False]
(a,b)[0]
a
В сети обмена стеками есть хороший список множества неприятных вещей, которые вы можете сделать с Python, чтобы сэкономить несколько байтов. /codegolf/54/tips-for-golfing-in-python
Хотя в обычном коде это никогда не должно использоваться, и в вашем случае это будет означать, что это x
действует как нечто, что можно сравнить с целым числом, и как контейнер, поддерживающий нарезку, что является очень необычной комбинацией. Вероятно, это код Numpy, как указывали другие.
Code Golf is the art of writing programs
: ')
В общем, это могло означать что угодно . Это уже было объяснено , что это значит , если x
это list
или , numpy.ndarray
но в целом это зависит только от того, как операторы сравнения ( <
, >
, ...) , а также как получить / установить-элемент ( [...]
реализуются -syntax).
x.__getitem__(x.__lt__(2)) # this is what x[x < 2] means!
x.__setitem__(x.__lt__(2), 0) # this is what x[x < 2] = 0 means!
Так как:
x < value
эквивалентно x.__lt__(value)
x[value]
(примерно) эквивалентно x.__getitem__(value)
x[value] = othervalue
(также примерно) эквивалентно x.__setitem__(value, othervalue)
.Его можно настроить так, чтобы делать все, что вы хотите. Просто в качестве примера (имитирует немного numpys-логическое индексирование):
class Test:
def __init__(self, value):
self.value = value
def __lt__(self, other):
# You could do anything in here. For example create a new list indicating if that
# element is less than the other value
res = [item < other for item in self.value]
return self.__class__(res)
def __repr__(self):
return '{0} ({1})'.format(self.__class__.__name__, self.value)
def __getitem__(self, item):
# If you index with an instance of this class use "boolean-indexing"
if isinstance(item, Test):
res = self.__class__([i for i, index in zip(self.value, item) if index])
return res
# Something else was given just try to use it on the value
return self.value[item]
def __setitem__(self, item, value):
if isinstance(item, Test):
self.value = [i if not index else value for i, index in zip(self.value, item)]
else:
self.value[item] = value
Итак, теперь давайте посмотрим, что произойдет, если вы его используете:
>>> a = Test([1,2,3])
>>> a
Test ([1, 2, 3])
>>> a < 2 # calls __lt__
Test ([True, False, False])
>>> a[Test([True, False, False])] # calls __getitem__
Test ([1])
>>> a[a < 2] # or short form
Test ([1])
>>> a[a < 2] = 0 # calls __setitem__
>>> a
Test ([0, 2, 3])
Обратите внимание, это всего лишь одна возможность. Вы можете реализовать практически все, что захотите.