标签归档:operators

Python中的’@ =’符号是什么?

问题:Python中的’@ =’符号是什么?

我知道@是给装饰器用的,但是@=Python有什么用呢?只是保留一些未来的想法吗?

这只是我阅读时遇到的许多问题之一tokenizer.py

I know @ is for decorators, but what is @= for in Python? Is it just reservation for some future idea?

This is just one of my many questions while reading tokenizer.py.


回答 0

文档

@(在)操作者意图被用于矩阵乘法。没有内置的Python类型实现此运算符。

@运算符是在Python 3.5中引入的。@=正如您所期望的那样,是矩阵乘法,后跟赋值。它们映射到__matmul____rmatmul____imatmul__类似于如何++=映射__add____radd____iadd__

PEP 465中详细讨论了操作员及其背后的原理。

From the documentation:

The @ (at) operator is intended to be used for matrix multiplication. No builtin Python types implement this operator.

The @ operator was introduced in Python 3.5. @= is matrix multiplication followed by assignment, as you would expect. They map to __matmul__, __rmatmul__ or __imatmul__ similar to how + and += map to __add__, __radd__ or __iadd__.

The operator and the rationale behind it are discussed in detail in PEP 465.


回答 1

@=@是Python 3.5中引入的用于执行矩阵乘法的新运算符。它们的目的是澄清迄今为止​​与运算符之间存在的混淆,该运算符*根据该特定库/代码中采用的约定用于元素方式乘法或矩阵乘法。结果,将来,运营商*只能用于按元素乘法。

PEP0465中所述,引入了两个运算符:

  • 一个新的二进制运算符A @ B,与A * B
  • 就地版本A @= B,与A *= B

矩阵乘法与按元素乘法

为了快速突出区别,对于两个矩阵:

A = [[1, 2],    B = [[11, 12],
     [3, 4]]         [13, 14]]
  • 逐元素乘法将生成:

    A * B = [[1 * 11,   2 * 12], 
             [3 * 13,   4 * 14]]
  • 矩阵乘法将生成:

    A @ B  =  [[1 * 11 + 2 * 13,   1 * 12 + 2 * 14],
               [3 * 11 + 4 * 13,   3 * 12 + 4 * 14]]

在Numpy中使用

到目前为止,Numpy使用以下约定:

@运算符的引入使涉及矩阵乘法的代码更易于阅读。PEP0465举了一个例子:

# Current implementation of matrix multiplications using dot function
S = np.dot((np.dot(H, beta) - r).T,
            np.dot(inv(np.dot(np.dot(H, V), H.T)), np.dot(H, beta) - r))

# Current implementation of matrix multiplications using dot method
S = (H.dot(beta) - r).T.dot(inv(H.dot(V).dot(H.T))).dot(H.dot(beta) - r)

# Using the @ operator instead
S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)

显然,最后一种实现更易于阅读和解释为等式。

@= and @ are new operators introduced in Python 3.5 performing matrix multiplication. They are meant to clarify the confusion which existed so far with the operator * which was used either for element-wise multiplication or matrix multiplication depending on the convention employed in that particular library/code. As a result, in the future, the operator * is meant to be used for element-wise multiplication only.

As explained in PEP0465, two operators were introduced:

  • A new binary operator A @ B, used similarly as A * B
  • An in-place version A @= B, used similarly as A *= B

Matrix Multiplication vs Element-wise Multiplication

To quickly highlight the difference, for two matrices:

A = [[1, 2],    B = [[11, 12],
     [3, 4]]         [13, 14]]
  • Element-wise multiplication will yield:

    A * B = [[1 * 11,   2 * 12], 
             [3 * 13,   4 * 14]]
    
  • Matrix multiplication will yield:

    A @ B  =  [[1 * 11 + 2 * 13,   1 * 12 + 2 * 14],
               [3 * 11 + 4 * 13,   3 * 12 + 4 * 14]]
    

Usage in Numpy

So far, Numpy used the following convention:

Introduction of the @ operator makes the code involving matrix multiplications much easier to read. PEP0465 gives us an example:

# Current implementation of matrix multiplications using dot function
S = np.dot((np.dot(H, beta) - r).T,
            np.dot(inv(np.dot(np.dot(H, V), H.T)), np.dot(H, beta) - r))

# Current implementation of matrix multiplications using dot method
S = (H.dot(beta) - r).T.dot(inv(H.dot(V).dot(H.T))).dot(H.dot(beta) - r)

# Using the @ operator instead
S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)

Clearly, the last implementation is much easier to read and interpret as an equation.


回答 2

@是Python3.5中新增的矩阵乘法运算符

参考:https : //docs.python.org/3/whatsnew/3.5.html#whatsnew-pep-465

C = A @ B

@ is the new operator for Matrix Multiplication added in Python3.5

Reference: https://docs.python.org/3/whatsnew/3.5.html#whatsnew-pep-465

Example

C = A @ B

+ =在python中到底是做什么的?

问题:+ =在python中到底是做什么的?

我需要知道+ =在python中做什么。就这么简单。我也希望链接到python中其他速记工具的定义。

I need to know what += does in python. It’s that simple. I also would appreciate links to definitions of other short hand tools in python.


回答 0

在Python中,+ =是__iadd__特殊方法的糖衣,__add__或者__radd__如果__iadd__不存在,则为+ 。__iadd__类的方法可以执行任何所需的操作。列表对象实现了它,并使用它来迭代一个可迭代对象,该对象将每个元素附加到自身上,方法与列表的extend方法相同。

这是一个实现__iadd__特殊方法的简单自定义类。您可以使用int初始化对象,然后可以使用+ =运算符添加数字。我在其中添加了一条打印语句,__iadd__以表明它被调用了。另外,__iadd__期望返回一个对象,因此我返回了自身的加号以及在这种情况下有意义的其他数字。

>>> class Adder(object):
        def __init__(self, num=0):
            self.num = num

        def __iadd__(self, other):
            print 'in __iadd__', other
            self.num = self.num + other
            return self.num

>>> a = Adder(2)
>>> a += 3
in __iadd__ 3
>>> a
5

希望这可以帮助。

In Python, += is sugar coating for the __iadd__ special method, or __add__ or __radd__ if __iadd__ isn’t present. The __iadd__ method of a class can do anything it wants. The list object implements it and uses it to iterate over an iterable object appending each element to itself in the same way that the list’s extend method does.

Here’s a simple custom class that implements the __iadd__ special method. You initialize the object with an int, then can use the += operator to add a number. I’ve added a print statement in __iadd__ to show that it gets called. Also, __iadd__ is expected to return an object, so I returned the addition of itself plus the other number which makes sense in this case.

>>> class Adder(object):
        def __init__(self, num=0):
            self.num = num

        def __iadd__(self, other):
            print 'in __iadd__', other
            self.num = self.num + other
            return self.num

>>> a = Adder(2)
>>> a += 3
in __iadd__ 3
>>> a
5

Hope this helps.


回答 1

+= 将另一个值与变量的值相加,然后将新值分配给该变量。

>>> x = 3
>>> x += 2
>>> print x
5

-=*=/=做减法,乘法和除法类似。

+= adds another value with the variable’s value and assigns the new value to the variable.

>>> x = 3
>>> x += 2
>>> print x
5

-=, *=, /= does similar for subtraction, multiplication and division.


回答 2

x += 5x = x + 5Python中所说的不完全相同。

注意这里:

In [1]: x = [2,3,4]    
In [2]: y = x    
In [3]: x += 7,8,9    
In [4]: x
Out[4]: [2, 3, 4, 7, 8, 9]    
In [5]: y
Out[5]: [2, 3, 4, 7, 8, 9]    
In [6]: x += [44,55]    
In [7]: x
Out[7]: [2, 3, 4, 7, 8, 9, 44, 55]    
In [8]: y
Out[8]: [2, 3, 4, 7, 8, 9, 44, 55]    
In [9]: x = x + [33,22]    
In [10]: x
Out[10]: [2, 3, 4, 7, 8, 9, 44, 55, 33, 22]    
In [11]: y
Out[11]: [2, 3, 4, 7, 8, 9, 44, 55]

请参阅以供参考:为什么+ =在列表上表现异常?

x += 5 is not exactly same as saying x = x + 5 in Python.

Note here:

In [1]: x = [2,3,4]    
In [2]: y = x    
In [3]: x += 7,8,9    
In [4]: x
Out[4]: [2, 3, 4, 7, 8, 9]    
In [5]: y
Out[5]: [2, 3, 4, 7, 8, 9]    
In [6]: x += [44,55]    
In [7]: x
Out[7]: [2, 3, 4, 7, 8, 9, 44, 55]    
In [8]: y
Out[8]: [2, 3, 4, 7, 8, 9, 44, 55]    
In [9]: x = x + [33,22]    
In [10]: x
Out[10]: [2, 3, 4, 7, 8, 9, 44, 55, 33, 22]    
In [11]: y
Out[11]: [2, 3, 4, 7, 8, 9, 44, 55]

See for reference: Why does += behave unexpectedly on lists?


回答 3

+=在变量中添加一个数字,从而在过程中更改变量本身(而+不会)。与此类似,以下内容也可以修改变量:

  • -=,从变量中减去一个值,将变量设置为结果
  • *=,将变量和一个值相乘,结果就是变量
  • /=,将变量除以值,使结果成为变量
  • %=,对变量执行模数,然后将变量设置为其结果

可能还有其他。我不是Python程序员。

+= adds a number to a variable, changing the variable itself in the process (whereas + would not). Similar to this, there are the following that also modifies the variable:

  • -=, subtracts a value from variable, setting the variable to the result
  • *=, multiplies the variable and a value, making the outcome the variable
  • /=, divides the variable by the value, making the outcome the variable
  • %=, performs modulus on the variable, with the variable then being set to the result of it

There may be others. I am not a Python programmer.


回答 4

它将右边的操作数添加到左边。 x += 2手段x = x + 2

它还可以将元素添加到列表中-请参见此SO线程

It adds the right operand to the left. x += 2 means x = x + 2

It can also add elements to a list — see this SO thread.


回答 5

这不仅仅是语法上的捷径。试试这个:

x=[]                   # empty list
x += "something"       # iterates over the string and appends to list
print(x)               # ['s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g']

x=[]                   # empty list
x = x + "something"    # TypeError: can only concatenate list (not "str") to list

这说明+ =调用iadd list方法,而+调用add,后者对列表执行不同的操作。

It is not a mere syntactic shortcut. Try this:

x=[]                   # empty list
x += "something"       # iterates over the string and appends to list
print(x)               # ['s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g']

versus

x=[]                   # empty list
x = x + "something"    # TypeError: can only concatenate list (not "str") to list

This illustrates that += invokes the iadd list method but + invokes add, which do different things with lists.


回答 6

通常,a + = b将b“加”到将结果存储到a中。这种简单的描述将以多种语言描述+ =运算符。

然而,简单的描述提出了两个问题。

  1. “添加”到底是什么意思?
  2. “将结果存储在a中”到底是什么意思?python变量不直接存储值,而是存储对对象的引用。

在python中,这两个问题的答案取决于a的数据类型。


那么“加”到底是什么意思?

  • 对于数字,表示数字加法。
  • 对于列表,元组,字符串等,意味着串联。

请注意,对于列表+ =比+更具灵活性,列表上的+运算符需要另一个列表,但是+ =运算符将接受任何可迭代的。


那么“将值存储在”中意味着什么呢?

如果对象是可变的,则鼓励(但不是必需)就地执行修改。因此,指向以前做过的同一个对象,但是该对象现在具有不同的内容。

如果对象是不可变的,则显然不能就地执行修改。一些可变对象也可能没有就地“添加”操作的实现。在这种情况下,变量“ a”将更新为指向包含加法运算结果的新对象。

从技术上讲,这是通过__IADD__首先查找来实现的,如果未实现,则__ADD__尝试并最后__RADD__


当在不确定确切类型的变量上使用python中的+ =时,需要特别小心,尤其是在不确定不确定类型是否可变的情况下。例如,考虑以下代码。

def dostuff(a):
    b = a
    a += (3,4)
    print(repr(a)+' '+repr(b))

dostuff((1,2))
dostuff([1,2])

当我们使用一个元组调用dostuff时,该元组将被复制为+ =操作的一部分,因此b不受影响。但是,当我们使用列表调用它时,列表会被修改,因此a和b都会受到影响。

在python 3中,“ bytes”和“ bytearray”类型的行为类似。


最后请注意,即使未替换对象,也会发生重新分配。如果左侧只是一个变量,这无关紧要,但是当您有一个引用可变集合的不可变集合时,它可能引起混乱的行为,例如:

a = ([1,2],[3,4])
a[0] += [5]

在这种情况下,[5]将成功添加到a [0]引用的列表中,但是此后,当代码尝试重新分配a [0]失败时,将引发异常。

Notionally a += b “adds” b to a storing the result in a. This simplistic description would describe the += operator in many languages.

However the simplistic description raises a couple of questions.

  1. What exactly do we mean by “adding”?
  2. What exactly do we mean by “storing the result in a”? python variables don’t store values directly they store references to objects.

In python the answers to both of these questions depend on the data type of a.


So what exactly does “adding” mean?

  • For numbers it means numeric addition.
  • For lists, tuples, strings etc it means concatenation.

Note that for lists += is more flexible than +, the + operator on a list requires another list, but the += operator will accept any iterable.


So what does “storing the value in a” mean?

If the object is mutable then it is encouraged (but not required) to perform the modification in-place. So a points to the same object it did before but that object now has different content.

If the object is immutable then it obviously can’t perform the modification in-place. Some mutable objects may also not have an implementation of an in-place “add” operation . In this case the variable “a” will be updated to point to a new object containing the result of an addition operation.

Technically this is implemented by looking for __IADD__ first, if that is not implemented then __ADD__ is tried and finally __RADD__.


Care is required when using += in python on variables where we are not certain of the exact type and in particular where we are not certain if the type is mutable or not. For example consider the following code.

def dostuff(a):
    b = a
    a += (3,4)
    print(repr(a)+' '+repr(b))

dostuff((1,2))
dostuff([1,2])

When we invoke dostuff with a tuple then the tuple is copied as part of the += operation and so b is unaffected. However when we invoke it with a list the list is modified in place, so both a and b are affected.

In python 3, similar behaviour is observed with the “bytes” and “bytearray” types.


Finally note that reassignment happens even if the object is not replaced. This doesn’t matter much if the left hand side is simply a variable but it can cause confusing behaviour when you have an immutable collection referring to mutable collections for example:

a = ([1,2],[3,4])
a[0] += [5]

In this case [5] will successfully be added to the list referred to by a[0] but then afterwards an exception will be raised when the code tries and fails to reassign a[0].


回答 7

简短的答案+=可以翻译为“将+ =右侧的任何内容添加到+ =“左侧的变量中。

例如 如果是a = 10这样的a += 5话: a = a + 5

因此,“ a”现在等于15。

The short answer is += can be translated as “add whatever is to the right of the += to the variable on the left of the +=”.

Ex. If you have a = 10 then a += 5 would be: a = a + 5

So, “a” now equal to 15.


回答 8

注意x += yx = x + y某些情况下包含附加运算符不同,这是因为运算符优先级加上总是首先评估右侧的事实,例如

>>> x = 2
>>> x += 2 and 1
>>> x
3

>>> x = 2
>>> x = x + 2 and 1
>>> x
1

注意第一种情况扩展为:

>>> x = 2
>>> x = x + (2 and 1)
>>> x
3

您更有可能在“现实世界”中与其他运营商(例如,

x *= 2 + 1== x = x * (2 + 1)!=x = x * 2 + 1

Note x += y is not the same as x = x + y in some situations where an additional operator is included because of the operator precedence combined with the fact that the right hand side is always evaluated first, e.g.

>>> x = 2
>>> x += 2 and 1
>>> x
3

>>> x = 2
>>> x = x + 2 and 1
>>> x
1

Note the first case expand to:

>>> x = 2
>>> x = x + (2 and 1)
>>> x
3

You are more likely to encounter this in the ‘real world’ with other operators, e.g.

x *= 2 + 1 == x = x * (2 + 1) != x = x * 2 + 1


回答 9

+= 只是写作的捷径

number = 4
number = number + 1

所以你会写

numbers = 4
numbers += 1

两种方法都是正确的,但是示例二可以帮助您减少编写代码

+= is just a shortcut for writing

number = 4
number = number + 1

So instead you would write

numbers = 4
numbers += 1

Both ways are correct but example two helps you write a little less code


回答 10

就像其他人所说的,+ =运算符是快捷方式。一个例子:

var = 1;
var = var + 1;
#var = 2

也可以这样写:

var = 1;
var += 1;
#var = 2

因此,无需编写第一个示例,您只需编写第二个示例,就可以了。

As others also said, the += operator is a shortcut. An example:

var = 1;
var = var + 1;
#var = 2

It could also be written like so:

var = 1;
var += 1;
#var = 2

So instead of writing the first example, you can just write the second one, which would work just fine.


回答 11

请记住,当您过去使用旧计算器求和时(例如2和3),每当您点击=总和时,您就会看到3被加到总数中,+=执行类似的工作。例:

>>> orange = 2
>>> orange += 3
>>> print(orange)
5
>>> orange +=3
>>> print(orange)
8

Remember when you used to sum, for example 2 & 3, in your old calculator and every time you hit the = you see 3 added to the total, the += does similar job. Example:

>>> orange = 2
>>> orange += 3
>>> print(orange)
5
>>> orange +=3
>>> print(orange)
8

回答 12

我看到很多使用+ =并带有多个整数的答案。

一个例子:

x -= 1 + 3

这类似于:

x = x - (1 + 3)

并不是:

x = (x - 1) + 3

I’m seeing a lot of answers that don’t bring up using += with multiple integers.

One example:

x -= 1 + 3

This would be similar to:

x = x - (1 + 3)

and not:

x = (x - 1) + 3

回答 13

根据文档

x += y等价于x = operator.iadd(x, y)。另一种表达方式是说它z = operator.iadd(x, y)等同于复合语句z = x; z += y

因此x += 3与相同x = x + 3

x = 2

x += 3

print(x)

将输出5。

请注意,还有

According to the documentation

x += y is equivalent to x = operator.iadd(x, y). Another way to put it is to say that z = operator.iadd(x, y) is equivalent to the compound statement z = x; z += y.

So x += 3 is the same as x = x + 3.

x = 2

x += 3

print(x)

will output 5.

Notice that there’s also


函数调用中的星号

问题:函数调用中的星号

我正在使用itertools.chain以这种方式“拉平”列表列表:

uniqueCrossTabs = list(itertools.chain(*uniqueCrossTabs))

这跟说的有什么不同?

uniqueCrossTabs = list(itertools.chain(uniqueCrossTabs))

I’m using itertools.chain to “flatten” a list of lists in this fashion:

uniqueCrossTabs = list(itertools.chain(*uniqueCrossTabs))

how is this different than saying:

uniqueCrossTabs = list(itertools.chain(uniqueCrossTabs))

回答 0

* 是“ splat”运算符:它接受一个列表作为输入,并将其扩展为函数调用中的实际位置参数。

所以如果uniqueCrossTabs[ [ 1, 2 ], [ 3, 4 ] ],那就itertools.chain(*uniqueCrossTabs)等于说itertools.chain([ 1, 2 ], [ 3, 4 ])

这与传递just显然不同uniqueCrossTabs。对于您的情况,您有一个想要拼合的列表列表;什么itertools.chain()确实是在所有你传递给它的位置参数,其中每个位置参数是在自己的权利迭代拼接返回一个迭代。

换句话说,您希望将每个列表uniqueCrossTabs作为参数传递给chain(),这会将它们链接在一起,但是您没有在单独的变量中使用列表,因此可以使用*运算符将列表扩展为多个列表参数。

正如Jochen Ritzel在评论中指出的那样,chain.from_iterable()它更适合于此操作,因为它假定一个可迭代的对象开始。然后,您的代码将变得简单:

uniqueCrossTabs = list(itertools.chain.from_iterable(uniqueCrossTabs))

* is the “splat” operator: It takes a list as input, and expands it into actual positional arguments in the function call.

So if uniqueCrossTabs was [ [ 1, 2 ], [ 3, 4 ] ], then itertools.chain(*uniqueCrossTabs) is the same as saying itertools.chain([ 1, 2 ], [ 3, 4 ])

This is obviously different from passing in just uniqueCrossTabs. In your case, you have a list of lists that you wish to flatten; what itertools.chain() does is return an iterator over the concatenation of all the positional arguments you pass to it, where each positional argument is iterable in its own right.

In other words, you want to pass each list in uniqueCrossTabs as an argument to chain(), which will chain them together, but you don’t have the lists in separate variables, so you use the * operator to expand the list of lists into several list arguments.

As Jochen Ritzel has pointed out in the comments, chain.from_iterable() is better-suited for this operation, as it assumes a single iterable of iterables to begin with. Your code then becomes simply:

uniqueCrossTabs = list(itertools.chain.from_iterable(uniqueCrossTabs))

回答 1

它将序列拆分为函数调用的单独参数。

>>> def foo(a, b=None, c=None):
...   print a, b, c
... 
>>> foo([1, 2, 3])
[1, 2, 3] None None
>>> foo(*[1, 2, 3])
1 2 3
>>> def bar(*a):
...   print a
... 
>>> bar([1, 2, 3])
([1, 2, 3],)
>>> bar(*[1, 2, 3])
(1, 2, 3)

It splits the sequence into separate arguments for the function call.

>>> def foo(a, b=None, c=None):
...   print a, b, c
... 
>>> foo([1, 2, 3])
[1, 2, 3] None None
>>> foo(*[1, 2, 3])
1 2 3
>>> def bar(*a):
...   print a
... 
>>> bar([1, 2, 3])
([1, 2, 3],)
>>> bar(*[1, 2, 3])
(1, 2, 3)

回答 2

只是解释概念/使用它的另一种方法。

import random

def arbitrary():
    return [x for x in range(1, random.randint(3,10))]

a, b, *rest = arbitrary()

# a = 1
# b = 2
# rest = [3,4,5]

Just an alternative way of explaining the concept/using it.

import random

def arbitrary():
    return [x for x in range(1, random.randint(3,10))]

a, b, *rest = arbitrary()

# a = 1
# b = 2
# rest = [3,4,5]

Python中的插入符(^)有什么作用?

问题:Python中的插入符(^)有什么作用?

我今天在python中遇到了插入符号运算符,并对其进行了尝试,得到了以下输出:

>>> 8^3
11
>>> 8^4
12
>>> 8^1
9
>>> 8^0
8
>>> 7^1
6
>>> 7^2
5
>>> 7^7
0
>>> 7^8
15
>>> 9^1
8
>>> 16^1
17
>>> 15^1
14
>>>

它似乎基于8,所以我猜某种字节操作?除了对浮点数的奇怪表现之外,我似乎无法找到更多关于此搜索网站的信息,是否有人链接到该运算符的工作,或者您可以在此处进行解释?

I ran across the caret operator in python today and trying it out, I got the following output:

>>> 8^3
11
>>> 8^4
12
>>> 8^1
9
>>> 8^0
8
>>> 7^1
6
>>> 7^2
5
>>> 7^7
0
>>> 7^8
15
>>> 9^1
8
>>> 16^1
17
>>> 15^1
14
>>>

It seems to be based on 8, so I’m guessing some sort of byte operation? I can’t seem to find much about this searching sites other than it behaves oddly for floats, does anybody have a link to what this operator does or can you explain it here?


回答 0

这是按位异或(异或)。

如果一个操作数(仅一个)(评估为)为true,则结果为true。

展示:

>>> 0^0
0
>>> 1^1
0
>>> 1^0
1
>>> 0^1
1

要解释您自己的示例之一:

>>> 8^3
11

这样考虑:

1000#8(二进制)
0011#3(二进制)
----#应用XOR(“垂直”)
1011#结果= 11(二进制)

It’s a bitwise XOR (exclusive OR).

It results to true if one (and only one) of the operands (evaluates to) true.

To demonstrate:

>>> 0^0
0
>>> 1^1
0
>>> 1^0
1
>>> 0^1
1

To explain one of your own examples:

>>> 8^3
11

Think about it this way:

1000  # 8 (binary)
0011  # 3 (binary)
----  # APPLY XOR ('vertically')
1011  # result = 11 (binary)

回答 1

它根据需要调用对象的__xor__()or __rxor__()方法,对于整数类型,它按位进行异或。

It invokes the __xor__() or __rxor__() method of the object as needed, which for integer types does a bitwise exclusive-or.


回答 2

这是一点一点的异或。《 Python语言参考》的第5章介绍了二进制按位运算符。

It’s a bit-by-bit exclusive-or. Binary bitwise operators are documented in chapter 5 of the Python Language Reference.


回答 3

一般来说,符号^是or 方法的中版本。无论在符号的左右放置什么数据类型,都必须以兼容的方式实现此功能。对于整数,这是常见的操作,但是例如没有一个内置的类型定义的函数类型:__xor____rxor__XORfloatint

In [12]: 3 ^ 4
Out[12]: 7

In [13]: 3.3 ^ 4
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-858cc886783d> in <module>()
----> 1 3.3 ^ 4

TypeError: unsupported operand type(s) for ^: 'float' and 'int'

关于Python的一件整洁的事情是,您可以在自己的类中重写此行为。例如,在某些语言中,^符号表示幂。您可以通过这种方式做到这一点,就像一个示例:

class Foo(float):
    def __xor__(self, other):
        return self ** other

然后,类似的事情将起作用,现在,对于Fooonly的实例,该^符号将表示幂。

In [16]: x = Foo(3)

In [17]: x
Out[17]: 3.0

In [18]: x ^ 4
Out[18]: 81.0

Generally speaking, the symbol ^ is an infix version of the __xor__ or __rxor__ methods. Whatever data types are placed to the right and left of the symbol must implement this function in a compatible way. For integers, it is the common XOR operation, but for example there is not a built-in definition of the function for type float with type int:

In [12]: 3 ^ 4
Out[12]: 7

In [13]: 3.3 ^ 4
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-858cc886783d> in <module>()
----> 1 3.3 ^ 4

TypeError: unsupported operand type(s) for ^: 'float' and 'int'

One neat thing about Python is that you can override this behavior in a class of your own. For example, in some languages the ^ symbol means exponentiation. You could do that this way, just as one example:

class Foo(float):
    def __xor__(self, other):
        return self ** other

Then something like this will work, and now, for instances of Foo only, the ^ symbol will mean exponentiation.

In [16]: x = Foo(3)

In [17]: x
Out[17]: 3.0

In [18]: x ^ 4
Out[18]: 81.0

回答 4

当您使用^操作员时,幕后方法__xor__便会调用。

a^b 相当于 a.__xor__(b)

此外,a ^= b等价于a = a.__ixor__(b)__xor____ixor__通过使用隐式调用时,其中用作后备^=但不存在)。

原则上,什么__xor__完全取决于其实施。Python中的常见用例是:

  • 集的对称差异(所有元素恰好存在于两个集之一)

演示:

>>> a = {1, 2, 3}
>>> b = {1, 4, 5}
>>> a^b
{2, 3, 4, 5}
>>> a.symmetric_difference(b)
{2, 3, 4, 5}
  • 两个整数的位的按位不等于

演示:

>>> a = 5
>>> b = 6
>>> a^b
3

说明:

    101 (5 decimal)
XOR 110 (6 decimal)
-------------------
    011 (3 decimal)

When you use the ^ operator, behind the curtains the method __xor__ is called.

a^b is equivalent to a.__xor__(b).

Also, a ^= b is equivalent to a = a.__ixor__(b) (where __xor__ is used as a fallback when __ixor__ is implicitly called via using ^= but does not exist).

In principle, what __xor__ does is completely up to its implementation. Common use cases in Python are:

  • Symmetric Difference of sets (all elements present in exactly one of two sets)

Demo:

>>> a = {1, 2, 3}
>>> b = {1, 4, 5}
>>> a^b
{2, 3, 4, 5}
>>> a.symmetric_difference(b)
{2, 3, 4, 5}
  • Bitwise Non-Equal for the bits of two integers

Demo:

>>> a = 5
>>> b = 6
>>> a^b
3

Explanation:

    101 (5 decimal)
XOR 110 (6 decimal)
-------------------
    011 (3 decimal)

Python,我是否应该基于__eq__实现__ne __()运算符?

问题:Python,我是否应该基于__eq__实现__ne __()运算符?

我有一个要覆盖__eq__()运算符的类。我也应该重写__ne__()运算符似乎很有意义,但是__ne__基于__eq__这样的实现是否有意义?

class A:
    def __eq__(self, other):
        return self.value == other.value

    def __ne__(self, other):
        return not self.__eq__(other)

还是Python缺少使用这些运算符的方式而导致的一个好主意?

I have a class where I want to override the __eq__ method. It seems to make sense that I should override the __ne__ method as well, but does it make sense to implement __ne__ in terms of __eq__ as such?

class A:

    def __init__(self, attr):
        self.attr = attr

    def __eq__(self, other):
        return self.attr == other.attr
    
    def __ne__(self, other):
        return not self.__eq__(other)

Or is there something that I am missing with the way Python uses these methods that makes this not a good idea?


回答 0

是的,那很好。实际上,文档敦促您在定义__ne__时定义__eq__

比较运算符之间没有隐含的关系。的真相x==y并不意味着那x!=y 是错误的。因此,在定义时 __eq__(),还应该定义一个,__ne__()以便操作符能够按预期运行。

在很多情况下(例如此情况),它__eq__与否的结果一样简单,但并不总是如此。

Yes, that’s perfectly fine. In fact, the documentation urges you to define __ne__ when you define __eq__:

There are no implied relationships among the comparison operators. The truth of x==y does not imply that x!=y is false. Accordingly, when defining __eq__(), one should also define __ne__() so that the operators will behave as expected.

In a lot of cases (such as this one), it will be as simple as negating the result of __eq__, but not always.


回答 1

Python,我应该实现__ne__()基于的运算符__eq__吗?

简短的回答:不要实现它,但是如果必须的话,请使用==,而不是__eq__

在Python 3中,默认情况下!=是否定==,因此您甚至不需要编写__ne__,并且文档不再赘述。

一般而言,对于仅Python 3的代码,除非您需要使父实现(例如,内置对象)蒙上阴影,否则不要编写任何代码。

也就是说,请记住Raymond Hettinger的评论

只有在超类中尚未定义时,该__ne__方法__eq__才 自动从该方法__ne__开始。因此,如果您要从内置继承,则最好同时覆盖两者。

如果您需要代码在Python 2中运行,请遵循针对Python 2的建议,它将在Python 3中正常运行。

在Python 2中,Python本身不会自动根据另一个执行任何操作-因此,您应__ne__使用==而不是来定义__eq__。例如

class A(object):
    def __eq__(self, other):
        return self.value == other.value

    def __ne__(self, other):
        return not self == other # NOT `return not self.__eq__(other)`

看到证明

  • __ne__()基于__eq__和实现操作符
  • 根本没有__ne__在Python 2中实现

在下面的演示中提供了错误的行为。

长答案

Python 2 的文档说:

比较运算符之间没有隐含的关系。的真相x==y并不意味着那x!=y是错误的。因此,在定义时__eq__(),还应该定义一个,__ne__()以便操作符能够按预期运行。

因此,这意味着,如果我们__ne__根据的倒数进行定义__eq__,我们可以获得一致的行为。

文档的这一部分已针对Python 3更新

默认情况下,除非为,否则将结果__ne__()委托__eq__()并反转NotImplemented

“新功能”部分中,我们看到此行为已更改:

  • !=现在返回与的相反==,除非==返回NotImplemented

为了实现__ne__,我们更喜欢使用==运算符,而不是__eq__直接使用方法,以便如果self.__eq__(other)子类返回NotImplemented了所检查类型,Python将适当地other.__eq__(self) 从文档中进行检查:

NotImplemented对象

此类型具有单个值。有一个具有此值的对象。通过内置名称访问该对象 NotImplemented。如果数字方法和丰富比较方法未实现所提供操作数的操作,则可能返回此值。(然后,解释程序将根据操作员尝试执行反射操作或其他回退。)其真实值是true。

当给定一个丰富比较运算符,如果他们不相同的类型,Python中检查是否other是一个子类型,并且如果它具有定义的操作者,它使用other第一的方法(逆为<<=>=>)。如果NotImplemented返回,使用相反的方法。(它不是检查相同的方法两次。)使用==操作员允许这种逻辑发生。


期望

从语义上讲,您应该__ne__按照是否相等的检查来实现,因为类的用户将期望以下函数对A的所有实例都等效:

def negation_of_equals(inst1, inst2):
    """always should return same as not_equals(inst1, inst2)"""
    return not inst1 == inst2

def not_equals(inst1, inst2):
    """always should return same as negation_of_equals(inst1, inst2)"""
    return inst1 != inst2

也就是说,以上两个函数应始终返回相同的结果。但这取决于程序员。

演示__ne__基于以下内容的意外行为__eq__

首先设置:

class BaseEquatable(object):
    def __init__(self, x):
        self.x = x
    def __eq__(self, other):
        return isinstance(other, BaseEquatable) and self.x == other.x

class ComparableWrong(BaseEquatable):
    def __ne__(self, other):
        return not self.__eq__(other)

class ComparableRight(BaseEquatable):
    def __ne__(self, other):
        return not self == other

class EqMixin(object):
    def __eq__(self, other):
        """override Base __eq__ & bounce to other for __eq__, e.g. 
        if issubclass(type(self), type(other)): # True in this example
        """
        return NotImplemented

class ChildComparableWrong(EqMixin, ComparableWrong):
    """__ne__ the wrong way (__eq__ directly)"""

class ChildComparableRight(EqMixin, ComparableRight):
    """__ne__ the right way (uses ==)"""

class ChildComparablePy3(EqMixin, BaseEquatable):
    """No __ne__, only right in Python 3."""

实例化非等效实例:

right1, right2 = ComparableRight(1), ChildComparableRight(2)
wrong1, wrong2 = ComparableWrong(1), ChildComparableWrong(2)
right_py3_1, right_py3_2 = BaseEquatable(1), ChildComparablePy3(2)

预期行为:

(请注意:虽然以下各项的第二个断言都是等效的,因此在逻辑上与其之前的一个断言是多余的,但我将它们包括在内以证明当一个是另一个的子类时顺序并不重要。

这些实例通过以下方式__ne__实现==

assert not right1 == right2
assert not right2 == right1
assert right1 != right2
assert right2 != right1

这些实例(在Python 3下测试)也可以正常运行:

assert not right_py3_1 == right_py3_2
assert not right_py3_2 == right_py3_1
assert right_py3_1 != right_py3_2
assert right_py3_2 != right_py3_1

并回想起这些已__ne__通过__eq__– 实现,尽管这是预期的行为,但实现不正确:

assert not wrong1 == wrong2         # These are contradicted by the
assert not wrong2 == wrong1         # below unexpected behavior!

意外行为:

请注意,此比较与上述(not wrong1 == wrong2)比较相矛盾。

>>> assert wrong1 != wrong2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

和,

>>> assert wrong2 != wrong1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

不要__ne__在Python 2中跳过

有关不应该跳过__ne__在Python 2中实现的证据,请参见以下等效对象:

>>> right_py3_1, right_py3_1child = BaseEquatable(1), ChildComparablePy3(1)
>>> right_py3_1 != right_py3_1child # as evaluated in Python 2!
True

以上结果应该是False

Python 3源码

的默认CPython实现__ne__typeobject.cobject_richcompare

case Py_NE:
    /* By default, __ne__() delegates to __eq__() and inverts the result,
       unless the latter returns NotImplemented. */
    if (Py_TYPE(self)->tp_richcompare == NULL) {
        res = Py_NotImplemented;
        Py_INCREF(res);
        break;
    }
    res = (*Py_TYPE(self)->tp_richcompare)(self, other, Py_EQ);
    if (res != NULL && res != Py_NotImplemented) {
        int ok = PyObject_IsTrue(res);
        Py_DECREF(res);
        if (ok < 0)
            res = NULL;
        else {
            if (ok)
                res = Py_False;
            else
                res = Py_True;
            Py_INCREF(res);
        }
    }
    break;

但是默认__ne__使用__eq__

__ne__C 3级别使用Python 3的默认实现细节,__eq__因为更高级别==PyObject_RichCompare)的效率较低-因此,它也必须处理NotImplemented

如果__eq__正确实现,则对的取反==也是正确的-并且它使我们能够避免在中使用低级实现细节__ne__

使用==可以使我们将底层逻辑保持在一个地方,并避免NotImplemented在中寻址__ne__

一个人可能错误地认为==可能返回NotImplemented

实际上,它使用与的默认实现相同的逻辑__eq__,以检查身份(请参阅下面的do_richcompare和我们的证据)

class Foo:
    def __ne__(self, other):
        return NotImplemented
    __eq__ = __ne__

f = Foo()
f2 = Foo()

和比较:

>>> f == f
True
>>> f != f
False
>>> f2 == f
False
>>> f2 != f
True

性能

不要相信我,让我们看看更有效的方法:

class CLevel:
    "Use default logic programmed in C"

class HighLevelPython:
    def __ne__(self, other):
        return not self == other

class LowLevelPython:
    def __ne__(self, other):
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not equal

def c_level():
    cl = CLevel()
    return lambda: cl != cl

def high_level_python():
    hlp = HighLevelPython()
    return lambda: hlp != hlp

def low_level_python():
    llp = LowLevelPython()
    return lambda: llp != llp

我认为这些表现数字说明了一切:

>>> import timeit
>>> min(timeit.repeat(c_level()))
0.09377292497083545
>>> min(timeit.repeat(high_level_python()))
0.2654011140111834
>>> min(timeit.repeat(low_level_python()))
0.3378178110579029

当您认为这样low_level_python做是在Python中执行本来可以在C级别处理的逻辑时,这才有意义。

对一些批评家的回应

另一个回答者写道:

亚伦·霍尔(Aaron Hall)not self == other__ne__方法的实现是不正确的,因为它永远不会返回NotImplementednot NotImplementedis False),因此__ne__具有优先级的方法永远不会退回到__ne__没有优先级的方法。

__ne__从来没有回报NotImplemented并不能使它不正确。相反,我们NotImplemented通过与的相等性检查来处理优先级==。假设==正确实施,我们就完成了。

not self == other曾经是该方法的默认Python 3实现,__ne__但它是一个错误,正如ShadowRanger所注意到的,它在2015年1月的Python 3.4中已得到纠正(请参阅问题#21408)。

好吧,让我们解释一下。

如前所述,Python 3默认情况下__ne__通过首先检查是否self.__eq__(other)返回NotImplemented(单例)来处理-应该使用with进行检查,is如果返回则返回,否则应返回相反的值。这是作为类mixin编写的逻辑:

class CStyle__ne__:
    """Mixin that provides __ne__ functionality equivalent to 
    the builtin functionality
    """
    def __ne__(self, other):
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not equal

为了确保C级Python API的正确性,这是必需的,它是在Python 3中引入的,

多余的。所有相关的__ne__方法被拆除,其中包括实施自己的支票,以及那些委托给那些__eq__直接或通过==-和==是这样做的最常见的方式。

对称重要吗?

我们持批评态度提供了一个病态的例子,使办案NotImplemented__ne__,重视高于一切的对称性。让我们用一个清晰​​的例子来说明这个论点:

class B:
    """
    this class has no __eq__ implementation, but asserts 
    any instance is not equal to any other object
    """
    def __ne__(self, other):
        return True

class A:
    "This class asserts instances are equivalent to all other objects"
    def __eq__(self, other):
        return True

>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, False, True)

因此,通过这种逻辑,为了保持对称性__ne__,无论Python版本如何,我们都需要编写复杂的。

class B:
    def __ne__(self, other):
        return True

class A:
    def __eq__(self, other):
        return True
    def __ne__(self, other):
        result = other.__eq__(self)
        if result is NotImplemented:
            return NotImplemented
        return not result

>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, True, True)

显然,我们不应该考虑这些实例是否相等。

我建议对称性不如假定合理的代码并遵循文档的建议重要。

但是,如果A明智地实现__eq__,那么我们仍然可以按照我的指示进行操作,并且仍然具有对称性:

class B:
    def __ne__(self, other):
        return True

class A:
    def __eq__(self, other):
        return False         # <- this boolean changed... 

>>> A() == B(), B() == A(), A() != B(), B() != A()
(False, False, True, True)

结论

对于Python 2兼容代码,请使用==实现__ne__。更重要的是:

  • 正确
  • 简单
  • 表演者

仅在Python 3中,在C级别使用低级取反-它甚至更加简单和高效(尽管程序员负责确定它是正确的)。

再次,做高层次的Python编写底层逻辑。

Python, should I implement __ne__() operator based on __eq__?

Short Answer: Don’t implement it, but if you must, use ==, not __eq__

In Python 3, != is the negation of == by default, so you are not even required to write a __ne__, and the documentation is no longer opinionated on writing one.

Generally speaking, for Python 3-only code, don’t write one unless you need to overshadow the parent implementation, e.g. for a builtin object.

That is, keep in mind Raymond Hettinger’s comment:

The __ne__ method follows automatically from __eq__ only if __ne__ isn’t already defined in a superclass. So, if you’re inheriting from a builtin, it’s best to override both.

If you need your code to work in Python 2, follow the recommendation for Python 2 and it will work in Python 3 just fine.

In Python 2, Python itself does not automatically implement any operation in terms of another – therefore, you should define the __ne__ in terms of == instead of the __eq__. E.G.

class A(object):
    def __eq__(self, other):
        return self.value == other.value

    def __ne__(self, other):
        return not self == other # NOT `return not self.__eq__(other)`

See proof that

  • implementing __ne__() operator based on __eq__ and
  • not implementing __ne__ in Python 2 at all

provides incorrect behavior in the demonstration below.

Long Answer

The documentation for Python 2 says:

There are no implied relationships among the comparison operators. The truth of x==y does not imply that x!=y is false. Accordingly, when defining __eq__(), one should also define __ne__() so that the operators will behave as expected.

So that means that if we define __ne__ in terms of the inverse of __eq__, we can get consistent behavior.

This section of the documentation has been updated for Python 3:

By default, __ne__() delegates to __eq__() and inverts the result unless it is NotImplemented.

and in the “what’s new” section, we see this behavior has changed:

  • != now returns the opposite of ==, unless == returns NotImplemented.

For implementing __ne__, we prefer to use the == operator instead of using the __eq__ method directly so that if self.__eq__(other) of a subclass returns NotImplemented for the type checked, Python will appropriately check other.__eq__(self) From the documentation:

The NotImplemented object

This type has a single value. There is a single object with this value. This object is accessed through the built-in name NotImplemented. Numeric methods and rich comparison methods may return this value if they do not implement the operation for the operands provided. (The interpreter will then try the reflected operation, or some other fallback, depending on the operator.) Its truth value is true.

When given a rich comparison operator, if they’re not the same type, Python checks if the other is a subtype, and if it has that operator defined, it uses the other‘s method first (inverse for <, <=, >= and >). If NotImplemented is returned, then it uses the opposite’s method. (It does not check for the same method twice.) Using the == operator allows for this logic to take place.


Expectations

Semantically, you should implement __ne__ in terms of the check for equality because users of your class will expect the following functions to be equivalent for all instances of A.:

def negation_of_equals(inst1, inst2):
    """always should return same as not_equals(inst1, inst2)"""
    return not inst1 == inst2

def not_equals(inst1, inst2):
    """always should return same as negation_of_equals(inst1, inst2)"""
    return inst1 != inst2

That is, both of the above functions should always return the same result. But this is dependent on the programmer.

Demonstration of unexpected behavior when defining __ne__ based on __eq__:

First the setup:

class BaseEquatable(object):
    def __init__(self, x):
        self.x = x
    def __eq__(self, other):
        return isinstance(other, BaseEquatable) and self.x == other.x

class ComparableWrong(BaseEquatable):
    def __ne__(self, other):
        return not self.__eq__(other)

class ComparableRight(BaseEquatable):
    def __ne__(self, other):
        return not self == other

class EqMixin(object):
    def __eq__(self, other):
        """override Base __eq__ & bounce to other for __eq__, e.g. 
        if issubclass(type(self), type(other)): # True in this example
        """
        return NotImplemented

class ChildComparableWrong(EqMixin, ComparableWrong):
    """__ne__ the wrong way (__eq__ directly)"""

class ChildComparableRight(EqMixin, ComparableRight):
    """__ne__ the right way (uses ==)"""

class ChildComparablePy3(EqMixin, BaseEquatable):
    """No __ne__, only right in Python 3."""

Instantiate non-equivalent instances:

right1, right2 = ComparableRight(1), ChildComparableRight(2)
wrong1, wrong2 = ComparableWrong(1), ChildComparableWrong(2)
right_py3_1, right_py3_2 = BaseEquatable(1), ChildComparablePy3(2)

Expected Behavior:

(Note: while every second assertion of each of the below is equivalent and therefore logically redundant to the one before it, I’m including them to demonstrate that order does not matter when one is a subclass of the other.)

These instances have __ne__ implemented with ==:

assert not right1 == right2
assert not right2 == right1
assert right1 != right2
assert right2 != right1

These instances, testing under Python 3, also work correctly:

assert not right_py3_1 == right_py3_2
assert not right_py3_2 == right_py3_1
assert right_py3_1 != right_py3_2
assert right_py3_2 != right_py3_1

And recall that these have __ne__ implemented with __eq__ – while this is the expected behavior, the implementation is incorrect:

assert not wrong1 == wrong2         # These are contradicted by the
assert not wrong2 == wrong1         # below unexpected behavior!

Unexpected Behavior:

Note that this comparison contradicts the comparisons above (not wrong1 == wrong2).

>>> assert wrong1 != wrong2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

and,

>>> assert wrong2 != wrong1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

Don’t skip __ne__ in Python 2

For evidence that you should not skip implementing __ne__ in Python 2, see these equivalent objects:

>>> right_py3_1, right_py3_1child = BaseEquatable(1), ChildComparablePy3(1)
>>> right_py3_1 != right_py3_1child # as evaluated in Python 2!
True

The above result should be False!

Python 3 source

The default CPython implementation for __ne__ is in typeobject.c in object_richcompare:

case Py_NE:
    /* By default, __ne__() delegates to __eq__() and inverts the result,
       unless the latter returns NotImplemented. */
    if (Py_TYPE(self)->tp_richcompare == NULL) {
        res = Py_NotImplemented;
        Py_INCREF(res);
        break;
    }
    res = (*Py_TYPE(self)->tp_richcompare)(self, other, Py_EQ);
    if (res != NULL && res != Py_NotImplemented) {
        int ok = PyObject_IsTrue(res);
        Py_DECREF(res);
        if (ok < 0)
            res = NULL;
        else {
            if (ok)
                res = Py_False;
            else
                res = Py_True;
            Py_INCREF(res);
        }
    }
    break;

But the default __ne__ uses __eq__?

Python 3’s default __ne__ implementation detail at the C level uses __eq__ because the higher level == (PyObject_RichCompare) would be less efficient – and therefore it must also handle NotImplemented.

If __eq__ is correctly implemented, then the negation of == is also correct – and it allows us to avoid low level implementation details in our __ne__.

Using == allows us to keep our low level logic in one place, and avoid addressing NotImplemented in __ne__.

One might incorrectly assume that == may return NotImplemented.

It actually uses the same logic as the default implementation of __eq__, which checks for identity (see do_richcompare and our evidence below)

class Foo:
    def __ne__(self, other):
        return NotImplemented
    __eq__ = __ne__

f = Foo()
f2 = Foo()

And the comparisons:

>>> f == f
True
>>> f != f
False
>>> f2 == f
False
>>> f2 != f
True

Performance

Don’t take my word for it, let’s see what’s more performant:

class CLevel:
    "Use default logic programmed in C"

class HighLevelPython:
    def __ne__(self, other):
        return not self == other

class LowLevelPython:
    def __ne__(self, other):
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not equal

def c_level():
    cl = CLevel()
    return lambda: cl != cl

def high_level_python():
    hlp = HighLevelPython()
    return lambda: hlp != hlp

def low_level_python():
    llp = LowLevelPython()
    return lambda: llp != llp

I think these performance numbers speak for themselves:

>>> import timeit
>>> min(timeit.repeat(c_level()))
0.09377292497083545
>>> min(timeit.repeat(high_level_python()))
0.2654011140111834
>>> min(timeit.repeat(low_level_python()))
0.3378178110579029

This makes sense when you consider that low_level_python is doing logic in Python that would otherwise be handled on the C level.

Response to some critics

Another answerer writes:

Aaron Hall’s implementation not self == other of the __ne__ method is incorrect as it can never return NotImplemented (not NotImplemented is False) and therefore the __ne__ method that has priority can never fall back on the __ne__ method that does not have priority.

Having __ne__ never return NotImplemented does not make it incorrect. Instead, we handle prioritization with NotImplemented via the check for equality with ==. Assuming == is correctly implemented, we’re done.

not self == other used to be the default Python 3 implementation of the __ne__ method but it was a bug and it was corrected in Python 3.4 on January 2015, as ShadowRanger noticed (see issue #21408).

Well, let’s explain this.

As noted earlier, Python 3 by default handles __ne__ by first checking if self.__eq__(other) returns NotImplemented (a singleton) – which should be checked for with is and returned if so, else it should return the inverse. Here is that logic written as a class mixin:

class CStyle__ne__:
    """Mixin that provides __ne__ functionality equivalent to 
    the builtin functionality
    """
    def __ne__(self, other):
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not equal

This is necessary for correctness for C level Python API, and it was introduced in Python 3, making

redundant. All relevant __ne__ methods were removed, including ones implementing their own check as well as ones that delegate to __eq__ directly or via == – and == was the most common way of doing so.

Is Symmetry Important?

Our persistent critic provides a pathological example to make the case for handling NotImplemented in __ne__, valuing symmetry above all else. Let’s steel-man the argument with a clear example:

class B:
    """
    this class has no __eq__ implementation, but asserts 
    any instance is not equal to any other object
    """
    def __ne__(self, other):
        return True

class A:
    "This class asserts instances are equivalent to all other objects"
    def __eq__(self, other):
        return True

>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, False, True)

So, by this logic, in order to maintain symmetry, we need to write the complicated __ne__, regardless of Python version.

class B:
    def __ne__(self, other):
        return True

class A:
    def __eq__(self, other):
        return True
    def __ne__(self, other):
        result = other.__eq__(self)
        if result is NotImplemented:
            return NotImplemented
        return not result

>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, True, True)

Apparently we should give no mind that these instances are both equal and not equal.

I propose that symmetry is less important than the presumption of sensible code and following the advice of the documentation.

However, if A had a sensible implementation of __eq__, then we could still follow my direction here and we would still have symmetry:

class B:
    def __ne__(self, other):
        return True

class A:
    def __eq__(self, other):
        return False         # <- this boolean changed... 

>>> A() == B(), B() == A(), A() != B(), B() != A()
(False, False, True, True)

Conclusion

For Python 2 compatible code, use == to implement __ne__. It is more:

  • correct
  • simple
  • performant

In Python 3 only, use the low-level negation on the C level – it is even more simple and performant (though the programmer is responsible for determining that it is correct).

Again, do not write low-level logic in high level Python.


回答 2

仅作记录,一个规范正确且可交叉的Py2 / Py3便携式计算机__ne__看起来像:

import sys

class ...:
    ...
    def __eq__(self, other):
        ...

    if sys.version_info[0] == 2:
        def __ne__(self, other):
            equal = self.__eq__(other)
            return equal if equal is NotImplemented else not equal

这适用于__eq__您可能定义的任何对象:

  • 不像not (self == other),不涉及一些比较烦人/复杂的情况下干扰,其中所涉及的类别之一,并不意味着结果__ne__是一样的结果not__eq__(如SQLAlchemy的的ORM,其中两个__eq____ne__返回特殊的代理对象,没有TrueFalse,并尝试not的结果__eq__将返回False,而不是正确的代理对象)。
  • 不像not self.__eq__(other),这个正确委托给__ne__其他实例的时候self.__eq__回报NotImplementednot self.__eq__(other)将额外错误的,因为NotImplemented是truthy,所以当__eq__不知道如何进行比较,__ne__将返回False,这意味着这两个对象是相等的,而实际上只询问的对象不知道,这意味着不相等的默认值)

如果您__eq__不使用NotImplemented退货,则可以正常工作(无意义的开销),如果NotImplemented有时使用退货,则可以正确处理它。而且,Python版本检查意味着如果该类import在Python 3中为-ed ,则将__ne__保持未定义状态,从而可以接替Python的本机高效后备__ne__实现(上述版本的C版本)


为什么需要这个

Python重载规则

为什么要这样做而不是其他解决方案的解释有些不可思议。Python有一些关于重载运算符的通用规则,尤其是比较运算符:

  1. (适用于所有运算符)运行时LHS OP RHS,请尝试LHS.__op__(RHS),如果返回NotImplemented,请尝试RHS.__rop__(LHS)。exceptions:如果RHS是的类的子LHS类,则RHS.__rop__(LHS) 进行测试。在比较操作符的情况下,__eq____ne__是自己的“ROP” S(所以测试顺序__ne__LHS.__ne__(RHS),那么RHS.__ne__(LHS),逆转如果RHS是的一个子类LHS的类)
  2. 除了“交换”运算符的概念外,运算符之间没有隐含的关系。即使是同一类的实例,LHS.__eq__(RHS)返回True也并不意味着LHS.__ne__(RHS)返回False(实际上,甚至不需要运算符返回布尔值; SQLAlchemy之类的ORM故意不这样做,从而允许更具表达性的查询语法)。从Python 3开始,默认__ne__实现的行为方式是这样的,但是它不是契约性的。您可以__ne__采用与严格相反的方式进行覆盖__eq__

这如何适用于比较器过载

因此,当您使运算符重载时,您有两个工作:

  1. 如果您知道如何自己执行操作,请使用您自己的比较知识来进行操作(绝对不要将其隐式或显式委派给操作的另一侧;这样做可能会导致错误和/或无限递归,取决于您的操作方式)
  2. 如果您知道如何自己实现该操作,请始终返回NotImplemented,以便Python可以委派给另一个操作数的实现。

问题所在 not self.__eq__(other)

def __ne__(self, other):
    return not self.__eq__(other)

从不委托给另一方(如果__eq__正确返回,则是不正确的NotImplemented)。当self.__eq__(other)收益NotImplemented(这是“truthy”),你不返回False,这样A() != something_A_knows_nothing_about的回报False,当它应该检查是否something_A_knows_nothing_about知道如何比较的情况下A,如果没有,就应该已经返回True(如果双方都不知道如何自相比之下,它们被认为是不相等的)。如果A.__eq__执行不正确(返回False而不是NotImplemented当它无法识别另一侧时),那么从A的角度来看,这是“正确的” ,返回True(因为A认为不相等,所以不相等),但是可能错误的something_A_knows_nothing_about的观点,因为它从来没有问过something_A_knows_nothing_aboutA() != something_A_knows_nothing_about结束了True,但something_A_knows_nothing_about != A()可能False返回,或其他任何返回值。

问题所在 not self == other

def __ne__(self, other):
    return not self == other

更微妙。对于99%的类,这将是正确的,包括所有__ne__与的逻辑取反的类__eq__。但是not self == other打破了上述两个规则,这意味着对于__ne__ 不是逻辑逆的类__eq__,结果再次是非对称的,因为永远不会询问其中一个操作数是否可以实现__ne__,即使另一个操作数也可以实现操作数不能。最简单的示例是一个weirdo类,该类False将为所有比较返回,因此A() == Incomparable()A() != Incomparable()两者都返回False。使用正确的实现A.__ne__(一个NotImplemented不知道如何进行比较时返回的关系),关系是对称的。A() != Incomparable()Incomparable() != A()同意结果(因为在前一种情况下,A.__ne__return NotImplemented,然后Incomparable.__ne__return False,而在后一种情况下,直接Incomparable.__ne__返回False)。但是,当A.__ne__实现为时return not self == otherA() != Incomparable()返回True(因为A.__eq__返回,而不是NotImplemented,然后Incomparable.__eq__返回False,并将其A.__ne__反转为True),而Incomparable() != A()返回False.

您可以在此处查看此操作的示例。

显然,一类总是返回False两个__eq____ne__是有点怪。但正如前面所提到的,__eq__并且__ne__甚至不需要返回True/ False; SQLAlchemy ORM具有带有比较器的类,这些类返回用于构建查询的特殊代理对象,而不是True/ 根本不返回False(如果在布尔上下文中进行评估,它们是“真实的”,但永远不应在这样的上下文中对其进行评估)。

由于无法__ne__正确地重载,您破坏该类,如代码所示:

 results = session.query(MyTable).filter(MyTable.fieldname != MyClassWithBadNE())

将起作用(假设SQLAlchemy完全知道如何插入MyClassWithBadNESQL字符串;这可以使用类型适配器来完成,而MyClassWithBadNE无需完全配合),将期望的代理对象传递给filter,而:

 results = session.query(MyTable).filter(MyClassWithBadNE() != MyTable.fieldname)

最终将传递filter一个纯文本False,因为它self == other返回一个代理对象,并且not self == other将真实的代理对象转换为False。希望filter在处理类似的无效参数时引发异常False。尽管我敢肯定,很多人会认为MyTable.fieldname 应该在比较的左手边保持一致,但事实是,在一般情况下,没有程序上的理由来强制执行此操作,并且正确的泛型__ne__将以两种方式return not self == other起作用,而仅能起作用在一种安排中。

Just for the record, a canonically correct and cross Py2/Py3 portable __ne__ would look like:

import sys

class ...:
    ...
    def __eq__(self, other):
        ...

    if sys.version_info[0] == 2:
        def __ne__(self, other):
            equal = self.__eq__(other)
            return equal if equal is NotImplemented else not equal

This works with any __eq__ you might define:

  • Unlike not (self == other), doesn’t interfere with in some annoying/complex cases involving comparisons where one of the classes involved doesn’t imply that the result of __ne__ is the same as the result of not on __eq__ (e.g. SQLAlchemy’s ORM, where both __eq__ and __ne__ return special proxy objects, not True or False, and trying to not the result of __eq__ would return False, rather than the correct proxy object).
  • Unlike not self.__eq__(other), this correctly delegates to the __ne__ of the other instance when self.__eq__ returns NotImplemented (not self.__eq__(other) would be extra wrong, because NotImplemented is truthy, so when __eq__ didn’t know how to perform the comparison, __ne__ would return False, implying that the two objects were equal when in fact the only object asked had no idea, which would imply a default of not equal)

If your __eq__ doesn’t use NotImplemented returns, this works (with meaningless overhead), if it does use NotImplemented sometimes, this handles it properly. And the Python version check means that if the class is import-ed in Python 3, __ne__ is left undefined, allowing Python’s native, efficient fallback __ne__ implementation (a C version of the above) to take over.


Why this is needed

Python overloading rules

The explanation of why you do this instead of other solutions is somewhat arcane. Python has a couple general rules about overloading operators, and comparison operators in particular:

  1. (Applies to all operators) When running LHS OP RHS, try LHS.__op__(RHS), and if that returns NotImplemented, try RHS.__rop__(LHS). Exception: If RHS is a subclass of LHS‘s class, then test RHS.__rop__(LHS) first. In the case of comparison operators, __eq__ and __ne__ are their own “rop”s (so the test order for __ne__ is LHS.__ne__(RHS), then RHS.__ne__(LHS), reversed if RHS is a subclass of LHS‘s class)
  2. Aside from the idea of the “swapped” operator, there is no implied relationship between the operators. Even for instance of the same class, LHS.__eq__(RHS) returning True does not imply LHS.__ne__(RHS) returns False (in fact, the operators aren’t even required to return boolean values; ORMs like SQLAlchemy intentionally do not, allowing for a more expressive query syntax). As of Python 3, the default __ne__ implementation behaves this way, but it’s not contractual; you can override __ne__ in ways that aren’t strict opposites of __eq__.

How this applies to overloading comparators

So when you overload an operator, you have two jobs:

  1. If you know how to implement the operation yourself, do so, using only your own knowledge of how to do the comparison (never delegate, implicitly or explicitly, to the other side of the operation; doing so risks incorrectness and/or infinite recursion, depending on how you do it)
  2. If you don’t know how to implement the operation yourself, always return NotImplemented, so Python can delegate to the other operand’s implementation

The problem with not self.__eq__(other)

def __ne__(self, other):
    return not self.__eq__(other)

never delegates to the other side (and is incorrect if __eq__ properly returns NotImplemented). When self.__eq__(other) returns NotImplemented (which is “truthy”), you silently return False, so A() != something_A_knows_nothing_about returns False, when it should have checked if something_A_knows_nothing_about knew how to compare to instances of A, and if it doesn’t, it should have returned True (since if neither side knows how to compare to the other, they’re considered not equal to one another). If A.__eq__ is incorrectly implemented (returning False instead of NotImplemented when it doesn’t recognize the other side), then this is “correct” from A‘s perspective, returning True (since A doesn’t think it’s equal, so it’s not equal), but it might be wrong from something_A_knows_nothing_about‘s perspective, since it never even asked something_A_knows_nothing_about; A() != something_A_knows_nothing_about ends up True, but something_A_knows_nothing_about != A() could False, or any other return value.

The problem with not self == other

def __ne__(self, other):
    return not self == other

is more subtle. It’s going to be correct for 99% of classes, including all classes for which __ne__ is the logical inverse of __eq__. But not self == other breaks both of the rules mentioned above, which means for classes where __ne__ isn’t the logical inverse of __eq__, the results are once again non-symmetric, because one of the operands is never asked if it can implement __ne__ at all, even if the other operand can’t. The simplest example is a weirdo class which returns False for all comparisons, so A() == Incomparable() and A() != Incomparable() both return False. With a correct implementation of A.__ne__ (one which returns NotImplemented when it doesn’t know how to do the comparison), the relationship is symmetric; A() != Incomparable() and Incomparable() != A() agree on the outcome (because in the former case, A.__ne__ returns NotImplemented, then Incomparable.__ne__ returns False, while in the latter, Incomparable.__ne__ returns False directly). But when A.__ne__ is implemented as return not self == other, A() != Incomparable() returns True (because A.__eq__ returns, not NotImplemented, then Incomparable.__eq__ returns False, and A.__ne__ inverts that to True), while Incomparable() != A() returns False.

You can see an example of this in action here.

Obviously, a class that always returns False for both __eq__ and __ne__ is a little strange. But as mentioned before, __eq__ and __ne__ don’t even need to return True/False; the SQLAlchemy ORM has classes with comparators that returns a special proxy object for query building, not True/False at all (they’re “truthy” if evaluated in a boolean context, but they’re never supposed to be evaluated in such a context).

By failing to overload __ne__ properly, you will break classes of that sort, as the code:

 results = session.query(MyTable).filter(MyTable.fieldname != MyClassWithBadNE())

will work (assuming SQLAlchemy knows how to insert MyClassWithBadNE into a SQL string at all; this can be done with type adapters without MyClassWithBadNE having to cooperate at all), passing the expected proxy object to filter, while:

 results = session.query(MyTable).filter(MyClassWithBadNE() != MyTable.fieldname)

will end up passing filter a plain False, because self == other returns a proxy object, and not self == other just converts the truthy proxy object to False. Hopefully, filter throws an exception on being handled invalid arguments like False. While I’m sure many will argue that MyTable.fieldname should be consistently on the left hand side of the comparison, the fact remains that there is no programmatic reason to enforce this in the general case, and a correct generic __ne__ will work either way, while return not self == other only works in one arrangement.


回答 3

简短答案:是(但请阅读文档以正确完成操作)

ShadowRanger的__ne__方法实现是正确的(并且恰好是__ne__自Python 3.4以来该方法的默认实现):

def __ne__(self, other):
    result = self.__eq__(other)

    if result is not NotImplemented:
        return not result

    return NotImplemented

为什么?因为它保留了重要的数学属性,所以算符的对称性!=。此运算符是二进制的,因此其结果应取决于两个操作数的动态类型,而不仅仅是一个。这是通过针对允许多种调度的编程语言(例如Julia)的双调度实现的。在只允许单调度的Python中,通过在不支持其他操作数类型的实现方法中返回值,对数值方法丰富的比较方法模拟了双调度。然后,解释器将尝试另一个操作数的反射方法。NotImplemented

亚伦·霍尔(Aaron Hall)not self == other__ne__方法的实现是不正确的,因为它消除了!=操作员的对称性。实际上,它永远不会返回NotImplementednot NotImplementedis False),因此__ne__优先级较高的方法永远不会退回到__ne__优先级较低的方法。not self == other曾经是该方法的默认Python 3实现,__ne__但正如ShadowRanger所注意到的那样,它是一个错误,已在2015年1月的Python 3.4中得到纠正(请参见问题#21408)。

比较运算符的实现

Python 3 的Python语言参考在其第三章数据模型中指出:

object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)

这些就是所谓的“丰富比较”方法。运算符和方法名称之间的对应关系如下:x<y调用 x.__lt__(y)x<=y调用x.__le__(y)x==y调用x.__eq__(y)x!=y调用x.__ne__(y)x>y调用x.__gt__(y)x>=y 调用x.__ge__(y)

如果富比较方法NotImplemented未实现给定参数对的操作,则可能返回单例。

这些方法没有交换参数版本(当left参数不支持该操作但right参数支持该操作时使用);相反,__lt__()and __gt__()是彼此的反射,__le__()and __ge__()是彼此的反射,and __eq__()and __ne__()是自己的反射。如果操作数的类型不同,并且右操作数的类型是左操作数类型的直接或间接子类,则右操作数的反射方法具有优先级,否则左操作数的方法具有优先级。不考虑虚拟子类化。

将其转换为Python代码即可(使用operator_eqfor ==operator_nefor !=operator_ltfor <operator_gtfor >operator_lefor <=operator_gefor >=):

def operator_eq(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__eq__(left)

        if result is NotImplemented:
            result = left.__eq__(right)
    else:
        result = left.__eq__(right)

        if result is NotImplemented:
            result = right.__eq__(left)

    if result is NotImplemented:
        result = left is right

    return result


def operator_ne(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__ne__(left)

        if result is NotImplemented:
            result = left.__ne__(right)
    else:
        result = left.__ne__(right)

        if result is NotImplemented:
            result = right.__ne__(left)

    if result is NotImplemented:
        result = left is not right

    return result


def operator_lt(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__gt__(left)

        if result is NotImplemented:
            result = left.__lt__(right)
    else:
        result = left.__lt__(right)

        if result is NotImplemented:
            result = right.__gt__(left)

    if result is NotImplemented:
        raise TypeError(f"'<' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result


def operator_gt(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__lt__(left)

        if result is NotImplemented:
            result = left.__gt__(right)
    else:
        result = left.__gt__(right)

        if result is NotImplemented:
            result = right.__lt__(left)

    if result is NotImplemented:
        raise TypeError(f"'>' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result


def operator_le(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__ge__(left)

        if result is NotImplemented:
            result = left.__le__(right)
    else:
        result = left.__le__(right)

        if result is NotImplemented:
            result = right.__ge__(left)

    if result is NotImplemented:
        raise TypeError(f"'<=' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result


def operator_ge(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__le__(left)

        if result is NotImplemented:
            result = left.__ge__(right)
    else:
        result = left.__ge__(right)

        if result is NotImplemented:
            result = right.__le__(left)

    if result is NotImplemented:
        raise TypeError(f"'>=' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result

比较方法的默认实现

该文档添加:

默认情况下,除非为,否则将结果__ne__()委托__eq__()并反转NotImplemented。比较运算符之间没有其他隐含关系,例如,的真相(x<y or x==y)并不意味着x<=y

的比较方法的缺省的实现(__eq____ne____lt____gt____le____ge__)因此可以由下式给出:

def __eq__(self, other):
    return NotImplemented

def __ne__(self, other):
    result = self.__eq__(other)

    if result is not NotImplemented:
        return not result

    return NotImplemented

def __lt__(self, other):
    return NotImplemented

def __gt__(self, other):
    return NotImplemented

def __le__(self, other):
    return NotImplemented

def __ge__(self, other):
    return NotImplemented

因此,这是该__ne__方法的正确实现。而且它并不总是返回的逆__eq__方法,因为当__eq__方法返回NotImplemented,它的倒数not NotImplementedFalse(因为bool(NotImplemented)True),而不是所期望的NotImplemented

错误的实现 __ne__

正如上文的亚伦·霍尔(Aaron Hall)所展示的,not self.__eq__(other)__ne__方法不是默认的实现。但是也不是not self == other下面通过not self == other在两种情况下将默认实现的行为与实现的行为进行比较来演示后者:

  • __eq__方法返回NotImplemented;
  • __eq__方法返回的值不同于NotImplemented

默认实现

让我们看看当该A.__ne__方法使用默认实现并且该A.__eq__方法返回时会发生什么NotImplemented

class A:
    pass


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) == "B.__ne__"
  1. !=来电A.__ne__
  2. A.__ne__来电A.__eq__
  3. A.__eq__返回NotImplemented
  4. !=来电B.__ne__
  5. B.__ne__返回"B.__ne__"

这表明当A.__eq__方法返回时NotImplemented,该A.__ne__方法将退回到该B.__ne__方法上。

现在,让我们看看当该A.__ne__方法使用默认实现并且该A.__eq__方法返回的值不同于时会发生什么NotImplemented

class A:

    def __eq__(self, other):
        return True


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) is False
  1. !=来电A.__ne__
  2. A.__ne__来电A.__eq__
  3. A.__eq__返回True
  4. !=返回not True,即False

这表明在这种情况下,该A.__ne__方法返回该方法的逆函数A.__eq__。因此,该__ne__方法的行为类似于文档中所宣传的那样。

A.__ne__用上面给出的正确实现覆盖方法的默认实现会产生相同的结果。

not self == other 实施

让我们来看看重写的默认实现时,会发生什么A.__ne__与方法not self == other的实现和A.__eq__方法返回NotImplemented

class A:

    def __ne__(self, other):
        return not self == other


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) is True
  1. !=来电A.__ne__
  2. A.__ne__来电==
  3. ==来电A.__eq__
  4. A.__eq__返回NotImplemented
  5. ==来电B.__eq__
  6. B.__eq__返回NotImplemented
  7. ==返回A() is B(),即False
  8. A.__ne__返回not False,即True

方法的默认实现__ne__返回"B.__ne__",而不是True

现在让我们看看重写的默认实现时,会发生什么A.__ne__与方法not self == other的实现和A.__eq__方法返回从值不同NotImplemented

class A:

    def __eq__(self, other):
        return True

    def __ne__(self, other):
        return not self == other


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) is False
  1. !=来电A.__ne__
  2. A.__ne__来电==
  3. ==来电A.__eq__
  4. A.__eq__返回True
  5. A.__ne__返回not True,即False

在这种情况下,__ne__也会返回该方法的默认实现False

由于此实现无法__ne____eq__方法返回时复制该方法的默认实现的行为NotImplemented,因此是不正确的。

Correct __ne__ implementation

@ShadowRanger’s implementation of the special method __ne__ is the correct one:

def __ne__(self, other):
    result = self.__eq__(other)
    if result is not NotImplemented:
        return not result
    return NotImplemented

It also happens to be the default implementation of the special method __ne__ since Python 3.4, as stated in the Python documentation:

By default, __ne__() delegates to __eq__() and inverts the result unless it is NotImplemented.

Also note that returning the value NotImplemented for unsupported operands is not specific to the special method __ne__. In fact, all the special comparison methods1 and special numeric methods2 should return the value NotImplemented for unsupported operands, as specified in the Python documentation:

NotImplemented

This type has a single value. There is a single object with this value. This object is accessed through the built-in name NotImplemented. Numeric methods and rich comparison methods should return this value if they do not implement the operation for the operands provided. (The interpreter will then try the reflected operation, or some other fallback, depending on the operator.) Its truth value is true.

An example for the special numeric methods is given in the Python documentation:

class MyIntegral(Integral):

    def __add__(self, other):
        if isinstance(other, MyIntegral):
            return do_my_adding_stuff(self, other)
        elif isinstance(other, OtherTypeIKnowAbout):
            return do_my_other_adding_stuff(self, other)
        else:
            return NotImplemented

    def __radd__(self, other):
        if isinstance(other, MyIntegral):
            return do_my_adding_stuff(other, self)
        elif isinstance(other, OtherTypeIKnowAbout):
            return do_my_other_adding_stuff(other, self)
        elif isinstance(other, Integral):
            return int(other) + int(self)
        elif isinstance(other, Real):
            return float(other) + float(self)
        elif isinstance(other, Complex):
            return complex(other) + complex(self)
        else:
            return NotImplemented

1 The special comparison methods: __lt__, __le__, __eq__, __ne__, __gt__ and __ge__.

2 The special numeric methods: __add__, __sub__, __mul__, __matmul__, __truediv__, __floordiv__, __mod__, __divmod__, __pow__, __lshift__, __rshift__, __and__, __xor__, __or__ and their __r*__ reflected and __i*__ in-place counterparts.

Incorrect __ne__ implementation #1

@Falmarri’s implementation of the special method __ne__ is incorrect:

def __ne__(self, other):
    return not self.__eq__(other)

The problem with this implementation is that it does not fall back on the special method __ne__ of the other operand as it never returns the value NotImplemented (the expression not self.__eq__(other) evaluates to the value True or False, including when its subexpression self.__eq__(other) evaluates to the value NotImplemented since the expression bool(NotImplemented) evaluates to the value True). The Boolean evaluation of the value NotImplemented breaks the complement relationship between the comparison operators != and ==:

class Correct:

    def __ne__(self, other):
        result = self.__eq__(other)
        if result is not NotImplemented:
            return not result
        return NotImplemented


class Incorrect:

    def __ne__(self, other):
        return not self.__eq__(other)


x, y = Correct(), Correct()
assert (x != y) is not (x == y)

x, y = Incorrect(), Incorrect()
assert (x != y) is not (x == y)  # AssertionError

Incorrect __ne__ implementation #2

@AaronHall’s implementation of the special method __ne__ is also incorrect:

def __ne__(self, other):
    return not self == other

The problem with this implementation is that it directly falls back on the special method __eq__ of the other operand, bypassing the special method __ne__ of the other operand as it never returns the value NotImplemented (the expression not self == other falls back on the special method __eq__ of the other operand and evaluates to the value True or False). Bypassing a method is incorrect because that method may have side effects like updating the state of the object:

class Correct:

    def __init__(self):
        self.counter = 0

    def __ne__(self, other):
        self.counter += 1
        result = self.__eq__(other)
        if result is not NotImplemented:
            return not result
        return NotImplemented


class Incorrect:

    def __init__(self):
        self.counter = 0

    def __ne__(self, other):
        self.counter += 1
        return not self == other


x, y = Correct(), Correct()
assert x != y
assert x.counter == y.counter

x, y = Incorrect(), Incorrect()
assert x != y
assert x.counter == y.counter  # AssertionError

Understanding comparison operations

In mathematics, a binary relation R over a set X is a set of ordered pairs (xy) in X2. The statement (xy) in R reads “x is R-related to y” and is denoted by xRy.

Properties of a binary relation R over a set X:

  • R is reflexive when for all x in X, xRx.
  • R is irreflexive (also called strict) when for all x in X, not xRx.
  • R is symmetric when for all x and y in X, if xRy then yRx.
  • R is antisymmetric when for all x and y in X, if xRy and yRx then x = y.
  • R is transitive when for all x, y and z in X, if xRy and yRz then xRz.
  • R is connex (also called total) when for all x and y in X, xRy or yRx.
  • R is an equivalence relation when R is reflexive, symmetric and transitive.
    For example, =. However ≠ is only symmetric.
  • R is an order relation when R is reflexive, antisymmetric and transitive.
    For example, ≤ and ≥.
  • R is a strict order relation when R is irreflexive, antisymmetric and transitive.
    For example, < and >. However ≠ is only irreflexive.

Operations on two binary relations R and S over a set X:

  • The converse of R is the binary relation RT = {(yx) | xRy} over X.
  • The complement of R is the binary relation ¬R = {(xy) | not xRy} over X.
  • The union of R and S is the binary relation R ∪ S = {(xy) | xRy or xSy} over X.

Relationships between comparison relations that are always valid:

  • 2 complementary relationships: = and ≠ are each other’s complement;
  • 6 converse relationships: = is the converse of itself, ≠ is the converse of itself, < and > are each other’s converse, and ≤ and ≥ are each other’s converse;
  • 2 union relationships: ≤ is the union < and =, and ≥ is the union of > and =.

Relationships between comparison relations that are only valid for connex orders:

  • 4 complementary relationships: < and ≥ are each other’s complement, and > and ≤ are each other’s complement.

So to correctly implement in Python the comparison operators ==, !=, <, >, <=, and >= corresponding to the comparison relations =, ≠, <, >, ≤, and ≥, all the above mathematical properties and relationships should hold.

A comparison operation x operator y calls the special comparison method __operator__ of the class of one of its operands:

class X:

    def __operator__(self, other):
        # implementation

Since R is reflexive implies xRx, a reflexive comparison operation x operator y (x == y, x <= y and x >= y) or reflexive special comparison method call x.__operator__(y) (x.__eq__(y), x.__le__(y) and x.__ge__(y)) should evaluate to the value True if x and y are identical, that is if the expression x is y evaluates to True. Since R is irreflexive implies not xRx, an irreflexive comparison operation x operator y (x != y, x < y and x > y) or irreflexive special comparison method call x.__operator__(y) (x.__ne__(y), x.__lt__(y) and x.__gt__(y)) should evaluate to the value False if x and y are identical, that is if the expression x is y evaluates to True. The reflexive property is considered by Python for the comparison operator == and associated special comparison method __eq__ but surprisingly not considered for the comparison operators <= and >= and associated special comparison methods __le__ and __ge__, and the irreflexive property is considered by Python for the comparison operator != and associated special comparison method __ne__ but surprisingly not considered for the comparison operators < and > and associated special comparison methods __lt__ and __gt__. The ignored comparison operators instead raise the exception TypeError (and associated special comparison methods instead return the value NotImplemented), as explained in the Python documentation:

The default behavior for equality comparison (== and !=) is based on the identity of the objects. Hence, equality comparison of instances with the same identity results in equality, and equality comparison of instances with different identities results in inequality. A motivation for this default behavior is the desire that all objects should be reflexive (i.e. x is y implies x == y).

A default order comparison (<, >, <=, and >=) is not provided; an attempt raises TypeError. A motivation for this default behavior is the lack of a similar invariant as for equality. [This is incorrect since <= and >= are reflexive like ==, and < and > are irreflexive like !=.]

The class object provides the default implementations of the special comparison methods which are inherited by all its subclasses, as explained in the Python documentation:

object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)

These are the so-called “rich comparison” methods. The correspondence between operator symbols and method names is as follows: x<y calls x.__lt__(y), x<=y calls x.__le__(y), x==y calls x.__eq__(y), x!=y calls x.__ne__(y), x>y calls x.__gt__(y), and x>=y calls x.__ge__(y).

A rich comparison method may return the singleton NotImplemented if it does not implement the operation for a given pair of arguments.

[…]

There are no swapped-argument versions of these methods (to be used when the left argument does not support the operation but the right argument does); rather, __lt__() and __gt__() are each other’s reflection, __le__() and __ge__() are each other’s reflection, and __eq__() and __ne__() are their own reflection. If the operands are of different types, and right operand’s type is a direct or indirect subclass of the left operand’s type, the reflected method of the right operand has priority, otherwise the left operand’s method has priority. Virtual subclassing is not considered.

Since R = (RT)T, a comparison xRy is equivalent to the converse comparison yRTx (informally named “reflected” in the Python documentation). So there are two ways to compute the result of a comparison operation x operator y: calling either x.__operator__(y) or y.__operatorT__(x). Python uses the following computing strategy:

  1. It calls x.__operator__(y) unless the right operand’s class is a descendant of the left operand’s class, in which case it calls y.__operatorT__(x) (allowing classes to override their ancestors’ converse special comparison method).
  2. If the operands x and y are unsupported (indicated by the return value NotImplemented), it calls the converse special comparison method as a 1st fallback.
  3. If the operands x and y are unsupported (indicated by the return value NotImplemented), it raises the exception TypeError except for the comparison operators == and != for which it tests respectively the identity and non-identity of the operands x and y as a 2nd fallback (leveraging the reflexivity property of == and irreflexivity property of !=).
  4. It returns the result.

In CPython this is implemented in C code, which can be translated into Python code (with the names eq for ==, ne for !=, lt for <, gt for >, le for <= and ge for >=):

def eq(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__eq__(left)
        if result is NotImplemented:
            result = left.__eq__(right)
    else:
        result = left.__eq__(right)
        if result is NotImplemented:
            result = right.__eq__(left)
    if result is NotImplemented:
        result = left is right
    return result
def ne(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__ne__(left)
        if result is NotImplemented:
            result = left.__ne__(right)
    else:
        result = left.__ne__(right)
        if result is NotImplemented:
            result = right.__ne__(left)
    if result is NotImplemented:
        result = left is not right
    return result
def lt(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__gt__(left)
        if result is NotImplemented:
            result = left.__lt__(right)
    else:
        result = left.__lt__(right)
        if result is NotImplemented:
            result = right.__gt__(left)
    if result is NotImplemented:
        raise TypeError(
            f"'<' not supported between instances of '{type(left).__name__}' "
            f"and '{type(right).__name__}'"
        )
    return result
def gt(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__lt__(left)
        if result is NotImplemented:
            result = left.__gt__(right)
    else:
        result = left.__gt__(right)
        if result is NotImplemented:
            result = right.__lt__(left)
    if result is NotImplemented:
        raise TypeError(
            f"'>' not supported between instances of '{type(left).__name__}' "
            f"and '{type(right).__name__}'"
        )
    return result
def le(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__ge__(left)
        if result is NotImplemented:
            result = left.__le__(right)
    else:
        result = left.__le__(right)
        if result is NotImplemented:
            result = right.__ge__(left)
    if result is NotImplemented:
        raise TypeError(
            f"'<=' not supported between instances of '{type(left).__name__}' "
            f"and '{type(right).__name__}'"
        )
    return result
def ge(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__le__(left)
        if result is NotImplemented:
            result = left.__ge__(right)
    else:
        result = left.__ge__(right)
        if result is NotImplemented:
            result = right.__le__(left)
    if result is NotImplemented:
        raise TypeError(
            f"'>=' not supported between instances of '{type(left).__name__}' "
            f"and '{type(right).__name__}'"
        )
    return result

Since R = ¬(¬R), a comparison xRy is equivalent to the complement comparison ¬(x¬Ry). ≠ is the complement of =, so the special method __ne__ is implemented in terms of the special method __eq__ for supported operands by default, while the other special comparison methods are implemented independently by default (the fact that ≤ is the union of < and =, and ≥ is the union of > and = is surprisingly not considered, which means that currently the special methods __le__ and __ge__ should be user implemented), as explained in the Python documentation:

By default, __ne__() delegates to __eq__() and inverts the result unless it is NotImplemented. There are no other implied relationships among the comparison operators, for example, the truth of (x<y or x==y) does not imply x<=y.

In CPython this is implemented in C code, which can be translated into Python code:

def __eq__(self, other):
    return self is other or NotImplemented
def __ne__(self, other):
    result = self.__eq__(other)
    if result is not NotImplemented:
        return not result
    return NotImplemented
def __lt__(self, other):
    return NotImplemented
def __gt__(self, other):
    return NotImplemented
def __le__(self, other):
    return NotImplemented
def __ge__(self, other):
    return NotImplemented

So by default:

  • a comparison operation x operator y raises the exception TypeError except for the comparison operators == and != for which it returns respectively the identity and non-identity of the operands x and y;
  • a special comparison method call x.__operator__(y) returns the value NotImplemented except for the special comparison methods __eq__ and __ne__ for which it returns respectively True and False if the operands x and y are respectively identical and non-identical and the value NotImplemented otherwise.

回答 4

如果所有的__eq____ne____lt____ge____le__,和__gt__为Class意义,那么就实现__cmp__代替。否则,由于Daniel DiPaolo所说的话,请按照您的方式做(在我进行测试而不是查找时;))

If all of __eq__, __ne__, __lt__, __ge__, __le__, and __gt__ make sense for the class, then just implement __cmp__ instead. Otherwise, do as you’re doing, because of the bit Daniel DiPaolo said (while I was testing it instead of looking it up ;) )


“和”和“或”如何与非布尔值一起使用?

问题:“和”和“或”如何与非布尔值一起使用?

我正在尝试学习python,并遇到了一些不错的代码,虽然简短但并不完全有意义

上下文是:

def fn(*args):
    return len(args) and max(args)-min(args)

我知道它在做什么,但是python为什么要这样做-即返回值而不是True / False?

10 and 7-2

返回5。同样,将和更改为或将导致功能更改。所以

10 or 7 - 2

将返回10。

这是合法/可靠的风格,还是有任何陷阱?

I’m trying to learn python and came across some code that is nice and short but doesn’t totally make sense

the context was:

def fn(*args):
    return len(args) and max(args)-min(args)

I get what it’s doing, but why does python do this – ie return the value rather than True/False?

10 and 7-2

returns 5. Similarly, changing the and to or will result in a change in functionality. So

10 or 7 - 2

Would return 10.

Is this legit/reliable style, or are there any gotchas on this?


回答 0

TL; DR

我们首先总结两个逻辑运算符and和的两个行为or。这些习语将构成我们下面讨论的基础。

and

如果有,则返回第一个Falsy值,否则返回表达式中的最后一个值。

or

如果有,则返回第一个Truthy值,否则返回表达式中的最后一个值。

行为也总结在docs中,尤其是在此表中:

无论其操作数如何,返回布尔值的唯一运算符是该not运算符。


“真实性”和“真实”评估

该声明

len(args) and max(args) - min(args)

是一种非常 Python式的简洁(并且可能可读性较低)的说法,即“如果args不为空,则返回结果max(args) - min(args)”,否则返回0。通常,它是if-else表达式的更简洁表示。例如,

exp1 and exp2

应该(大致)翻译为:

r1 = exp1
if r1:
    r1 = exp2

或者,等效地,

r1 = exp1 if exp1 else exp2

同样,

exp1 or exp2

相当于

r1 = exp1
if not r1:
    r1 = exp2

其中exp1exp2是任意的python对象,或返回某些对象的表达式。在这里理解逻辑andor运算符用法的关键是要理解它们不限于对布尔值进行操作或返回布尔值。任何具有真实性值的对象都可以在此处进行测试。这包括intstrlistdicttuplesetNoneType,和用户定义的对象。短路规则也同样适用。

但是什么是真实性?
它指的是在条件表达式中使用对象时如何求值。@Patrick Haugh在这篇文章中很好地总结了真实性。

除以下值“ falsy”外,所有值均被视为“真实”值:

  • None
  • False
  • 0
  • 0.0
  • 0j
  • Decimal(0)
  • Fraction(0, 1)
  • [] -一个空的 list
  • {} -一个空的 dict
  • () -一个空的 tuple
  • '' -一个空的 str
  • b'' -一个空的 bytes
  • set() -一个空的 set
  • 一个空的range,像range(0)
  • 为其对象
    • obj.__bool__() 退货 False
    • obj.__len__() 退货 0

“真实的”值将满足ifwhile 语句执行的检查。我们使用“真实的”和“虚假的”来区别 boolTrueFalse


如何and运作

我们以OP的问题为切入点,讨论在这些情况下如何操作这些运算符。

给定一个带有定义的函数

def foo(*args):
    ...

如何返回零个或多个参数列表中的最小值和最大值之间的差?

找到最小值和最大值很容易(使用内置函数!)。这里唯一的障碍是适当地处理参数列表可能为空的极端情况(例如,调用foo())。多亏and操作员,我们可以在一行中完成这两项操作:

def foo(*args):
     return len(args) and max(args) - min(args)

foo(1, 2, 3, 4, 5)
# 4

foo()
# 0

由于and使用,如果第二个表达式为,则也必须对其求值True。请注意,如果第一个表达式的值为真,则返回值始终第二个表达式的结果。如果第一个表达式的计算结果为Falsy,则返回的结果为第一个表达式的结果。

在上面的函数中,如果foo接收到一个或多个参数,len(args)则大于0(一个正数),因此返回的结果为max(args) - min(args)。OTOH,如果没有参数传递,len(args)0这是Falsy,并0返回。

请注意,编写此函数的另一种方法是:

def foo(*args):
    if not len(args):
        return 0

    return max(args) - min(args)

或者,更简而言之,

def foo(*args):
    return 0 if not args else max(args) - min(args)

当然,如果这些功能都不执行任何类型检查,那么除非您完全信任所提供的输入,否则不要依赖这些结构的简单性。


如何or运作

or用一个人为的例子以类似的方式解释了它的工作。

给定一个带有定义的函数

def foo(*args):
    ...

您将如何完成foo所有数字的归还9000

我们or这里用来处理拐角处的情况。我们定义foo为:

def foo(*args):
     return [x for x in args if x > 9000] or 'No number over 9000!'

foo(9004, 1, 2, 500)
# [9004]

foo(1, 2, 3, 4)
# 'No number over 9000!'

foo对列表执行过滤,以保留上的所有数字9000。如果存在任何这样的数字,则列表理解的结果是一个非空列表,该列表为Truthy,因此将其返回(此处发生短路)。如果不存在这样的数字,则list comp的结果[]为Falsy。因此,现在对第二个表达式求值(一个非空字符串)并返回。

使用条件,我们可以将该函数重写为:

def foo(*args):
    r = [x for x in args if x > 9000]
    if not r:
        return 'No number over 9000!' 

    return r

和以前一样,此结构在错误处理方面更加灵活。

TL;DR

We start by summarising the two behaviour of the two logical operators and and or. These idioms will form the basis of our discussion below.

and

Return the first Falsy value if there are any, else return the last value in the expression.

or

Return the first Truthy value if there are any, else return the last value in the expression.

The behaviour is also summarised in the docs, especially in this table:

The only operator returning a boolean value regardless of its operands is the not operator.


“Truthiness”, and “Truthy” Evaluations

The statement

len(args) and max(args) - min(args)

Is a very pythonic concise (and arguably less readable) way of saying “if args is not empty, return the result of max(args) - min(args)“, otherwise return 0. In general, it is a more concise representation of an if-else expression. For example,

exp1 and exp2

Should (roughly) translate to:

r1 = exp1
if r1:
    r1 = exp2

Or, equivalently,

r1 = exp2 if exp1 else exp1

Similarly,

exp1 or exp2

Should (roughly) translate to:

r1 = exp1
if not r1:
    r1 = exp2

Or, equivalently,

r1 = exp1 if exp1 else exp2

Where exp1 and exp2 are arbitrary python objects, or expressions that return some object. The key to understanding the uses of the logical and and or operators here is understanding that they are not restricted to operating on, or returning boolean values. Any object with a truthiness value can be tested here. This includes int, str, list, dict, tuple, set, NoneType, and user defined objects. Short circuiting rules still apply as well.

But what is truthiness?
It refers to how objects are evaluated when used in conditional expressions. @Patrick Haugh summarises truthiness nicely in this post.

All values are considered “truthy” except for the following, which are “falsy”:

  • None
  • False
  • 0
  • 0.0
  • 0j
  • Decimal(0)
  • Fraction(0, 1)
  • [] – an empty list
  • {} – an empty dict
  • () – an empty tuple
  • '' – an empty str
  • b'' – an empty bytes
  • set() – an empty set
  • an empty range, like range(0)
  • objects for which
    • obj.__bool__() returns False
    • obj.__len__() returns 0

A “truthy” value will satisfy the check performed by if or while statements. We use “truthy” and “falsy” to differentiate from the bool values True and False.


How and Works

We build on OP’s question as a segue into a discussion on how these operators in these instances.

Given a function with the definition

def foo(*args):
    ...

How do I return the difference between the minimum and maximum value in a list of zero or more arguments?

Finding the minimum and maximum is easy (use the inbuilt functions!). The only snag here is appropriately handling the corner case where the argument list could be empty (for example, calling foo()). We can do both in a single line thanks to the and operator:

def foo(*args):
     return len(args) and max(args) - min(args)
foo(1, 2, 3, 4, 5)
# 4

foo()
# 0

Since and is used, the second expression must also be evaluated if the first is True. Note that, if the first expression is evaluated to be truthy, the return value is always the result of the second expression. If the first expression is evaluated to be Falsy, then the result returned is the result of the first expression.

In the function above, If foo receives one or more arguments, len(args) is greater than 0 (a positive number), so the result returned is max(args) - min(args). OTOH, if no arguments are passed, len(args) is 0 which is Falsy, and 0 is returned.

Note that an alternative way to write this function would be:

def foo(*args):
    if not len(args):
        return 0
    
    return max(args) - min(args)

Or, more concisely,

def foo(*args):
    return 0 if not args else max(args) - min(args)

If course, none of these functions perform any type checking, so unless you completely trust the input provided, do not rely on the simplicity of these constructs.


How or Works

I explain the working of or in a similar fashion with a contrived example.

Given a function with the definition

def foo(*args):
    ...

How would you complete foo to return all numbers over 9000?

We use or to handle the corner case here. We define foo as:

def foo(*args):
     return [x for x in args if x > 9000] or 'No number over 9000!'

foo(9004, 1, 2, 500)
# [9004]

foo(1, 2, 3, 4)
# 'No number over 9000!'

foo performs a filtration on the list to retain all numbers over 9000. If there exist any such numbers, the result of the list comprehension is a non-empty list which is Truthy, so it is returned (short circuiting in action here). If there exist no such numbers, then the result of the list comp is [] which is Falsy. So the second expression is now evaluated (a non-empty string) and is returned.

Using conditionals, we could re-write this function as,

def foo(*args):
    r = [x for x in args if x > 9000]
    if not r:
        return 'No number over 9000!' 
    
    return r

As before, this structure is more flexible in terms of error handling.


回答 1

Python Docs报价

注意,既不and也不or 限制它们返回到和的类型,而是返回最后一个求值的参数。有时这很有用,例如,如果为空则应替换为默认值的字符串,则表达式会产生所需的值。FalseTruess or 'foo'

因此,这就是设计Python评估布尔表达式的方式,并且上述文档使我们了解了为什么要这样做。

要获得布尔值,只需对其进行类型转换。

return bool(len(args) and max(args)-min(args))

为什么?

短路。

例如:

2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too
0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all

同样or也是如此,也就是说,它将在找到表达式后立即返回Truthy表达式,因为评估表达式的其余部分是多余的。

而不是返回铁杆True或者False,Python的回报TruthyFalsey,这是无论如何要评估为TrueFalse。您可以按原样使用该表达式,它将仍然有效。


要知道什么是TruthyFalsey,检查帕特里克·哈夫的答案

Quoting from Python Docs

Note that neither and nor or restrict the value and type they return to False and True, but rather return the last evaluated argument. This is sometimes useful, e.g., if s is a string that should be replaced by a default value if it is empty, the expression s or 'foo' yields the desired value.

So, this is how Python was designed to evaluate the boolean expressions and the above documentation gives us an insight of why they did it so.

To get a boolean value just typecast it.

return bool(len(args) and max(args)-min(args))

Why?

Short-circuiting.

For example:

2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too
0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all

The same goes for or too, that is, it will return the expression which is Truthy as soon as it finds it, cause evaluating the rest of the expression is redundant.

Instead of returning hardcore True or False, Python returns Truthy or Falsey, which are anyway going to evaluate to True or False. You could use the expression as is, and it will still work.


To know what’s Truthy and Falsey, check Patrick Haugh’s answer


回答 2

执行布尔逻辑,但它们在比较时返回实际值之一。使用和时,值在布尔上下文中从左到右求值。0,”,[],(),{}None在布尔上下文中为false;其他一切都是真的。

如果所有的值在布尔上下文是真实的,并且返回最后一个值。

>>> 2 and 5
5
>>> 2 and 5 and 10
10

如果任何值在布尔上下文中为false ,则返回第一个false值。

>>> '' and 5
''
>>> 2 and 0 and 5
0

所以代码

return len(args) and max(args)-min(args)

返回max(args)-min(args)存在args时的值, 否则返回len(args)0。

and and or perform boolean logic, but they return one of the actual values when they are comparing. When using and, values are evaluated in a boolean context from left to right. 0, ”, [], (), {}, and None are false in a boolean context; everything else is true.

If all values are true in a boolean context, and returns the last value.

>>> 2 and 5
5
>>> 2 and 5 and 10
10

If any value is false in a boolean context and returns the first false value.

>>> '' and 5
''
>>> 2 and 0 and 5
0

So the code

return len(args) and max(args)-min(args)

returns the value of max(args)-min(args) when there is args else it returns len(args) which is 0.


回答 3

这是合法/可靠的风格,还是有任何陷阱?

这是合法的,这是短路评估,返回最后一个值。

您提供了一个很好的例子。0如果不传递任何参数,该函数将返回,并且代码不必检查是否传递无参数的特殊情况。

使用此方法的另一种方法是将None参数默认设置为可变基元,例如空列表:

def fn(alist=None):
    alist = alist or []
    ....

如果将一些非真实的值传递给alist它,则默认为空列表,这是避免if语句和可变的默认参数陷阱的便捷方法

Is this legit/reliable style, or are there any gotchas on this?

This is legit, it is a short circuit evaluation where the last value is returned.

You provide a good example. The function will return 0 if no arguments are passed, and the code doesn’t have to check for a special case of no arguments passed.

Another way to use this, is to default None arguments to a mutable primitive, like an empty list:

def fn(alist=None):
    alist = alist or []
    ....

If some non-truthy value is passed to alist it defaults to an empty list, handy way to avoid an if statement and the mutable default argument pitfall


回答 4

陷阱

是的,有一些陷阱。

fn() == fn(3) == fn(4, 4)

首先,如果fnreturn 0,则无法知道是否在没有任何参数,一个参数或多个相等参数的情况下调用它:

>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0

什么fn意思

那么,Python是一种动态语言。在任何地方都没有指定它的fn作用,输入应为什么以及输出应为什么样。因此,正确命名函数真的很重要。同样,不必调用参数argsdelta(*numbers)或者calculate_range(*numbers)可以更好地描述该功能应该做什么。

参数错误

最后,and如果没有任何参数调用逻辑运算符,则可以防止该函数失败。但是,如果某些参数不是数字,它将仍然失败:

>>> fn('1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>> fn(1, '2')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: '>' not supported between instances of 'str' and 'int'
>>> fn('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'

可能的选择

这是一种根据“比请求更容易获得宽恕”来编写函数的方法原理

def delta(*numbers):
    try:
        return max(numbers) - min(numbers)
    except TypeError:
        raise ValueError("delta should only be called with numerical arguments") from None
    except ValueError:
        raise ValueError("delta should be called with at least one numerical argument") from None

举个例子:

>>> delta()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in delta
ValueError: delta should be called with at least one numerical argument
>>> delta(3)
0
>>> delta('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta(3, 4.5)
1.5
>>> delta(3, 5, 7, 2)
5

如果您确实不希望在delta不带任何参数的情况下被调用时引发异常,则可以返回一些否则无法实现的值(例如-1None):

>>> def delta(*numbers):
...     try:
...         return max(numbers) - min(numbers)
...     except TypeError:
...         raise ValueError("delta should only be called with numerical arguments") from None
...     except ValueError:
...         return -1 # or None
... 
>>> 
>>> delta()
-1

Gotchas

Yes, there are a few gotchas.

fn() == fn(3) == fn(4, 4)

First, if fn returns 0, you cannot know if it was called without any parameter, with one parameter or with multiple, equal parameters :

>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0

What does fn mean?

Then, Python is a dynamic language. It’s not specified anywhere what fn does, what its input should be and what its output should look like. Therefore, it’s really important to name the function correctly. Similarly, arguments don’t have to be called args. delta(*numbers) or calculate_range(*numbers) might describe better what the function is supposed to do.

Argument errors

Finally, the logical and operator is supposed to prevent the function to fail if called without any argument. It still fails if some argument isn’t a number, though:

>>> fn('1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>> fn(1, '2')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: '>' not supported between instances of 'str' and 'int'
>>> fn('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'

Possible alternative

Here’s a way to write the function according to the “Easier to ask for forgiveness than permission.” principle:

def delta(*numbers):
    try:
        return max(numbers) - min(numbers)
    except TypeError:
        raise ValueError("delta should only be called with numerical arguments") from None
    except ValueError:
        raise ValueError("delta should be called with at least one numerical argument") from None

As an example:

>>> delta()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in delta
ValueError: delta should be called with at least one numerical argument
>>> delta(3)
0
>>> delta('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta(3, 4.5)
1.5
>>> delta(3, 5, 7, 2)
5

If you really don’t want to raise an exception when delta is called without any argument, you could return some value which cannot be possible otherwise (e.g. -1 or None):

>>> def delta(*numbers):
...     try:
...         return max(numbers) - min(numbers)
...     except TypeError:
...         raise ValueError("delta should only be called with numerical arguments") from None
...     except ValueError:
...         return -1 # or None
... 
>>> 
>>> delta()
-1

回答 5

这是合法/可靠的风格,还是有任何陷阱?

我想补充一下这个问题,它不仅合法可靠,而且非常实用。这是一个简单的示例:

>>>example_list = []
>>>print example_list or 'empty list'
empty list

因此,您可以真正利用它来发挥自己的优势。为了简明扼要,这是我的看法:

Or 算子

Python的or运算符返回第一个Truth-y值或最后一个值,然后停止

And 算子

Python的and运算符返回第一个False-y值或最后一个值,然后停止

幕后花絮

在python中,所有数字都被解释True为0以外的数字。因此,请说:

0 and 10 

是相同的:

False and True

这显然是False。因此,逻辑上返回0

Is this legit/reliable style, or are there any gotchas on this?

I would like to add to this question that it not only legit and reliable but it also ultra practical. Here is a simple example:

>>>example_list = []
>>>print example_list or 'empty list'
empty list

Therefore you can really use it at your advantage. In order to be conscise this is how I see it:

Or operator

Python’s or operator returns the first Truth-y value, or the last value, and stops

And operator

Python’s and operator returns the first False-y value, or the last value, and stops

Behind the scenes

In python, all numbers are interpreted as True except for 0. Therefore, saying:

0 and 10 

is the same as:

False and True

Which is clearly False. It is therefore logical that it returns 0


回答 6

是。这是和比较的正确行为。

至少在Python,A and B返回B如果A基本上True包括:如果A不为空,NOT None不是空的容器(如一个空的listdict等)。A返回的IFF A本质上是Falseor None或Empty或Null。

在另一方面,A or B返回A如果A基本上True包括:如果A不为空,NOT None不是空的容器(如一个空的listdict等),否则返回B

不容易注意到(或忽略)此行为,因为在Python中,任何non-null非空对象的求值为True都被视为布尔值。

例如,以下所有内容将打印“ True”

if [102]: 
    print "True"
else: 
    print "False"

if "anything that is not empty or None": 
    print "True"
else: 
    print "False"

if {1, 2, 3}: 
    print "True"
else: 
    print "False"

另一方面,以下所有内容将打印“ False”

if []: 
    print "True"
else: 
    print "False"

if "": 
    print "True"
else: 
    print "False"

if set ([]): 
    print "True"
else: 
    print "False"

Yes. This is the correct behaviour of and comparison.

At least in Python, A and B returns B if A is essentially True including if A is NOT Null, NOT None NOT an Empty container (such as an empty list, dict, etc). A is returned IFF A is essentially False or None or Empty or Null.

On the other hand, A or B returns A if A is essentially True including if A is NOT Null, NOT None NOT an Empty container (such as an empty list, dict, etc), otherwise it returns B.

It is easy to not notice (or to overlook) this behaviour because, in Python, any non-null non-empty object evaluates to True is treated like a boolean.

For example, all the following will print “True”

if [102]: 
    print "True"
else: 
    print "False"

if "anything that is not empty or None": 
    print "True"
else: 
    print "False"

if {1, 2, 3}: 
    print "True"
else: 
    print "False"

On the other hand, all the following will print “False”

if []: 
    print "True"
else: 
    print "False"

if "": 
    print "True"
else: 
    print "False"

if set ([]): 
    print "True"
else: 
    print "False"

回答 7

以简单的方式理解

与: if first_val is False return first_val else second_value

例如:

1 and 2 # here it will return 2 because 1 is not False

但,

0 and 2 # will return 0 because first value is 0 i.e False

=>如果任何人为假,它将为假。如果两个都成立,那么只有它会成立

要么 : if first_val is False return second_val else first_value

原因是,如果first为false,则检查2是否为true。

例如:

1 or 2 # here it will return 1 because 1 is not False

但,

0 or 2 # will return 2 because first value is 0 i.e False

或=>如果任何人为假,则为true。因此,如果第一个值是假,那么无论假设哪个是2值。因此它会返回第二个值。

如果任何人是真实的,那么它将成为真实。如果两者均为假,那么它将变为假。

to understand in simple way,

AND : if first_val is False return first_val else second_value

eg:

1 and 2 # here it will return 2 because 1 is not False

but,

0 and 2 # will return 0 because first value is 0 i.e False

and => if anyone false, it will be false. if both are true then only it will become true

OR : if first_val is False return second_val else first_value

reason is, if first is false it check whether 2 is true or not.

eg:

1 or 2 # here it will return 1 because 1 is not False

but,

0 or 2 # will return 2 because first value is 0 i.e False

or => if anyone false, it will be true. so if first value is false no matter what 2 value suppose to be. so it returns second value what ever it can be.

if anyone is true then it will become true. if both are false then it will become false.


“ x不在y”或“ x不在y”

问题:“ x不在y”或“ x不在y”

在测试成员资格时,我们可以使用:

x not in y

或者:

not x in y

取决于x和,此表达式可能有许多可能的上下文y。例如,它可能用于子字符串检查,列表成员资格,字典键存在。

  • 两种形式总是相等的吗?
  • 有首选的语法吗?

When testing for membership, we can use:

x not in y

Or alternatively:

not x in y

There can be many possible contexts for this expression depending on x and y. It could be for a substring check, list membership, dict key existence, for example.

  • Are the two forms always equivalent?
  • Is there a preferred syntax?

回答 0

他们总是给出相同的结果。

实际上,not 'ham' in 'spam and eggs'在特殊情况下,执行单个“非输入”操作而不是“输入”操作,然后取反:

>>> import dis

>>> def notin():
    'ham' not in 'spam and eggs'
>>> dis.dis(notin)
  2           0 LOAD_CONST               1 ('ham')
              3 LOAD_CONST               2 ('spam and eggs')
              6 COMPARE_OP               7 (not in)
              9 POP_TOP             
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE    

>>> def not_in():
    not 'ham' in 'spam and eggs'
>>> dis.dis(not_in)
  2           0 LOAD_CONST               1 ('ham')
              3 LOAD_CONST               2 ('spam and eggs')
              6 COMPARE_OP               7 (not in)
              9 POP_TOP             
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE    

>>> def not__in():
    not ('ham' in 'spam and eggs')
>>> dis.dis(not__in)
  2           0 LOAD_CONST               1 ('ham')
              3 LOAD_CONST               2 ('spam and eggs')
              6 COMPARE_OP               7 (not in)
              9 POP_TOP             
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE        

>>> def noteq():
    not 'ham' == 'spam and eggs'
>>> dis.dis(noteq)
  2           0 LOAD_CONST               1 ('ham')
              3 LOAD_CONST               2 ('spam and eggs')
              6 COMPARE_OP               2 (==)
              9 UNARY_NOT           
             10 POP_TOP             
             11 LOAD_CONST               0 (None)
             14 RETURN_VALUE      

起初我以为它们总是给出相同的结果,但是not它本身只是一个低优先级的逻辑否定运算符,可以a in b像其他布尔表达式一样容易地应用,而not in为了方便和清楚起见,它是一个单独的运算符。

上面的拆卸很明显!not显然,虽然显然是逻辑取反运算符,但该窗体not a in b是特殊情况,因此实际上并未使用通用运算符。这not a in b实际上使表达式与相同a not in b,而不仅仅是产生相同值的表达式。

They always give the same result.

In fact, not 'ham' in 'spam and eggs' appears to be special cased to perform a single “not in” operation, rather than an “in” operation and then negating the result:

>>> import dis

>>> def notin():
    'ham' not in 'spam and eggs'
>>> dis.dis(notin)
  2           0 LOAD_CONST               1 ('ham')
              3 LOAD_CONST               2 ('spam and eggs')
              6 COMPARE_OP               7 (not in)
              9 POP_TOP             
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE    

>>> def not_in():
    not 'ham' in 'spam and eggs'
>>> dis.dis(not_in)
  2           0 LOAD_CONST               1 ('ham')
              3 LOAD_CONST               2 ('spam and eggs')
              6 COMPARE_OP               7 (not in)
              9 POP_TOP             
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE    

>>> def not__in():
    not ('ham' in 'spam and eggs')
>>> dis.dis(not__in)
  2           0 LOAD_CONST               1 ('ham')
              3 LOAD_CONST               2 ('spam and eggs')
              6 COMPARE_OP               7 (not in)
              9 POP_TOP             
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE        

>>> def noteq():
    not 'ham' == 'spam and eggs'
>>> dis.dis(noteq)
  2           0 LOAD_CONST               1 ('ham')
              3 LOAD_CONST               2 ('spam and eggs')
              6 COMPARE_OP               2 (==)
              9 UNARY_NOT           
             10 POP_TOP             
             11 LOAD_CONST               0 (None)
             14 RETURN_VALUE      

I had thought at first that they always gave the same result, but that not on its own was simply a low precedence logical negation operator, which could be applied to a in b just as easily as any other boolean expression, whereas not in was a separate operator for convenience and clarity.

The disassembly above was revealing! It seems that while not obviously is a logical negation operator, the form not a in b is special cased so that it’s not actually using the general operator. This makes not a in b literally the same expression as a not in b, rather than merely an expression that results in the same value.


回答 1

  1. 不,没有区别。

    运算符not in被定义为具有的反真值in

    Python文档

  2. 我认为not in它是首选的,因为它更明显,他们为此添加了特殊情况。

  1. No, there is no difference.

    The operator not in is defined to have the inverse true value of in.

    Python documentation

  2. I would assume not in is preferred because it is more obvious and they added a special case for it.

回答 2

它们的含义相同,但是pycodestyle Python样式指南检查器(以前称为pep8)更喜欢规则E713中not in运算符:

E713:会员资格测试应为 not in

另请参见“ Python if x is not Noneif not x is None?” 选择非常相似的样式

They are identical in meaning, but the pycodestyle Python style guide checker (formerly called pep8) prefers the not in operator in rule E713:

E713: test for membership should be not in

See also “Python if x is not None or if not x is None?” for a very similar choice of style.


回答 3

其他人已经非常清楚地表明,这两个语句在相当低的水平上是等效的。

但是,我认为尚未有人对此施加过足够的压力,因为这让您自己选择,您应该

选择使代码尽可能可读的形式。

并且不一定对任何人都可读,即使这当然是一个不错的目标。不,请确保该代码对您来说尽可能易读,因为您是最有可能在稍后返回此代码并尝试阅读该代码的人。

Others have already made it very clear that the two statements are, down to a quite low level, equivalent.

However, I don’t think that anyone yet has stressed enough that since this leaves the choice up to you, you should

choose the form that makes your code as readable as possible.

And not necessarily as readable as possible to anyone, even if that’s of course a nice thing to aim for. No, make sure the code is as readable as possible to you, since you are the one who is the most likely to come back to this code later and try to read it.


回答 4

在Python中,没有区别。而且没有偏好。

In Python, there is no difference. And there is no preference.


回答 5

从句法上讲,它们是相同的陈述。我会很快指出,它'ham' not in 'spam and eggs'传达了更清晰的意图,但是我已经看到not 'ham' in 'spam and eggs'了比其他传达了更清晰含义的代码和方案。

Syntactically they’re the same statement. I would be quick to state that 'ham' not in 'spam and eggs' conveys clearer intent, but I’ve seen code and scenarios in which not 'ham' in 'spam and eggs' conveys a clearer meaning than the other.


了解Python的“ is”运算符

问题:了解Python的“ is”运算符

is运营商不匹配变量的值,但这些实例本身。

到底是什么意思

我声明了两个变量,xy在两个变量中分配了相同的值,但是当我使用is运算符时,它返回false 。

我需要澄清。这是我的代码。

x = [1, 2, 3]
y = [1, 2, 3]

print(x is y)  # It prints false!

The is operator does not match the values of the variables, but the instances themselves.

What does it really mean?

I declared two variables named x and y assigning the same values in both variables, but it returns false when I use the is operator.

I need a clarification. Here is my code.

x = [1, 2, 3]
y = [1, 2, 3]

print(x is y)  # It prints false!

回答 0

您误解了is操作员的测试内容。它测试两个变量是否指向同一对象,而不是两个变量具有相同的值。

is操作员文档中:

运算符isis not对象标识测试:x is y当且仅当xy是相同对象时为true 。

改用==运算符:

print(x == y)

打印Truex并且y是两个单独的列表:

x[0] = 4
print(y)  # prints [1, 2, 3]
print(x == y)   # prints False

如果使用该id()函数,您将看到xy具有不同的标识符:

>>> id(x)
4401064560
>>> id(y)
4401098192

但是如果要分配给yx则它们都指向同一个对象:

>>> x = y
>>> id(x)
4401064560
>>> id(y)
4401064560
>>> x is y
True

is显示两者是同一个对象,则返回True

请记住,在Python中,名称只是引用值的标签;您可以有多个名称指向同一个对象。is告诉您两个名称是否指向一个相同的对象。==告诉您两个名称是否引用具有相同值的对象。

You misunderstood what the is operator tests. It tests if two variables point the same object, not if two variables have the same value.

From the documentation for the is operator:

The operators is and is not test for object identity: x is y is true if and only if x and y are the same object.

Use the == operator instead:

print(x == y)

This prints True. x and y are two separate lists:

x[0] = 4
print(y)  # prints [1, 2, 3]
print(x == y)   # prints False

If you use the id() function you’ll see that x and y have different identifiers:

>>> id(x)
4401064560
>>> id(y)
4401098192

but if you were to assign y to x then both point to the same object:

>>> x = y
>>> id(x)
4401064560
>>> id(y)
4401064560
>>> x is y
True

and is shows both are the same object, it returns True.

Remember that in Python, names are just labels referencing values; you can have multiple names point to the same object. is tells you if two names point to one and the same object. == tells you if two names refer to objects that have the same value.


回答 1

另一个重复是在问为什么两个相等的字符串通常不相同,在这里并没有真正回答:

>>> x = 'a' 
>>> x += 'bc'
>>> y = 'abc'
>>> x == y
True
>>> x is y
False

那么,为什么它们不是相同的字符串?特别是考虑到这一点:

>>> z = 'abc'
>>> w = 'abc'
>>> z is w
True

让我们推迟第二部分。第一个怎么可能是真的?

解释器将必须有一个“内部表”,该表将字符串值映射到字符串对象,因此,每次尝试使用内容创建新字符串时'abc',都将获得相同的对象。维基百科对实习的工作方式进行了更详细的讨论。

Python 一个字符串实习表;您可以使用该sys.intern方法手动插入字符串。

实际上,允许 Python 自动实习任何不可变的类型,但并非必须这样做。不同的实现将产生不同的值。

CPython(如果您不知道使用的是哪种实现,则使用的实现)会自动插入小整数和一些特殊的单例,例如False,但不会自动生成字符串(或大整数,小元组或其他任何东西)。您可以很容易地看到这一点:

>>> a = 0
>>> a += 1
>>> b = 1
>>> a is b
True
>>> a = False
>>> a = not a
>>> b = True
a is b
True
>>> a = 1000
>>> a += 1
>>> b = 1001
>>> a is b
False

好的,但是为什么zw相同?

那不是解释器自动进行内插,而是编译器的折叠值。

如果相同的编译时间字符串在同一模块中出现两次(究竟这个手段是很难界定的,它是不一样的事,作为一个字符串字面量,因为r'abc''abc''a' 'b' 'c'都是不同的文字,但相同的字符串,但很容易理解直观地),编译器将只创建带有两个引用的字符串的一个实例。

实际上,编译器可以走得更远:'ab' + 'c'可以'abc'由优化器转换为编译器,在这种情况下,可以将其与'abc'常量一起折叠在同一模块中。

同样,这是允许但并非必须执行的Python。但是在这种情况下,CPython总是折叠小字符串(还有小元组)。(尽管交互式解释器的按语句陈述式编译器与一次模块编译器没有进行相同的优化,所以您不会在交互中看到完全相同的结果。)


那么,作为程序员,您应该怎么做?

好吧…什么都没有。您几乎没有任何理由关心两个不可变值是否相同。如果您想知道何时可以使用a is b而不是a == b,那么您会提出错误的问题。仅a == b在两种情况下始终使用:

  • 与单例值的比较可读性比较x is None
  • 对于可变值,当您需要知道突变是否x会影响时y

Another duplicate was asking why two equal strings are generally not identical, which isn’t really answered here:

>>> x = 'a' 
>>> x += 'bc'
>>> y = 'abc'
>>> x == y
True
>>> x is y
False

So, why aren’t they the same string? Especially given this:

>>> z = 'abc'
>>> w = 'abc'
>>> z is w
True

Let’s put off the second part for a bit. How could the first one be true?

The interpreter would have to have an “interning table”, a table mapping string values to string objects, so every time you try to create a new string with the contents 'abc', you get back the same object. Wikipedia has a more detailed discussion on how interning works.

And Python has a string interning table; you can manually intern strings with the sys.intern method.

In fact, Python is allowed to automatically intern any immutable types, but not required to do so. Different implementations will intern different values.

CPython (the implementation you’re using if you don’t know which implementation you’re using) auto-interns small integers and some special singletons like False, but not strings (or large integers, or small tuples, or anything else). You can see this pretty easily:

>>> a = 0
>>> a += 1
>>> b = 1
>>> a is b
True
>>> a = False
>>> a = not a
>>> b = True
a is b
True
>>> a = 1000
>>> a += 1
>>> b = 1001
>>> a is b
False

OK, but why were z and w identical?

That’s not the interpreter automatically interning, that’s the compiler folding values.

If the same compile-time string appears twice in the same module (what exactly this means is hard to define—it’s not the same thing as a string literal, because r'abc', 'abc', and 'a' 'b' 'c' are all different literals but the same string—but easy to understand intuitively), the compiler will only create one instance of the string, with two references.

In fact, the compiler can go even further: 'ab' + 'c' can be converted to 'abc' by the optimizer, in which case it can be folded together with an 'abc' constant in the same module.

Again, this is something Python is allowed but not required to do. But in this case, CPython always folds small strings (and also, e.g., small tuples). (Although the interactive interpreter’s statement-by-statement compiler doesn’t run the same optimization as the module-at-a-time compiler, so you won’t see exactly the same results interactively.)


So, what should you do about this as a programmer?

Well… nothing. You almost never have any reason to care if two immutable values are identical. If you want to know when you can use a is b instead of a == b, you’re asking the wrong question. Just always use a == b except in two cases:

  • For more readable comparisons to the singleton values like x is None.
  • For mutable values, when you need to know whether mutating x will affect the y.

回答 2

is仅当它们实际上是同一对象时才返回true。如果它们相同,则对另一个的更改也会出现。这是区别的一个例子。

>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> print x is y
False
>>> z = y
>>> print y is z
True
>>> print x is z
False
>>> y[0] = 5
>>> print z
[5, 2, 3]

is only returns true if they’re actually the same object. If they were the same, a change to one would also show up in the other. Here’s an example of the difference.

>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> print x is y
False
>>> z = y
>>> print y is z
True
>>> print x is z
False
>>> y[0] = 5
>>> print z
[5, 2, 3]

回答 3

重复的问题提示,此类推可能会起作用:

# - Darling, I want some pudding!
# - There is some in the fridge.

pudding_to_eat = fridge_pudding
pudding_to_eat is fridge_pudding
# => True

# - Honey, what's with all the dirty dishes?
# - I wanted to eat pudding so I made some. Sorry about the mess, Darling.
# - But there was already some in the fridge.

pudding_to_eat = make_pudding(ingredients)
pudding_to_eat is fridge_pudding
# => False

Prompted by a duplicate question, this analogy might work:

# - Darling, I want some pudding!
# - There is some in the fridge.

pudding_to_eat = fridge_pudding
pudding_to_eat is fridge_pudding
# => True

# - Honey, what's with all the dirty dishes?
# - I wanted to eat pudding so I made some. Sorry about the mess, Darling.
# - But there was already some in the fridge.

pudding_to_eat = make_pudding(ingredients)
pudding_to_eat is fridge_pudding
# => False

回答 4

isis not是Python中的两个身份运算符。is运算符不比较变量的值,而是比较变量的标识。考虑一下:

>>> a = [1,2,3]
>>> b = [1,2,3]
>>> hex(id(a))
'0x1079b1440'
>>> hex(id(b))
'0x107960878'
>>> a is b
False
>>> a == b
True
>>>

上面的例子显示,你的身份(也可以在CPython的内存地址)是两个不同的ab(即使它们的值是相同的)。这就是为什么当您说a is b由于两个操作数的标识不匹配而返回false时的原因。但是,当您说时a == b,它返回true,因为==操作仅验证两个操作数的赋值是否相同。

有趣的示例(针对高级成绩):

>>> del a
>>> del b
>>> a = 132
>>> b = 132
>>> hex(id(a))
'0x7faa2b609738'
>>> hex(id(b))
'0x7faa2b609738'
>>> a is b
True
>>> a == b
True
>>>

在上面的示例中,即使ab是两个不同的变量,也a is b返回True。这是因为类型a就是int这是一个不可变的对象。因此,python(我想是为了节省内存)b在创建具有相同值的对象时分配了相同的对象。因此,在这种情况下,变量的标识匹配并a is b变为True

这将适用于所有不可变对象:

>>> del a
>>> del b
>>> a = "asd"
>>> b = "asd"
>>> hex(id(a))
'0x1079b05a8'
>>> hex(id(b))
'0x1079b05a8'
>>> a is b
True
>>> a == b
True
>>>

希望有帮助。

is and is not are the two identity operators in Python. is operator does not compare the values of the variables, but compares the identities of the variables. Consider this:

>>> a = [1,2,3]
>>> b = [1,2,3]
>>> hex(id(a))
'0x1079b1440'
>>> hex(id(b))
'0x107960878'
>>> a is b
False
>>> a == b
True
>>>

The above example shows you that the identity (can also be the memory address in Cpython) is different for both a and b (even though their values are the same). That is why when you say a is b it returns false due to the mismatch in the identities of both the operands. However when you say a == b, it returns true because the == operation only verifies if both the operands have the same value assigned to them.

Interesting example (for the extra grade):

>>> del a
>>> del b
>>> a = 132
>>> b = 132
>>> hex(id(a))
'0x7faa2b609738'
>>> hex(id(b))
'0x7faa2b609738'
>>> a is b
True
>>> a == b
True
>>>

In the above example, even though a and b are two different variables, a is b returned True. This is because the type of a is int which is an immutable object. So python (I guess to save memory) allocated the same object to b when it was created with the same value. So in this case, the identities of the variables matched and a is b turned out to be True.

This will apply for all immutable objects:

>>> del a
>>> del b
>>> a = "asd"
>>> b = "asd"
>>> hex(id(a))
'0x1079b05a8'
>>> hex(id(b))
'0x1079b05a8'
>>> a is b
True
>>> a == b
True
>>>

Hope that helps.


回答 5

x is yid(x) == id(y),比较对象的身份相同。

正如@ tomasz-kurgan在下面的注释中指出的那样,is运算符在某些对象上的行为异常。

例如

>>> class A(object):
...   def foo(self):
...     pass
... 
>>> a = A()
>>> a.foo is a.foo
False
>>> id(a.foo) == id(a.foo)
True

参考;
https://docs.python.org/2/reference/expressions.html#is-not
https://docs.python.org/2/reference/expressions.html#id24

x is y is same as id(x) == id(y), comparing identity of objects.

As @tomasz-kurgan pointed out in the comment below is operator behaves unusually with certain objects.

E.g.

>>> class A(object):
...   def foo(self):
...     pass
... 
>>> a = A()
>>> a.foo is a.foo
False
>>> id(a.foo) == id(a.foo)
True

Ref;
https://docs.python.org/2/reference/expressions.html#is-not
https://docs.python.org/2/reference/expressions.html#id24


回答 6

您可以在此处检查一个小的整数。257以上的数字不是一个小整数,因此将其计算为另一个对象。

==在这种情况下,最好使用替代方法。

更多信息在这里:http : //docs.python.org/2/c-api/int.html

As you can check here to a small integers. Numbers above 257 are not an small ints, so it is calculated as a different object.

It is better to use == instead in this case.

Further information is here: http://docs.python.org/2/c-api/int.html


回答 7

X指向一个数组,Y指向另一个数组。这些数组是相同的,但是is运算符将查看不相同的那些指针。

X points to an array, Y points to a different array. Those arrays are identical, but the is operator will look at those pointers, which are not identical.


回答 8

它比较对象标识,即变量是否引用内存中的同一对象。就像==Java或C中的(比较指针时)一样。

It compares object identity, that is, whether the variables refer to the same object in memory. It’s like the == in Java or C (when comparing pointers).


回答 9

一个简单的例子,水果

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist is newfruitlist )
print ( fruitlist is verynewfruitlist )
print ( newfruitlist is verynewfruitlist )

输出:

True
False
False

如果你试试

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist == newfruitlist )
print ( fruitlist == verynewfruitlist )
print ( newfruitlist == verynewfruitlist )

输出是不同的:

True
True
True

这是因为==运算符仅比较变量的内容。要比较2个变量的标识,请使用is运算符

要打印标识号:

print ( id( variable ) )

A simple example with fruits

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist is newfruitlist )
print ( fruitlist is verynewfruitlist )
print ( newfruitlist is verynewfruitlist )

Output:

True
False
False

If you try

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist == newfruitlist )
print ( fruitlist == verynewfruitlist )
print ( newfruitlist == verynewfruitlist )

The output is different:

True
True
True

That’s because the == operator compares just the content of the variable. To compare the identities of 2 variable use the is operator

To print the identification number:

print ( id( variable ) )

回答 10

is运营商不过是英文版的==。由于两个列表的ID不同,因此答案为假。你可以试试:

a=[1,2,3]
b=a
print(b is a )#True

*因为两个列表的ID都相同

The is operator is nothing but an English version of ==. Because the IDs of the two lists are different so the answer is false. You can try:

a=[1,2,3]
b=a
print(b is a )#True

*Because the IDs of both the list would be same


按位操作和用法

问题:按位操作和用法

考虑以下代码:

x = 1        # 0001
x << 2       # Shift left 2 bits: 0100
# Result: 4

x | 2        # Bitwise OR: 0011
# Result: 3

x & 1        # Bitwise AND: 0001
# Result: 1

我可以用Python(和其他语言)理解算术运算符,但是我对“按位”运算符却不太了解。在上面的示例(来自Python书)中,我了解了左移功能,但不了解其他两个。

另外,按位运算符实际上是做什么用的?我会喜欢一些例子。

Consider this code:

x = 1        # 0001
x << 2       # Shift left 2 bits: 0100
# Result: 4

x | 2        # Bitwise OR: 0011
# Result: 3

x & 1        # Bitwise AND: 0001
# Result: 1

I can understand the arithmetic operators in Python (and other languages), but I never understood ‘bitwise’ operators quite well. In the above example (from a Python book), I understand the left-shift but not the other two.

Also, what are bitwise operators actually used for? I’d appreciate some examples.


回答 0

按位运算符是对多位值进行运算的运算符,但从概念上讲一次只能运算一位。

  • AND仅当其两个输入均为1时,才为1;否则为0。
  • OR如果其输入之一或全部为1,则为1,否则为0。
  • XOR仅当其输入之一恰好为1时为1,否则为0。
  • NOT 仅当其输入为0时为1,否则为0。

这些通常可以最好地显示为真值表。输入的可能性在顶部和左侧,结果位是在输入交点处显示的四个值之一(如果不是,则为两个,因为它只有一个输入)。

AND | 0 1     OR | 0 1     XOR | 0 1    NOT | 0 1
----+-----    ---+----     ----+----    ----+----
 0  | 0 0      0 | 0 1       0 | 0 1        | 1 0
 1  | 0 1      1 | 1 1       1 | 1 0

一个示例是,如果您只想要整数的低4位,则将其与15(二进制1111)进行与,则:

    201: 1100 1001
AND  15: 0000 1111
------------------
 IS   9  0000 1001

在那种情况下,15中的零位有效地充当了过滤器,迫使结果中的位也为零。

此外,>><<通常作为按位运算符包含在内,它们分别将值“左”右移和左移一定数量的比特,丢掉将要移向的端点的比特,并在该比特处输入零。另一端。

因此,例如:

1001 0101 >> 2 gives 0010 0101
1111 1111 << 4 gives 1111 0000

请注意,Python中的左移是不寻常的,因为它没有使用固定的宽度来丢弃位-虽然许多语言根据数据类型使用固定的宽度,但是Python只是扩展宽度以迎合额外的位。为了获得Python中的丢弃行为,您可以按位向左移动,and例如在8位值中向左移动四位:

bits8 = (bits8 << 4) & 255

考虑到这一点,位运算符的另一个例子是,如果你有两个4位值要打包成一个8位的一个,你可以使用所有这三个操作员的(left-shiftandor):

packed_val = ((val1 & 15) << 4) | (val2 & 15)
  • & 15操作将确保两个值仅具有低4位。
  • << 4是一个4位左移位移动val1进入前的8位值的4位。
  • |简单地结合了这两者结合起来。

如果val1为7且val2为4:

                val1            val2
                ====            ====
 & 15 (and)   xxxx-0111       xxxx-0100  & 15
 << 4 (left)  0111-0000           |
                  |               |
                  +-------+-------+
                          |
| (or)                0111-0100

Bitwise operators are operators that work on multi-bit values, but conceptually one bit at a time.

  • AND is 1 only if both of its inputs are 1, otherwise it’s 0.
  • OR is 1 if one or both of its inputs are 1, otherwise it’s 0.
  • XOR is 1 only if exactly one of its inputs are 1, otherwise it’s 0.
  • NOT is 1 only if its input is 0, otherwise it’s 0.

These can often be best shown as truth tables. Input possibilities are on the top and left, the resultant bit is one of the four (two in the case of NOT since it only has one input) values shown at the intersection of the inputs.

AND | 0 1     OR | 0 1     XOR | 0 1    NOT | 0 1
----+-----    ---+----     ----+----    ----+----
 0  | 0 0      0 | 0 1       0 | 0 1        | 1 0
 1  | 0 1      1 | 1 1       1 | 1 0

One example is if you only want the lower 4 bits of an integer, you AND it with 15 (binary 1111) so:

    201: 1100 1001
AND  15: 0000 1111
------------------
 IS   9  0000 1001

The zero bits in 15 in that case effectively act as a filter, forcing the bits in the result to be zero as well.

In addition, >> and << are often included as bitwise operators, and they “shift” a value respectively right and left by a certain number of bits, throwing away bits that roll of the end you’re shifting towards, and feeding in zero bits at the other end.

So, for example:

1001 0101 >> 2 gives 0010 0101
1111 1111 << 4 gives 1111 0000

Note that the left shift in Python is unusual in that it’s not using a fixed width where bits are discarded – while many languages use a fixed width based on the data type, Python simply expands the width to cater for extra bits. In order to get the discarding behaviour in Python, you can follow a left shift with a bitwise and such as in an 8-bit value shifting left four bits:

bits8 = (bits8 << 4) & 255

With that in mind, another example of bitwise operators is if you have two 4-bit values that you want to pack into an 8-bit one, you can use all three of your operators (left-shift, and and or):

packed_val = ((val1 & 15) << 4) | (val2 & 15)
  • The & 15 operation will make sure that both values only have the lower 4 bits.
  • The << 4 is a 4-bit shift left to move val1 into the top 4 bits of an 8-bit value.
  • The | simply combines these two together.

If val1 is 7 and val2 is 4:

                val1            val2
                ====            ====
 & 15 (and)   xxxx-0111       xxxx-0100  & 15
 << 4 (left)  0111-0000           |
                  |               |
                  +-------+-------+
                          |
| (or)                0111-0100

回答 1

一种典型用法:

| 用于将某个位设置为1

& 用于测试或清除特定位

  • 设置一个位(其中n是位数,0是最低有效位):

    unsigned char a |= (1 << n);

  • 清除一点:

    unsigned char b &= ~(1 << n);

  • 切换一下:

    unsigned char c ^= (1 << n);

  • 测试一下:

    unsigned char e = d & (1 << n);

以您的清单为例:

x | 2用于将第1位的设置x为1

x & 1用于测试第0位x是1还是0

One typical usage:

| is used to set a certain bit to 1

& is used to test or clear a certain bit

  • Set a bit (where n is the bit number, and 0 is the least significant bit):

    unsigned char a |= (1 << n);

  • Clear a bit:

    unsigned char b &= ~(1 << n);

  • Toggle a bit:

    unsigned char c ^= (1 << n);

  • Test a bit:

    unsigned char e = d & (1 << n);

Take the case of your list for example:

x | 2 is used to set bit 1 of x to 1

x & 1 is used to test if bit 0 of x is 1 or 0


回答 2

实际使用的按位运算符是什么?我会喜欢一些例子。

按位运算的最常见用途之一是解析十六进制颜色。

例如,这是一个Python函数,该函数接受类似于String的字符串,#FF09BE并返回其Red,Green和Blue值的元组。

def hexToRgb(value):
    # Convert string to hexadecimal number (base 16)
    num = (int(value.lstrip("#"), 16))

    # Shift 16 bits to the right, and then binary AND to obtain 8 bits representing red
    r = ((num >> 16) & 0xFF)

    # Shift 8 bits to the right, and then binary AND to obtain 8 bits representing green
    g = ((num >> 8) & 0xFF)

    # Simply binary AND to obtain 8 bits representing blue
    b = (num & 0xFF)
    return (r, g, b)

我知道有达到此目的的更有效方法,但是我相信这是一个非常简洁的示例,说明了移位和按位布尔运算。

what are bitwise operators actually used for? I’d appreciate some examples.

One of the most common uses of bitwise operations is for parsing hexadecimal colours.

For example, here’s a Python function that accepts a String like #FF09BE and returns a tuple of its Red, Green and Blue values.

def hexToRgb(value):
    # Convert string to hexadecimal number (base 16)
    num = (int(value.lstrip("#"), 16))

    # Shift 16 bits to the right, and then binary AND to obtain 8 bits representing red
    r = ((num >> 16) & 0xFF)

    # Shift 8 bits to the right, and then binary AND to obtain 8 bits representing green
    g = ((num >> 8) & 0xFF)

    # Simply binary AND to obtain 8 bits representing blue
    b = (num & 0xFF)
    return (r, g, b)

I know that there are more efficient ways to acheive this, but I believe that this is a really concise example illustrating both shifts and bitwise boolean operations.


回答 3

我认为问题的第二部分:

另外,按位运算符实际上是做什么用的?我会喜欢一些例子。

仅得到部分解决。这是我的两分钱。

在处理许多应用程序时,编程语言中的按位运算起着基本作用。几乎所有低层计算都必须使用此类操作来完成。

在所有需要在两个节点之间发送数据的应用程序中,例如:

  • 计算机网络;

  • 电信应用(蜂窝电话,卫星通信等)。

在通信的较低层,数据通常以所谓的frame发送。帧只是通过物理通道发送的字节字符串。该帧通常包含实际数据以及一些其他字段(以字节为单位),这些字段是标头的一部分。标头通常包含字节,这些字节编码一些与通信状态有关的信息(例如,带有标志(位)),帧计数器,校正和错误检测代码等。要在帧中获取传输的数据并构建帧帧发送数据,则需要确定按位操作。

通常,在处理此类应用程序时,可以使用API​​,因此您不必处理所有这些细节。例如,所有现代编程语言都提供用于套接字连接的库,因此您实际上不需要构建TCP / IP通信框架。但是,请考虑为您编程这些API的优秀人员,他们肯定必须处理框架构造;使用各种按位运算来从低级通信到高级通信。

举一个具体的例子,假设有人给您一个文件,其中包含直接由电信硬件捕获的原始数据。在这种情况下,为了找到帧,您将需要读取文件中的原始字节,并通过逐位扫描数据来尝试找到某种同步字。在识别了同步字之后,您将需要获取实际的帧,并在必要时(并只是故事的开始)按Shift键以获取正在传输的实际数据。

另一个非常不同的低层应用程序系列是当您需要使用某些(较旧的)端口(例如并行端口和串行端口)来控制硬件时。通过设置一些字节来控制此端口,就指令而言,该字节的每个位对该端口具有特定含义(例如,请参见http://en.wikipedia.org/wiki/Parallel_port)。如果要构建可以对该硬件执行某些操作的软件,则将需要按位操作以将要执行的指令转换为端口可以理解的字节。

例如,如果您有一些物理按钮连接到并行端口以控制其他设备,则可以在软件应用程序中找到以下代码行:

read = ((read ^ 0x80) >> 4) & 0x0f; 

希望这能有所作为。

I think that the second part of the question:

Also, what are bitwise operators actually used for? I’d appreciate some examples.

Has been only partially addressed. These are my two cents on that matter.

Bitwise operations in programming languages play a fundamental role when dealing with a lot of applications. Almost all low-level computing must be done using this kind of operations.

In all applications that need to send data between two nodes, such as:

  • computer networks;

  • telecommunication applications (cellular phones, satellite communications, etc).

In the lower level layer of communication, the data is usually sent in what is called frames. Frames are just strings of bytes that are sent through a physical channel. This frames usually contain the actual data plus some other fields (coded in bytes) that are part of what is called the header. The header usually contains bytes that encode some information related to the status of the communication (e.g, with flags (bits)), frame counters, correction and error detection codes, etc. To get the transmitted data in a frame, and to build the frames to send data, you will need for sure bitwise operations.

In general, when dealing with that kind of applications, an API is available so you don’t have to deal with all those details. For example, all modern programming languages provide libraries for socket connections, so you don’t actually need to build the TCP/IP communication frames. But think about the good people that programmed those APIs for you, they had to deal with frame construction for sure; using all kinds of bitwise operations to go back and forth from the low-level to the higher-level communication.

As a concrete example, imagine some one gives you a file that contains raw data that was captured directly by telecommunication hardware. In this case, in order to find the frames, you will need to read the raw bytes in the file and try to find some kind of synchronization words, by scanning the data bit by bit. After identifying the synchronization words, you will need to get the actual frames, and SHIFT them if necessary (and that is just the start of the story) to get the actual data that is being transmitted.

Another very different low level family of application is when you need to control hardware using some (kind of ancient) ports, such as parallel and serial ports. This ports are controlled by setting some bytes, and each bit of that bytes has a specific meaning, in terms of instructions, for that port (see for instance http://en.wikipedia.org/wiki/Parallel_port). If you want to build software that does something with that hardware you will need bitwise operations to translate the instructions you want to execute to the bytes that the port understand.

For example, if you have some physical buttons connected to the parallel port to control some other device, this is a line of code that you can find in the soft application:

read = ((read ^ 0x80) >> 4) & 0x0f; 

Hope this contributes.


回答 4

我希望这可以澄清这两个问题:

x | 2

0001 //x
0010 //2

0011 //result = 3

x & 1

0001 //x
0001 //1

0001 //result = 1

I hope this clarifies those two:

x | 2

0001 //x
0010 //2

0011 //result = 3

x & 1

0001 //x
0001 //1

0001 //result = 1

回答 5

将0视为假,将1视为真。然后按位的and(&)和or(|)就像常规的and和或或一样工作,只不过它们一次完成值中的所有位。通常,如果您可以设置30个选项(例如,在窗口上绘制样式),而又不想传递30个单独的布尔值来设置或取消设置每个布尔值,则可以将它们用作标志。将选项合并为一个值,然后使用&检查是否设置了每个选项。OpenGL大量使用这种标志传递方式。由于每个位都是一个单独的标志,因此您将获得以2(即仅设置了一位的数字)为幂的标志值1(2 ^ 0)2(2 ^ 1)4(2 ^ 2)8(2 ^ 3) 2的幂可以告诉您如果标志打开则将哪个位置1。

还要注意2 = 10,所以x | 2是110(6)而不是111(7)如果没有位重叠(在这种情况下为true)| 就像加法一样。

Think of 0 as false and 1 as true. Then bitwise and(&) and or(|) work just like regular and and or except they do all of the bits in the value at once. Typically you will see them used for flags if you have 30 options that can be set (say as draw styles on a window) you don’t want to have to pass in 30 separate boolean values to set or unset each one so you use | to combine options into a single value and then you use & to check if each option is set. This style of flag passing is heavily used by OpenGL. Since each bit is a separate flag you get flag values on powers of two(aka numbers that have only one bit set) 1(2^0) 2(2^1) 4(2^2) 8(2^3) the power of two tells you which bit is set if the flag is on.

Also note 2 = 10 so x|2 is 110(6) not 111(7) If none of the bits overlap(which is true in this case) | acts like addition.


回答 6

我没有看到上面提到的内容,但是您还会看到一些人使用左右移位进行算术运算。左移x等于乘以2 ^ x(只要它不会溢出),右移等同于除以2 ^ x。

最近,我看到人们使用x << 1和x >> 1来加倍和减半,尽管我不确定他们是否只是想变得聪明,还是真的比普通运算符有明显的优势。

I didn’t see it mentioned above but you will also see some people use left and right shift for arithmetic operations. A left shift by x is equivalent to multiplying by 2^x (as long as it doesn’t overflow) and a right shift is equivalent to dividing by 2^x.

Recently I’ve seen people using x << 1 and x >> 1 for doubling and halving, although I’m not sure if they are just trying to be clever or if there really is a distinct advantage over the normal operators.


回答 7

套装

可以使用数学运算来组合集合。

  • 联合运算符|将两个集合组合在一起,形成一个新集合,其中两个集合都包含项。
  • 交集运算符&仅在两个项中都获得项。
  • 差异运算符-在第一组中获得项目,但在第二组中则没有。
  • 对称差运算符^获取任一集合中的项目,但不能同时获取两者。

自己尝试:

first = {1, 2, 3, 4, 5, 6}
second = {4, 5, 6, 7, 8, 9}

print(first | second)

print(first & second)

print(first - second)

print(second - first)

print(first ^ second)

结果:

{1, 2, 3, 4, 5, 6, 7, 8, 9}

{4, 5, 6}

{1, 2, 3}

{8, 9, 7}

{1, 2, 3, 7, 8, 9}

Sets

Sets can be combined using mathematical operations.

  • The union operator | combines two sets to form a new one containing items in either.
  • The intersection operator & gets items only in both.
  • The difference operator - gets items in the first set but not in the second.
  • The symmetric difference operator ^ gets items in either set, but not both.

Try It Yourself:

first = {1, 2, 3, 4, 5, 6}
second = {4, 5, 6, 7, 8, 9}

print(first | second)

print(first & second)

print(first - second)

print(second - first)

print(first ^ second)

Result:

{1, 2, 3, 4, 5, 6, 7, 8, 9}

{4, 5, 6}

{1, 2, 3}

{8, 9, 7}

{1, 2, 3, 7, 8, 9}

回答 8

本示例将向您显示所有四个2位值的操作:

10 | 12

1010 #decimal 10
1100 #decimal 12

1110 #result = 14

10 & 12

1010 #decimal 10
1100 #decimal 12

1000 #result = 8

这是用法的一个示例:

x = raw_input('Enter a number:')
print 'x is %s.' % ('even', 'odd')[x&1]

This example will show you the operations for all four 2 bit values:

10 | 12

1010 #decimal 10
1100 #decimal 12

1110 #result = 14

10 & 12

1010 #decimal 10
1100 #decimal 12

1000 #result = 8

Here is one example of usage:

x = raw_input('Enter a number:')
print 'x is %s.' % ('even', 'odd')[x&1]

回答 9

另一个常见用例是操纵/测试文件权限。请参阅Python stat模块:http : //docs.python.org/library/stat.html

例如,要将文件的权限与所需的权限集进行比较,可以执行以下操作:

import os
import stat

#Get the actual mode of a file
mode = os.stat('file.txt').st_mode

#File should be a regular file, readable and writable by its owner
#Each permission value has a single 'on' bit.  Use bitwise or to combine 
#them.
desired_mode = stat.S_IFREG|stat.S_IRUSR|stat.S_IWUSR

#check for exact match:
mode == desired_mode
#check for at least one bit matching:
bool(mode & desired_mode)
#check for at least one bit 'on' in one, and not in the other:
bool(mode ^ desired_mode)
#check that all bits from desired_mode are set in mode, but I don't care about 
# other bits.
not bool((mode^desired_mode)&desired_mode)

我将结果转换为布尔值,因为我只关心真实性或虚假性,但是打印每个值的bin()值将是值得进行的练习。

Another common use-case is manipulating/testing file permissions. See the Python stat module: http://docs.python.org/library/stat.html.

For example, to compare a file’s permissions to a desired permission set, you could do something like:

import os
import stat

#Get the actual mode of a file
mode = os.stat('file.txt').st_mode

#File should be a regular file, readable and writable by its owner
#Each permission value has a single 'on' bit.  Use bitwise or to combine 
#them.
desired_mode = stat.S_IFREG|stat.S_IRUSR|stat.S_IWUSR

#check for exact match:
mode == desired_mode
#check for at least one bit matching:
bool(mode & desired_mode)
#check for at least one bit 'on' in one, and not in the other:
bool(mode ^ desired_mode)
#check that all bits from desired_mode are set in mode, but I don't care about 
# other bits.
not bool((mode^desired_mode)&desired_mode)

I cast the results as booleans, because I only care about the truth or falsehood, but it would be a worthwhile exercise to print out the bin() values for each one.


回答 10

在科学计算中,整数的位表示形式经常用于表示真假信息的数组,因为按位运算比迭代布尔数组快得多。(高级语言可能会使用位数组的概念。)

一个很好且相当简单的例子是Nim游戏的一般解决方案。看一下Wikipedia页面Python代码。它大量使用按位异或。^

Bit representations of integers are often used in scientific computing to represent arrays of true-false information because a bitwise operation is much faster than iterating through an array of booleans. (Higher level languages may use the idea of a bit array.)

A nice and fairly simple example of this is the general solution to the game of Nim. Take a look at the Python code on the Wikipedia page. It makes heavy use of bitwise exclusive or, ^.


回答 11

可能有更好的方法来查找数组元素在两个值之间的位置,但是如本示例所示,在此处有效,而and则无效。

import numpy as np
a=np.array([1.2, 2.3, 3.4])
np.where((a>2) and (a<3))      
#Result: Value Error
np.where((a>2) & (a<3))
#Result: (array([1]),)

There may be a better way to find where an array element is between two values, but as this example shows, the & works here, whereas and does not.

import numpy as np
a=np.array([1.2, 2.3, 3.4])
np.where((a>2) and (a<3))      
#Result: Value Error
np.where((a>2) & (a<3))
#Result: (array([1]),)

回答 12

我没有看到它,本示例将为您显示2位值的(-)十进制运算:AB(仅当A包含B时)

当我们在程序中持有表示位的动词时,需要执行此操作。有时我们需要添加位(如上),有时我们需要删除位(如果动词包含)

111 #decimal 7
-
100 #decimal 4
--------------
011 #decimal 3

使用python: 7&〜4 = 3(从7删除代表4的位)

001 #decimal 1
-
100 #decimal 4
--------------
001 #decimal 1

使用python: 1&〜4 = 1(从1删除代表4的位-在这种情况下1不是“包含” 4)。

i didnt see it mentioned, This example will show you the (-) decimal operation for 2 bit values: A-B (only if A contains B)

this operation is needed when we hold an verb in our program that represent bits. sometimes we need to add bits (like above) and sometimes we need to remove bits (if the verb contains then)

111 #decimal 7
-
100 #decimal 4
--------------
011 #decimal 3

with python: 7 & ~4 = 3 (remove from 7 the bits that represent 4)

001 #decimal 1
-
100 #decimal 4
--------------
001 #decimal 1

with python: 1 & ~4 = 1 (remove from 1 the bits that represent 4 – in this case 1 is not ‘contains’ 4)..


回答 13

操纵整数的位很有用,但对于网络协议(可能一直指定到位)通常是有用的,但是可能需要操纵更长的字节序列(不容易转换为一个整数)。在这种情况下,使用允许对数据按位进行操作的位库很有用-例如,可以将字符串“ ABCDEFGHIJKLMNOPQ”作为字符串或十六进制导入并对其进行位移(或执行其他按位操作):

>>> import bitstring
>>> bitstring.BitArray(bytes='ABCDEFGHIJKLMNOPQ') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')
>>> bitstring.BitArray(hex='0x4142434445464748494a4b4c4d4e4f5051') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')

Whilst manipulating bits of an integer is useful, often for network protocols, which may be specified down to the bit, one can require manipulation of longer byte sequences (which aren’t easily converted into one integer). In this case it is useful to employ the bitstring library which allows for bitwise operations on data – e.g. one can import the string ‘ABCDEFGHIJKLMNOPQ’ as a string or as hex and bit shift it (or perform other bitwise operations):

>>> import bitstring
>>> bitstring.BitArray(bytes='ABCDEFGHIJKLMNOPQ') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')
>>> bitstring.BitArray(hex='0x4142434445464748494a4b4c4d4e4f5051') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')

回答 14

以下按位运算符:| ^返回值(基于它们的输入),其逻辑门影响信号的方式相同。您可以使用它们来仿真电路。

the following bitwise operators: &, |, ^, and ~ return values (based on their input) in the same way logic gates affect signals. You could use them to emulate circuits.


回答 15

要翻转位(即1的补码/取反),可以执行以下操作:

由于ExORed与全1的值会导致取反,因此对于给定的位宽,您可以使用ExOR对其进行取反。

In Binary
a=1010 --> this is 0xA or decimal 10
then 
c = 1111 ^ a = 0101 --> this is 0xF or decimal 15
-----------------
In Python
a=10
b=15
c = a ^ b --> 0101
print(bin(c)) # gives '0b101'

To flip bits (i.e. 1’s complement/invert) you can do the following:

Since value ExORed with all 1s results into inversion, for a given bit width you can use ExOR to invert them.

In Binary
a=1010 --> this is 0xA or decimal 10
then 
c = 1111 ^ a = 0101 --> this is 0xF or decimal 15
-----------------
In Python
a=10
b=15
c = a ^ b --> 0101
print(bin(c)) # gives '0b101'

什么时候“ i + = x”与Python中的“ i = i + x”不同?

问题:什么时候“ i + = x”与Python中的“ i = i + x”不同?

有人告诉我它+=可能会与标准符号产生不同的影响i = i +。是否有与以下情况i += 1不同的情况i = i + 1

I was told that += can have different effects than the standard notation of i = i +. Is there a case in which i += 1 would be different from i = i + 1?


回答 0

这完全取决于对象i

+=调用__iadd__方法(如果存在- __add__如果不存在则返回),而+调用__add__方法1或在__radd__某些情况下调用方法2

从API的角度来看,__iadd__应该将其用于就地修改可变对象(返回已变异的对象),而__add__应该返回某些东西的新实例。对于不可变的对象,这两种方法都返回一个新实例,但是__iadd__会将新实例放置在当前命名空间中,其名称与旧实例的名称相同。这就是为什么

i = 1
i += 1

似乎在增加i。实际上,您将获得一个新的整数并将其分配给“顶部” i-丢失对旧整数的一个引用。在这种情况下,i += 1与完全相同i = i + 1。但是,对于大多数易变的物体,情况就不同了:

作为一个具体的例子:

a = [1, 2, 3]
b = a
b += [1, 2, 3]
print a  #[1, 2, 3, 1, 2, 3]
print b  #[1, 2, 3, 1, 2, 3]

相比:

a = [1, 2, 3]
b = a
b = b + [1, 2, 3]
print a #[1, 2, 3]
print b #[1, 2, 3, 1, 2, 3]

请注意,在第一个示例中,由于ba引用相同的对象,因此当我+=在on上使用时b,它实际上发生了变化b(并且也a看到了这种变化-毕竟,它引用了相同的列表)。但是,在第二种情况下,当我这样做时b = b + [1, 2, 3],它将采用b引用的列表并将其与新列表连接起来[1, 2, 3]。然后,它将串联的列表存储为b-而不考虑b之前的行。


1在表达式中x + y,如果x.__add__未实现或如果x.__add__(y)返回NotImplemented 并且 xy具有不同的类型,则x + y尝试调用y.__radd__(x)。所以,如果你有

foo_instance += bar_instance

如果Foo没有实现__add____iadd__那么这里的结果与

foo_instance = bar_instance.__radd__(bar_instance, foo_instance)

2在表达式中foo_instance + bar_instance如果的类型是(例如)类型的子类,bar_instance.__radd__则将在之前尝试。合理的,这是因为在某种意义上是一种“更高级”的对象不是那么应该得到压倒一切的选项的行为。foo_instance.__add__ bar_instancefoo_instanceissubclass(Bar, Foo)BarFooBarFoo

This depends entirely on the object i.

+= calls the __iadd__ method (if it exists — falling back on __add__ if it doesn’t exist) whereas + calls the __add__ method1 or the __radd__ method in a few cases2.

From an API perspective, __iadd__ is supposed to be used for modifying mutable objects in place (returning the object which was mutated) whereas __add__ should return a new instance of something. For immutable objects, both methods return a new instance, but __iadd__ will put the new instance in the current namespace with the same name that the old instance had. This is why

i = 1
i += 1

seems to increment i. In reality, you get a new integer and assign it “on top of” i — losing one reference to the old integer. In this case, i += 1 is exactly the same as i = i + 1. But, with most mutable objects, it’s a different story:

As a concrete example:

a = [1, 2, 3]
b = a
b += [1, 2, 3]
print a  #[1, 2, 3, 1, 2, 3]
print b  #[1, 2, 3, 1, 2, 3]

compared to:

a = [1, 2, 3]
b = a
b = b + [1, 2, 3]
print a #[1, 2, 3]
print b #[1, 2, 3, 1, 2, 3]

notice how in the first example, since b and a reference the same object, when I use += on b, it actually changes b (and a sees that change too — After all, it’s referencing the same list). In the second case however, when I do b = b + [1, 2, 3], this takes the list that b is referencing and concatenates it with a new list [1, 2, 3]. It then stores the concatenated list in the current namespace as b — With no regard for what b was the line before.


1In the expression x + y, if x.__add__ isn’t implemented or if x.__add__(y) returns NotImplemented and x and y have different types, then x + y tries to call y.__radd__(x). So, in the case where you have

foo_instance += bar_instance

if Foo doesn’t implement __add__ or __iadd__ then the result here is the same as

foo_instance = bar_instance.__radd__(bar_instance, foo_instance)

2In the expression foo_instance + bar_instance, bar_instance.__radd__ will be tried before foo_instance.__add__ if the type of bar_instance is a subclass of the type of foo_instance (e.g. issubclass(Bar, Foo)). The rationale for this is that Bar is in some sense a “higher-level” object than Foo so Bar should get the option of overriding Foo‘s behavior.


回答 1

在幕后,i += 1执行以下操作:

try:
    i = i.__iadd__(1)
except AttributeError:
    i = i.__add__(1)

虽然i = i + 1做了这样的事情:

i = i.__add__(1)

这有点过分简化,但您会明白:Python +=通过创建__iadd__方法和,为类型提供了一种专门处理类型的方法__add__

目的是使可变类型(如list)将自身变异__iadd__(然后返回self,除非您做的非常棘手),而可变类型(如int将不会实现。

例如:

>>> l1 = []
>>> l2 = l1
>>> l1 += [3]
>>> l2
[3]

因为和l2是同一对象l1,并且您进行了突变l1,所以您也进行了突变l2

但:

>>> l1 = []
>>> l2 = l1
>>> l1 = l1 + [3]
>>> l2
[]

在这里,你没有变异l1;相反,您创建了一个新列表,l1 + [3]然后反弹该名称l1l2指向该列表,而指向原始列表。

(在该+=版本中,您还需要重新绑定l1,只是在这种情况下,您需要将list其重新绑定到已经绑定的相同的位置,因此通常可以忽略该部分。)

Under the covers, i += 1 does something like this:

try:
    i = i.__iadd__(1)
except AttributeError:
    i = i.__add__(1)

While i = i + 1 does something like this:

i = i.__add__(1)

This is a slight oversimplification, but you get the idea: Python gives types a way to handle += specially, by creating an __iadd__ method as well as an __add__.

The intention is that mutable types, like list, will mutate themselves in __iadd__ (and then return self, unless you’re doing something very tricky), while immutable types, like int, will just not implement it.

For example:

>>> l1 = []
>>> l2 = l1
>>> l1 += [3]
>>> l2
[3]

Because l2 is the same object as l1, and you mutated l1, you also mutated l2.

But:

>>> l1 = []
>>> l2 = l1
>>> l1 = l1 + [3]
>>> l2
[]

Here, you didn’t mutate l1; instead, you created a new list, l1 + [3], and rebound the name l1 to point at it, leaving l2 pointing at the original list.

(In the += version, you were also rebinding l1, it’s just that in that case you were rebinding it to the same list it was already bound to, so you can usually ignore that part.)


回答 2

下面是比较直接的例子i += xi = i + x

def foo(x):
  x = x + [42]

def bar(x):
  x += [42]

c = [27]
foo(c); # c is not changed
bar(c); # c is changed to [27, 42]

Here is an example that directly compares i += x with i = i + x:

def foo(x):
  x = x + [42]

def bar(x):
  x += [42]

c = [27]
foo(c); # c is not changed
bar(c); # c is changed to [27, 42]