x [x <2] = 0在Python中是什么意思?


85

我遇到了一些代码,类似于

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]

7
用列表来做到这一点是没有意义的。
dbliss '16

12
这仅对NumPy数组或类似对象有意义,它们的行为与实验行为或任一答案中说明的基于列表的行为完全不同。
user2357112支持Monica

11
请注意,这在Python 3中不起作用。只有在比较有意义时才可以比较类型。在Python 3中,此示例抛出TypeError: unorderable types: list() < int()
Morgan Thrapp '16

2
信息太少。应该提到的是该数组是一个numpy数组。
lmaooooo '16

3
我感到震惊的是,它获得了如此多的赞誉(尽管对于SO格式确实是一个好问题)。
PascalVKooten '16

Answers:


120

这仅对NumPy数组有意义。列表的行为是无用的,并且特定于Python 2(不是Python 3)。您可能需要仔细检查原始对象是否确实是NumPy数组(请参见下文)而不是列表。

但是在您的代码中,x是一个简单的列表。

以来

x < 2

是False,即0,因此

x[x<2]x[0]

x[0] 被改变。

相反,x[x>2]x[True]x[1]

因此,x[1]得到改变。

为什么会这样?

比较的规则是:

  1. 当您对两个字符串或两个数字类型进行排序时,将以预期的方式进行排序(字符串的字典顺序,整数的数字顺序)。

  2. 订购数字类型和非数字类型时,数字类型优先。

  3. 当您订购两种都不兼容的不兼容类型时,它们都不是数字,则按其名称的字母顺序排序:

因此,我们有以下顺序

数字<列表<字符串<元组

有关Python如何比较字符串和整数的信息,请参见可接受的答案

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

11
鉴于OP特别指出“我遇到了类似这样的代码...”,我认为您描述numpy布尔索引的答案非常有用-可能值得指出的是,如果OP向上滚动他们查看的代码,它们我几乎肯定会看到一个importnumpy。
J理查德·斯内普

2
当然,这仍然是一种过于聪明的方法吗?(与之相比[0 if i < 2 else i for i in x]。)还是Numpy中这种鼓励的风格?
蒂姆·皮德里克

6
@TimPederick:对NumPy使用列表推导是一个非常糟糕的主意。它的速度慢了几十到数百倍,它不适用于任意维数组,更容易弄乱元素类型,并且它创建列表而不是数组。布尔数组索引是完全正常的,在NumPy中是预期的。
user2357112支持Monica 2016年

@TimPederick除了性能受到影响外,编写代码的人可能还会继续使用numpy数组。x[x<2]将返回一个numpy数组,而[0 if i<2 else i for i in x]返回一个列表。这是因为x[x<2]是索引操作(由于能够屏蔽数据,因此在numpy / scipy / pandas中称为切片操作),而列表理解是新的对象定义。请参阅NumPy索引编制
Michael Delgado

45
>>> x = [1,2,3,4,5]
>>> x<2
False
>>> x[False]
1
>>> x[True]
2

布尔值仅转换为整数。索引为0或1。


7
你可能会提到,x2一致,但任意订制和订货会在不同的Python实现改变。
罗伯ᵩ

2
我还要补充一点,这是一种聪明的做事方式,我认为应该避免。明确地做到这一点-OP必须问这个问题的事实支持了我的观点。
kratenko '16

11
您能否添加更多详细信息,为什么x<2 == false
伊利亚Bursov

15
bool不转换为整数,boolPython中的
Antti Haapala

2
只是为了澄清@ AnttiHaapala的声明为任何其他人出现时,bool 是一个子类int
porglezomp

14

在你的问题的原代码只能在Python 2.如果xlist在Python 2中,比较x < yFalse,如果yint埃格尔。这是因为将列表与整数进行比较没有意义。但是在Python 2中,如果操作数不具有可比性,则比较在CPython中基于类型名称字母顺序;此外,所有数字在混合类型比较中都排在第一位。CPython 2的文档中甚至没有阐明这一点,不同的Python 2实现可能会产生不同的结果。这是[1, 2, 3, 4, 5] < 2计算结果为False,因为2是一个数字,因此多了一个“小”list的CPython的。最终,这种混合的比较被视为功能过于模糊,并已在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

因此,基本上,您将元素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。必然的结果是,对异构列表进行排序不再有意义-所有元素必须彼此可比。请注意,这不适用于==!=运算符:不同类型的对象总是比较彼此不相等。


有许多数据类型使比较运算符过载,以执行不同的操作(来自pandas的数据帧,numpy的数组)。如果您正在使用的代码做了别的东西,那是因为x不是一个list,而是与运营商的一些其他类的实例<覆盖,以返回的值是不是一个bool; 然后由x[](aka __getitem__/ __setitem__)专门处理此值


6
+False嗨,Perl,嗨,JavaScript,大家好吗?

Perl Javascript中的@cat,它将值转换为数字。在Python中,它是为UNARY_POSITIVE操作码调用的__pos__
Antti Haapala

我认为您的意思__setitem__不是__getitem__在上一节中。另外,我希望您不要介意我的答案受到那部分答案的启发。
MSeifert '16

不,我的意思是并正在考虑,__getitem__尽管同样可以做到,__setitem__以及__delitem__
Antti Haapala

9

这还有另一种用途:编码高尔夫。代码高尔夫是编写程序的艺术,该程序可以在尽可能少的源代码字节中解决某些问题。

return(a,b)[c<d]

大致相当于

if c < d:
    return b
else:
    return a

除了a和b均在第一个版本中评估,而在第二个版本中不评估。

c<d评估为TrueFalse
(a, b)是一个元组。
在元组上建立索引的工作方式类似于在列表上建立索引:(3,5)[1]== 5
True等于1False等于0

  1. (a,b)[c<d]
  2. (a,b)[True]
  3. (a,b)[1]
  4. b

False

  1. (a,b)[c<d]
  2. (a,b)[False]
  3. (a,b)[0]
  4. a

在堆栈交换网络上有很多不错的清单,您可以对python做很多讨厌的事情,以节省一些字节。/codegolf/54/tips-for-golfing-in-python

尽管在常规代码中永远不要使用它,但是在您的情况下,这意味着x它既可以与整数进行比较,又可以作为支持切片的容器,这是非常不寻常的组合。正如其他人指出的那样,这可能是Numpy代码。


6
Code Golf is the art of writing programs:')
cat

1
未成年人挑剔:本BOOL不为int,它仅仅一个(见其他答案)

6

一般来说,这可能意味着任何事情。这是已经解释这是什么意思,如果xlistnumpy.ndarray,但一般只取决于如何比较运算符(<>,...)以及如何了get / set项([...]-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-boolean索引):

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

请注意,这只是一种可能性。您可以自由实施几乎所有想要的东西。


我要说的是,对于逻辑上可以解释的行为(如已接受的答案),使用任何东西真的太笼统。
PascalVKooten '16

@PascalvKooten您不同意“任何”或广义的答案吗?我认为这很重要,因为python中的大多数逻辑行为只是按照惯例。
MSeifert '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.