我发现一些代码中有一行类似于:
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]
这只对 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]
被改变了。
为什么会发生这种情况?
比较规则如下:
当你比较两个字符串或两个数值类型时,按预期的方式进行排序(按字典顺序排序的字符串,按整数进行数值排序)。
当你比较一个数值类型和一个非数值类型时,数值类型排在前面。
当你比较两个不兼容的类型,其中没有一个是数值类型时,按它们类型名称的字母顺序进行排序:
因此,我们有以下顺序:
数值 < 列表 < 字符串 < 元组
参见How does Python compare string and int?的接受答案。
如果x是NumPy数组,那么由于布尔数组索引,语法更有意义。在这种情况下,x<2
根本不是布尔值;它是一个由布尔值组成的数组,表示x
的每个元素是否小于2。x[x<2] = 0
然后选择小于2的x
的元素,并将这些单元格设置为0。请参见Indexing。
>>> 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
[0 if i < 2 else i for i in x]
相比)或者这是Numpy中鼓励的风格吗? - Tim Pederickx[x<2]
将返回一个numpy数组,而[0 if i<2 else i for i in x]
则返回一个列表。这是因为x[x<2]
是一个索引操作(在numpy/scipy/pandas中称为切片操作,因为它可以掩盖数据),而列表推导式是一个新对象定义。请参见NumPy索引。 - Michael Delgado>>> x = [1,2,3,4,5]
>>> x<2
False
>>> x[False]
1
>>> x[True]
2
布尔类型会被简单地转换为整数。索引只可能是0或1。
x
和 2
是“有序一致但任意的”,并且在不同的Python实现中排序可能会发生变化。 - Robᵩx<2==false
? - Iłya Bursovbool
不会转换为整数,而是bool
本身就是一个整数。 - Antti Haapala -- Слава Україні你问题中的原始代码仅适用于Python 2。如果x
是Python 2中的list
,则比较x < y
如果y
是一个int
eger,则为False
。这是因为将列表与整数进行比较没有意义。然而,在Python 2中,如果操作数不可比较,则基于类型名称按字母顺序排序的方式进行比较;此外,在混合类型比较中所有数字都排在前面。这甚至未在CPython 2的文档中详细说明,不同的Python 2实现可能会产生不同的结果。因此,[1, 2, 3, 4, 5] < 2
的结果为False
,因为2
是一个数字,因此在CPython中比list
“小”。这种混合比较最终被认为是过于晦涩的功能,并在Python 3.0中被删除。
<
的结果是一个 bool
; 而 bool
是 int
的一个子类:>>> isinstance(False, int)
True
>>> isinstance(True, int)
True
>>> False == 0
True
>>> True == 1
True
>>> False + 5
5
>>> True + 5
6
TypeError: unorderable types: list() < int()
的错误,这是由于Python 3.0中的更改所导致的:
比较操作
Python 3.0 简化了比较操作的规则:<
,<=
,>=
,>
)在操作数没有有意义的自然排序时会引发TypeError
异常。因此,表达式如1 < ''
,0 > None
或len <= len
不再有效,并且例如None < None
引发TypeError
而不是返回False
。一个推论是对于异构列表进行排序不再有意义——所有元素必须可相互比较。请注意,这不适用于==
和!=
运算符:不同类型的对象始终与彼此不可比较。
有许多数据类型会重载比较运算符以执行不同的操作(例如pandas中的数据框架和numpy的数组)。如果你使用的代码执行了其他操作,那么这是因为x
不是一个list
,而是另一个类的实例,该类重载了运算符<
以返回一个不是bool
的值;然后这个值被x[]
(也称为__getitem__
/__setitem__
)特殊处理。
__setitem__
而不是 __getitem__
。另外,希望你不介意我的回答受到了你回答中那部分的启发。 - MSeifert__getitem__
,虽然同样也可以是__setitem__
和__delitem__
。 - Antti Haapala -- Слава Україні这里还有一个用途:代码高尔夫。代码高尔夫是编写尽可能少的源代码字节解决某些问题的艺术。
return(a,b)[c<d]
大致等同于
if c < d:
return b
else:
return a
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])
请注意,这只是一种可能性。您可以自由地实现几乎所有您想要的东西。