957 lines
25 KiB
Markdown
957 lines
25 KiB
Markdown
|
|
# Python 中的运算符和表达式
|
|||
|
|
|
|||
|
|
> 原文:<https://realpython.com/python-operators-expressions/>
|
|||
|
|
|
|||
|
|
在完成了本系列上一篇关于 [Python 变量](https://realpython.com/python-variables)的教程之后,您现在应该已经很好地掌握了创建和命名不同类型的 Python 对象。让我们和他们一起工作吧!
|
|||
|
|
|
|||
|
|
**以下是你将在本教程中学到的:**你将看到如何在 Python 中对对象进行计算。本教程结束时,你将能够通过组合对象和**操作符**来创建复杂的**表达式**。
|
|||
|
|
|
|||
|
|
***参加测验:****通过我们的交互式“Python 运算符和表达式”测验来测试您的知识。完成后,您将收到一个分数,以便您可以跟踪一段时间内的学习进度:*
|
|||
|
|
|
|||
|
|
*[参加测验](/quizzes/python-operators-expressions/)
|
|||
|
|
|
|||
|
|
在 Python 中,操作符是一种特殊的符号,表示应该执行某种计算。运算符作用的值称为**操作数**。
|
|||
|
|
|
|||
|
|
这里有一个例子:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> a = 10
|
|||
|
|
>>> b = 20
|
|||
|
|
>>> a + b
|
|||
|
|
30
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
在这种情况下,`+`运算符将操作数`a`和`b`相加。操作数可以是文字值,也可以是引用对象的变量:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> a = 10
|
|||
|
|
>>> b = 20
|
|||
|
|
>>> a + b - 5
|
|||
|
|
25
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
一系列操作数和运算符,如`a + b - 5`,被称为表达式。Python 支持许多将数据对象组合成表达式的操作符。下文将对此进行探讨。
|
|||
|
|
|
|||
|
|
## 算术运算符
|
|||
|
|
|
|||
|
|
下表列出了 Python 支持的算术运算符:
|
|||
|
|
|
|||
|
|
| 操作员 | 例子 | 意义 | 结果 |
|
|||
|
|
| --- | --- | --- | --- |
|
|||
|
|
| `+`(一元) | `+a` | **一元正** | `a`
|
|||
|
|
换句话说,它并没有真正做什么。它的存在主要是为了完整,补充**的一元否定**。 |
|
|||
|
|
| `+`(二元) | `a + b` | **加法** | `a`和`b`之和 |
|
|||
|
|
| `-`(一元) | `-a` | **一元否定** | 值等于`a`但符号相反 |
|
|||
|
|
| `-`(二元) | `a - b` | **减法** | 从`a`中减去`b` |
|
|||
|
|
| `*` | `a * b` | **乘法运算** | `a`和`b`的乘积 |
|
|||
|
|
| `/` | `a / b` | **分部** | `a`除以`b`的商。
|
|||
|
|
结果总是有类型`float`。 |
|
|||
|
|
| `%` | `a % b` | 模块 | `a`除以`b`的余数 |
|
|||
|
|
| `//` | `a // b` | **楼层划分**(也叫**整数划分**) | `a`除以`b`的商,四舍五入到下一个最小的整数 |
|
|||
|
|
| `**` | `a ** b` | **求幂运算** | `a`提高到`b`的幂 |
|
|||
|
|
|
|||
|
|
以下是这些运算符的一些使用示例:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> a = 4
|
|||
|
|
>>> b = 3
|
|||
|
|
>>> +a
|
|||
|
|
4
|
|||
|
|
>>> -b
|
|||
|
|
-3
|
|||
|
|
>>> a + b
|
|||
|
|
7
|
|||
|
|
>>> a - b
|
|||
|
|
1
|
|||
|
|
>>> a * b
|
|||
|
|
12
|
|||
|
|
>>> a / b
|
|||
|
|
1.3333333333333333
|
|||
|
|
>>> a % b
|
|||
|
|
1
|
|||
|
|
>>> a ** b
|
|||
|
|
64
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
标准除法(`/`)的结果总是一个`float`,即使被除数能被除数整除:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> 10 / 5
|
|||
|
|
2.0
|
|||
|
|
>>> type(10 / 5)
|
|||
|
|
<class 'float'>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
当底数除法(`//`)的结果为正时,就好像小数部分被截断,只留下整数部分。当结果为负时,结果向下舍入到下一个最小(更大的负)整数:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> 10 / 4
|
|||
|
|
2.5
|
|||
|
|
>>> 10 // 4
|
|||
|
|
2
|
|||
|
|
>>> 10 // -4
|
|||
|
|
-3
|
|||
|
|
>>> -10 // 4
|
|||
|
|
-3
|
|||
|
|
>>> -10 // -4
|
|||
|
|
2
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
顺便注意,在 REPL 会话中,只需在`>>>`提示符下键入表达式的值而不键入`print()`,就可以显示表达式的值,这与使用文字值或变量一样:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> 25
|
|||
|
|
25
|
|||
|
|
>>> x = 4
|
|||
|
|
>>> y = 6
|
|||
|
|
>>> x
|
|||
|
|
4
|
|||
|
|
>>> y
|
|||
|
|
6
|
|||
|
|
>>> x * 25 + y
|
|||
|
|
106
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
[*Remove ads*](/account/join/)
|
|||
|
|
|
|||
|
|
## 比较运算符
|
|||
|
|
|
|||
|
|
| 操作员 | 例子 | 意义 | 结果 |
|
|||
|
|
| --- | --- | --- | --- |
|
|||
|
|
| `==` | `a == b` | **等于** | `True`如果`a`的值等于`b`
|
|||
|
|
的值,否则 |
|
|||
|
|
| `!=` | `a != b` | **不等于** | `True`如果`a`不等于`b`
|
|||
|
|
否则`False` |
|
|||
|
|
| `<` | `a < b` | **小于** | `True`如果`a`小于`b`
|
|||
|
|
否则 |
|
|||
|
|
| `<=` | `a <= b` | **小于或等于** | `True`如果`a`小于或等于`b`
|
|||
|
|
否则为`False` |
|
|||
|
|
| `>` | `a > b` | **大于** | `True`如果`a`大于`b`
|
|||
|
|
否则 |
|
|||
|
|
| `>=` | `a >= b` | **大于或等于** | `True`如果`a`大于或等于`b`
|
|||
|
|
否则为`False` |
|
|||
|
|
|
|||
|
|
下面是正在使用的[比较运算符](https://realpython.com/python-is-identity-vs-equality/)的例子:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> a = 10
|
|||
|
|
>>> b = 20
|
|||
|
|
>>> a == b
|
|||
|
|
False
|
|||
|
|
>>> a != b
|
|||
|
|
True
|
|||
|
|
>>> a <= b
|
|||
|
|
True
|
|||
|
|
>>> a >= b
|
|||
|
|
False
|
|||
|
|
|
|||
|
|
>>> a = 30
|
|||
|
|
>>> b = 30
|
|||
|
|
>>> a == b
|
|||
|
|
True
|
|||
|
|
>>> a <= b
|
|||
|
|
True
|
|||
|
|
>>> a >= b
|
|||
|
|
True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
比较运算符通常用在[布尔](https://realpython.com/python-boolean/)上下文中,如条件和循环语句,以指导程序流程,您将在后面看到。
|
|||
|
|
|
|||
|
|
### 浮点值的相等比较
|
|||
|
|
|
|||
|
|
回想一下之前关于浮点数的讨论,一个`float`对象内部存储的值可能并不完全是你所想的那样。因此,比较浮点值是否完全相等是不明智的做法。考虑这个例子:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> x = 1.1 + 2.2
|
|||
|
|
>>> x == 3.3
|
|||
|
|
False
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
呀!加法操作数的内部表示并不完全等于`1.1`和`2.2`,所以你不能依靠`x`来与`3.3`进行精确的比较。
|
|||
|
|
|
|||
|
|
确定两个浮点值是否“相等”的首选方法是,在给定一定容差的情况下,计算它们是否彼此接近。看一下这个例子:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> tolerance = 0.00001
|
|||
|
|
>>> x = 1.1 + 2.2
|
|||
|
|
>>> abs(x - 3.3) < tolerance
|
|||
|
|
True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
[`abs()`](https://realpython.com/python-absolute-value/#using-the-built-in-abs-function-with-numbers) 返回[绝对值](https://realpython.com/python-absolute-value/)。如果两个[数字](https://realpython.com/python-numbers/)之差的绝对值小于规定的公差,则它们足够接近,可以认为相等。
|
|||
|
|
|
|||
|
|
## 逻辑运算符
|
|||
|
|
|
|||
|
|
逻辑运算符`not`、`or`和`and`修改并连接在布尔上下文中评估的表达式,以创建更复杂的条件。
|
|||
|
|
|
|||
|
|
### 涉及布尔操作数的逻辑表达式
|
|||
|
|
|
|||
|
|
正如您所看到的,Python 中的一些对象和表达式实际上是布尔类型的。也就是说,它们等于 Python 对象`True`或`False`中的一个。考虑这些例子:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> x = 5
|
|||
|
|
>>> x < 10
|
|||
|
|
True
|
|||
|
|
>>> type(x < 10)
|
|||
|
|
<class 'bool'>
|
|||
|
|
|
|||
|
|
>>> t = x > 10
|
|||
|
|
>>> t
|
|||
|
|
False
|
|||
|
|
>>> type(t)
|
|||
|
|
<class 'bool'>
|
|||
|
|
|
|||
|
|
>>> callable(x)
|
|||
|
|
False
|
|||
|
|
>>> type(callable(x))
|
|||
|
|
<class 'bool'>
|
|||
|
|
|
|||
|
|
>>> t = callable(len)
|
|||
|
|
>>> t
|
|||
|
|
True
|
|||
|
|
>>> type(t)
|
|||
|
|
<class 'bool'>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
在上面的例子中,`x < 10`、`callable(x)`和`t`都是布尔对象或表达式。
|
|||
|
|
|
|||
|
|
当操作数为布尔型时,涉及`not`、`or`和`and`的逻辑表达式的解释很简单:
|
|||
|
|
|
|||
|
|
| 操作员 | 例子 | 意义 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| `not` | `not x` | `True` if `x`是`False`
|
|||
|
|
`False` if `x`是`True`
|
|||
|
|
(逻辑上颠倒了`x`的意义) |
|
|||
|
|
| `or` | `x or y` | `True`如果`x`或者`y`是`True`
|
|||
|
|
`False`否则 |
|
|||
|
|
| `and` | `x and y` | `True`如果`x`和`y`都是`True`
|
|||
|
|
`False`否则 |
|
|||
|
|
|
|||
|
|
下面看看它们在实践中是如何工作的。
|
|||
|
|
|
|||
|
|
#### "`not`"和布尔操作数
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
x = 5
|
|||
|
|
not x < 10
|
|||
|
|
False
|
|||
|
|
not callable(x)
|
|||
|
|
True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 操作数 | 价值 | 逻辑表达式 | 价值 |
|
|||
|
|
| --- | --- | --- | --- |
|
|||
|
|
| `x < 10` | `True` | `not x < 10` | `False` |
|
|||
|
|
| `callable(x)` | `False` | `not callable(x)` | `True` |
|
|||
|
|
|
|||
|
|
#### "`or`"和布尔操作数
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
x = 5
|
|||
|
|
x < 10 or callable(x)
|
|||
|
|
True
|
|||
|
|
x < 0 or callable(x)
|
|||
|
|
False
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 操作数 | 价值 | 操作数 | 价值 | 逻辑表达式 | 价值 |
|
|||
|
|
| --- | --- | --- | --- | --- | --- |
|
|||
|
|
| `x < 10` | `True` | `callable(x)` | `False` | `x < 10 or callable(x)` | `True` |
|
|||
|
|
| `x < 0` | `False` | `callable(x)` | `False` | `x < 0 or callable(x)` | `False` |
|
|||
|
|
|
|||
|
|
#### "`and`"和布尔操作数
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
x = 5
|
|||
|
|
x < 10 and callable(x)
|
|||
|
|
False
|
|||
|
|
x < 10 and callable(len)
|
|||
|
|
True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 操作数 | 价值 | 操作数 | 价值 | 逻辑表达式 | 价值 |
|
|||
|
|
| --- | --- | --- | --- | --- | --- |
|
|||
|
|
| `x < 10` | `True` | `callable(x)` | `False` | `x < 10 and callable(x)` | `False` |
|
|||
|
|
| `x < 10` | `True` | `callable(len)` | `True` | `x < 10 or callable(len)` | `True` |
|
|||
|
|
|
|||
|
|
[*Remove ads*](/account/join/)
|
|||
|
|
|
|||
|
|
### 布尔上下文中非布尔值的评估
|
|||
|
|
|
|||
|
|
很多对象和表达式不等于`True`或`False`。尽管如此,它们仍然可以在布尔上下文中被评估,并被确定为“真”或“假”
|
|||
|
|
|
|||
|
|
那么什么是真的,什么不是?作为一个哲学问题,这超出了本教程的范围!
|
|||
|
|
|
|||
|
|
但是在 Python 中,它是定义明确的。在布尔上下文中评估时,以下所有内容都被视为假:
|
|||
|
|
|
|||
|
|
* 布尔值`False`
|
|||
|
|
* 任何数值为零的值(`0`、`0.0`、`0.0+0.0j`)
|
|||
|
|
* 空字符串
|
|||
|
|
* 内置复合数据类型的对象为空(见下文)
|
|||
|
|
* 由 [Python 关键字](https://realpython.com/python-keywords/) [`None`](https://realpython.com/null-in-python/) 表示的特殊值
|
|||
|
|
|
|||
|
|
事实上,Python 中内置的任何其他对象都被认为是真实的。
|
|||
|
|
|
|||
|
|
您可以使用内置的`bool()`函数来确定对象或表达式的“真实性”。如果参数为真,则`bool()`返回`True`,如果参数为假,则`False`返回。
|
|||
|
|
|
|||
|
|
#### 数值
|
|||
|
|
|
|||
|
|
> 零值为假。
|
|||
|
|
> 非零值为真。
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> print(bool(0), bool(0.0), bool(0.0+0j))
|
|||
|
|
False False False
|
|||
|
|
|
|||
|
|
>>> print(bool(-3), bool(3.14159), bool(1.0+1j))
|
|||
|
|
True True True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 字符串
|
|||
|
|
|
|||
|
|
> 空字符串为 false。
|
|||
|
|
> 非空字符串为真。
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> print(bool(''), bool(""), bool(""""""))
|
|||
|
|
False False False
|
|||
|
|
|
|||
|
|
>>> print(bool('foo'), bool(" "), bool(''' '''))
|
|||
|
|
True True True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 内置复合数据对象
|
|||
|
|
|
|||
|
|
> Python 提供了名为`list`、`tuple`、`dict`和`set`的内置复合数据类型。这些是包含其他对象的“容器”类型。如果一个对象是空的,那么它被认为是 false,如果它不是空的,那么它被认为是 true。
|
|||
|
|
>
|
|||
|
|
> 下面的例子为`list`类型演示了这一点。(列表是用方括号在 Python 中定义的。)
|
|||
|
|
>
|
|||
|
|
> 有关`list`、`tuple`、`dict`和`set`类型的更多信息,请参见即将到来的教程。
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> type([])
|
|||
|
|
<class 'list'>
|
|||
|
|
>>> bool([])
|
|||
|
|
False
|
|||
|
|
|
|||
|
|
>>> type([1, 2, 3])
|
|||
|
|
<class 'list'>
|
|||
|
|
>>> bool([1, 2, 3])
|
|||
|
|
True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### `None`关键字
|
|||
|
|
|
|||
|
|
`None`永远是假的:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> bool(None)
|
|||
|
|
False
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 涉及非布尔操作数的逻辑表达式
|
|||
|
|
|
|||
|
|
非布尔值也可以通过`not`、`or`和`and`进行修改和连接。结果取决于操作数的“真实性”。
|
|||
|
|
|
|||
|
|
#### “`not`”和非布尔操作数
|
|||
|
|
|
|||
|
|
下面是非布尔值`x`的情况:
|
|||
|
|
|
|||
|
|
| 如果`x`为 | `not x`是 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| “真实” | `False` |
|
|||
|
|
| “福尔西” | `True` |
|
|||
|
|
|
|||
|
|
以下是一些具体的例子:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> x = 3
|
|||
|
|
>>> bool(x)
|
|||
|
|
True
|
|||
|
|
>>> not x
|
|||
|
|
False
|
|||
|
|
|
|||
|
|
>>> x = 0.0
|
|||
|
|
>>> bool(x)
|
|||
|
|
False
|
|||
|
|
>>> not x
|
|||
|
|
True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### “`or`”和非布尔操作数
|
|||
|
|
|
|||
|
|
这是两个非布尔值`x`和`y`的情况:
|
|||
|
|
|
|||
|
|
| 如果`x`为 | `x or y`是 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| 真理 | `x` |
|
|||
|
|
| 福尔西 | `y` |
|
|||
|
|
|
|||
|
|
注意,在这种情况下,表达式`x or y`的计算结果不是`True`或`False`,而是`x`或`y`中的一个:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> x = 3
|
|||
|
|
>>> y = 4
|
|||
|
|
>>> x or y
|
|||
|
|
3
|
|||
|
|
|
|||
|
|
>>> x = 0.0
|
|||
|
|
>>> y = 4.4
|
|||
|
|
>>> x or y
|
|||
|
|
4.4
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
即便如此,如果`x`或`y`为真,则表达式`x or y`为真,如果`x`和`y`都为假,则表达式为假。
|
|||
|
|
|
|||
|
|
#### “`and`”和非布尔操作数
|
|||
|
|
|
|||
|
|
下面是两个非布尔值`x`和`y`的结果:
|
|||
|
|
|
|||
|
|
| 如果`x`为 | `x and y`是 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| “真实” | `y` |
|
|||
|
|
| “福尔西” | `x` |
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> x = 3
|
|||
|
|
>>> y = 4
|
|||
|
|
>>> x and y
|
|||
|
|
4
|
|||
|
|
|
|||
|
|
>>> x = 0.0
|
|||
|
|
>>> y = 4.4
|
|||
|
|
>>> x and y
|
|||
|
|
0.0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
与`or`一样,表达式`x and y`的计算结果不是`True`或`False`,而是`x`或`y`中的一个。如果`x`和`y`都为真,则`x and y`为真,否则为假。
|
|||
|
|
|
|||
|
|
[*Remove ads*](/account/join/)
|
|||
|
|
|
|||
|
|
### 复合逻辑表达式和短路评估
|
|||
|
|
|
|||
|
|
到目前为止,您已经看到了只有一个`or`或`and`操作符和两个操作数的表达式:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
x or y
|
|||
|
|
x and y
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
多个逻辑运算符和操作数可以串在一起形成复合逻辑表达式。
|
|||
|
|
|
|||
|
|
#### 复合“`or`”表达式
|
|||
|
|
|
|||
|
|
考虑下面的表达式:
|
|||
|
|
|
|||
|
|
> **x<sub>1</sub>**`or`T7】x<sub>2</sub>`or`**x<sub>3</sub>**`or`……**x<sub>n</sub>**
|
|||
|
|
|
|||
|
|
如果任一个**x<sub>I</sub>T3】为真,则该表达式为真。**
|
|||
|
|
|
|||
|
|
在这样一个表达式中,Python 使用了一种叫做[短路评估](https://en.wikipedia.org/wiki/Short-circuit_evaluation)的方法,也叫做麦卡锡评估,以纪念计算机科学家约翰·麦卡锡。从左到右依次对**x<sub>I</sub>T5】操作数求值。一旦发现一个表达式为真,就知道整个表达式为真。在这一点上,Python 停止,不再计算任何术语。整个表达式的值是终止求值的 **x <sub>i</sub>** 的值。**
|
|||
|
|
|
|||
|
|
为了帮助演示短路评估,假设您有一个简单的“身份”函数`f()`,其行为如下:
|
|||
|
|
|
|||
|
|
* `f()`采用单一参数。
|
|||
|
|
* 它向控制台显示参数。
|
|||
|
|
* 它返回传递给它的参数作为返回值。
|
|||
|
|
|
|||
|
|
(您将在接下来的函数教程中看到如何定义这样的函数。)
|
|||
|
|
|
|||
|
|
对`f()`的几个调用示例如下所示:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> f(0)
|
|||
|
|
-> f(0) = 0
|
|||
|
|
0
|
|||
|
|
|
|||
|
|
>>> f(False)
|
|||
|
|
-> f(False) = False
|
|||
|
|
False
|
|||
|
|
|
|||
|
|
>>> f(1.5)
|
|||
|
|
-> f(1.5) = 1.5
|
|||
|
|
1.5
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
因为`f()`简单地返回传递给它的参数,我们可以根据需要通过为`arg`指定一个适当的 true 或 falsy 值来使表达式`f(arg)`为 true 或 falsy。另外,`f()`向控制台显示它的参数,控制台直观地确认它是否被调用。
|
|||
|
|
|
|||
|
|
现在,考虑下面的复合逻辑表达式:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> f(0) or f(False) or f(1) or f(2) or f(3)
|
|||
|
|
-> f(0) = 0
|
|||
|
|
-> f(False) = False
|
|||
|
|
-> f(1) = 1
|
|||
|
|
1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
解释器首先评估`f(0)`,也就是`0`。`0`的数值为假。表达式还不为真,所以计算从左到右进行。下一个操作数`f(False)`返回`False`。这也是错误的,所以评估还在继续。
|
|||
|
|
|
|||
|
|
接下来是`f(1)`。计算结果为`1`,这是真的。此时,解释器停止,因为它现在知道整个表达式为真。`1`作为表达式的值返回,其余的操作数`f(2)`和`f(3)`永远不会被计算。从显示屏上可以看到`f(2)`和`f(3)`呼叫没有发生。
|
|||
|
|
|
|||
|
|
#### 复合“`and`”表达式
|
|||
|
|
|
|||
|
|
具有多个`and`运算符的表达式中也存在类似的情况:
|
|||
|
|
|
|||
|
|
> **x<sub>1</sub>**`and`T7】x<sub>2</sub>`and`**x<sub>3</sub>**`and`……**x<sub>n</sub>**
|
|||
|
|
|
|||
|
|
如果所有的**x<sub>I</sub>T3】都为真,则该表达式为真。**
|
|||
|
|
|
|||
|
|
在这种情况下,短路求值决定了一旦发现任何操作数为假,解释器就停止求值,因为此时整个表达式都被认为是假的。一旦出现这种情况,就不再计算操作数,并且终止计算的 falsy 操作数作为表达式的值返回:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> f(1) and f(False) and f(2) and f(3)
|
|||
|
|
-> f(1) = 1
|
|||
|
|
-> f(False) = False
|
|||
|
|
False
|
|||
|
|
|
|||
|
|
>>> f(1) and f(0.0) and f(2) and f(3)
|
|||
|
|
-> f(1) = 1
|
|||
|
|
-> f(0.0) = 0.0
|
|||
|
|
0.0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
在上面的两个例子中,求值在第一个为假的词处停止——第一个例子是`f(False)`,第二个例子是`f(0.0)`——`f(2)`和`f(3)`调用都没有发生。`False`和`0.0`分别作为表达式的值返回。
|
|||
|
|
|
|||
|
|
如果所有的操作数都是真的,它们都会被求值,最后一个(最右边的)操作数作为表达式的值返回:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> f(1) and f(2.2) and f('bar')
|
|||
|
|
-> f(1) = 1
|
|||
|
|
-> f(2.2) = 2.2
|
|||
|
|
-> f(bar) = bar
|
|||
|
|
'bar'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
[*Remove ads*](/account/join/)
|
|||
|
|
|
|||
|
|
### 利用短路评估的习惯用法
|
|||
|
|
|
|||
|
|
有一些常见的惯用模式利用短路评估来简化表达。
|
|||
|
|
|
|||
|
|
#### 避免异常
|
|||
|
|
|
|||
|
|
假设你定义了两个变量`a`和`b`,你想知道`(b / a) > 0`:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> a = 3
|
|||
|
|
>>> b = 1
|
|||
|
|
>>> (b / a) > 0
|
|||
|
|
True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
但是你需要考虑到`a`可能是`0`的可能性,在这种情况下,解释器将[引发一个异常](https://realpython.com/python-exceptions/):
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> a = 0
|
|||
|
|
>>> b = 1
|
|||
|
|
>>> (b / a) > 0
|
|||
|
|
Traceback (most recent call last):
|
|||
|
|
File "<pyshell#2>", line 1, in <module>
|
|||
|
|
(b / a) > 0
|
|||
|
|
ZeroDivisionError: division by zero
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
您可以使用这样的表达式来避免错误:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> a = 0
|
|||
|
|
>>> b = 1
|
|||
|
|
>>> a != 0 and (b / a) > 0
|
|||
|
|
False
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
当`a`为`0`时,`a != 0`为假。短路评估确保评估在该点停止。`(b / a)`不被评估,也不引发错误。
|
|||
|
|
|
|||
|
|
事实上,你可以更简洁。当`a`是`0`时,表达式`a`本身就是假的。不需要明确的比较`a != 0`:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> a = 0
|
|||
|
|
>>> b = 1
|
|||
|
|
>>> a and (b / a) > 0
|
|||
|
|
0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 选择默认值
|
|||
|
|
|
|||
|
|
另一种习语包括当指定值为零或为空时选择默认值。例如,假设您想将一个变量`s`赋给包含在另一个名为`string`的变量中的值。但是如果`string`是空的,你需要提供一个默认值。
|
|||
|
|
|
|||
|
|
下面是使用短路评估表达这一点的简明方法:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
s = string or '<default_value>'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
如果`string`非空,则为真,此时表达式`string or '<default_value>'`为真。求值停止,`string`的值被返回并赋给`s`:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> string = 'foo bar'
|
|||
|
|
>>> s = string or '<default_value>'
|
|||
|
|
>>> s
|
|||
|
|
'foo bar'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
另一方面,如果`string`是一个空的[字符串](https://realpython.com/python-strings/),那么它就是 falsy。`string or '<default_value>'`的求值继续到下一个操作数`'<default_value>'`,该操作数被返回并赋值给`s`:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> string = ''
|
|||
|
|
>>> s = string or '<default_value>'
|
|||
|
|
>>> s
|
|||
|
|
'<default_value>'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
[*Remove ads*](/account/join/)
|
|||
|
|
|
|||
|
|
### 链式比较
|
|||
|
|
|
|||
|
|
比较运算符可以任意长度链接在一起。例如,以下表达式几乎是等效的:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
x < y <= z
|
|||
|
|
x < y and y <= z
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
它们都将计算出相同的布尔值。两者的细微区别在于,在链式比较`x < y <= z`中,`y`只被求值一次。更长的表达式`x < y and y <= z`将导致`y`被求值两次。
|
|||
|
|
|
|||
|
|
**注意:**在`y`是静态值的情况下,这不是一个显著的区别。但是考虑一下这些表达式:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
x < f() <= z
|
|||
|
|
x < f() and f() <= z
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
如果`f()`是一个导致程序数据被修改的函数,那么它在第一种情况下被调用一次和在第二种情况下被调用两次之间的差别可能很重要。
|
|||
|
|
|
|||
|
|
更一般的是,如果 ***op <sub>1</sub>* , *op <sub>2</sub>* ,** …,***op<sub>n</sub>***是比较运算符,那么下面的具有相同的布尔值:
|
|||
|
|
|
|||
|
|
> x<sub>1</sub>**T3】op<sub>1</sub>**x<sub>2</sub>***op<sub>2</sub>***x<sub>3</sub>…x<sub>n-1</sub>***op<sub>n</sub>***x<sub>n</sub>
|
|||
|
|
>
|
|||
|
|
> x<sub>1</sub>**T5】op<sub>1</sub>**x<sub>2</sub>`and`x<sub>2</sub>***op<sub>2</sub>***x<sub>3</sub>`and`…x<sub>n-1</sub>***op<sub>n</sub>***x<sub>n</sub>
|
|||
|
|
|
|||
|
|
在前一种情况下,每个 x <sub>i</sub> 只计算一次。在后一种情况下,除了第一次和最后一次,每个都将被评估两次,除非短路评估导致过早终止。
|
|||
|
|
|
|||
|
|
## 按位运算符
|
|||
|
|
|
|||
|
|
[按位运算符](https://realpython.com/python-bitwise-operators/)将操作数视为二进制数字序列,并对其进行逐位运算。支持以下运算符:
|
|||
|
|
|
|||
|
|
| 操作员 | 例子 | 意义 | 结果 |
|
|||
|
|
| --- | --- | --- | --- |
|
|||
|
|
| `&` | `a & b` | 按位**和** | 结果中的每个位位置是操作数相应位置的位的逻辑**和**。(`1`如果两者都是`1`,否则为`0`。) |
|
|||
|
|
| `|` | `a | b` | 按位**或** | 结果中的每个位位置是操作数相应位置的位的逻辑**或**。(`1`如果任一个为`1`,否则为`0`。) |
|
|||
|
|
| `~` | `~a` | 按位**求反** | 结果中的每个位位置都是操作数相应位置的位的逻辑反。(`1`如果`0`,`0`如果`1`)。) |
|
|||
|
|
| `^` | `a ^ b` | 按位**异或** | 结果中的每个位位置是操作数的相应位置中的位的逻辑**异或**。(`1`如果操作数中的位不同,`0`如果相同。) |
|
|||
|
|
| `>>` | `a >> n` | **右移** `n` **地点** | 每一位都右移`n`位。 |
|
|||
|
|
| `<<` | `a << n` | **左移** `n` **位置** | 每一位左移`n`位。 |
|
|||
|
|
|
|||
|
|
以下是一些例子:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> '0b{:04b}'.format(0b1100 & 0b1010)
|
|||
|
|
'0b1000'
|
|||
|
|
>>> '0b{:04b}'.format(0b1100 | 0b1010)
|
|||
|
|
'0b1110'
|
|||
|
|
>>> '0b{:04b}'.format(0b1100 ^ 0b1010)
|
|||
|
|
'0b0110'
|
|||
|
|
>>> '0b{:04b}'.format(0b1100 >> 2)
|
|||
|
|
'0b0011'
|
|||
|
|
>>> '0b{:04b}'.format(0b0011 << 2)
|
|||
|
|
'0b1100'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**注意:**`'0b{:04b}'.format()`的目的是格式化按位运算的数字输出,使它们更容易阅读。稍后您将看到`format()`方法的更多细节。现在,只需注意按位运算的操作数和结果。
|
|||
|
|
|
|||
|
|
## 标识运算符
|
|||
|
|
|
|||
|
|
Python 提供了两个操作符`is`和`is not`,它们决定了给定的操作数是否具有相同的身份——也就是说,引用同一个对象。这与相等不是一回事,相等意味着两个操作数引用包含相同数据的对象,但不一定是同一对象。
|
|||
|
|
|
|||
|
|
以下是两个相等但不相同对象的示例:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> x = 1001
|
|||
|
|
>>> y = 1000 + 1
|
|||
|
|
>>> print(x, y)
|
|||
|
|
1001 1001
|
|||
|
|
|
|||
|
|
>>> x == y
|
|||
|
|
True
|
|||
|
|
>>> x is y
|
|||
|
|
False
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
这里的`x`和`y`都是指值为`1001`的对象。他们是平等的。但是它们不引用同一个对象,您可以验证:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> id(x)
|
|||
|
|
60307920
|
|||
|
|
>>> id(y)
|
|||
|
|
60307936
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`x`和`y`没有相同的身份,`x is y`返回`False`。
|
|||
|
|
|
|||
|
|
您之前已经看到,当您进行类似于`x = y`的赋值时,Python 仅仅创建了对同一对象的第二个引用,并且您可以使用`id()`函数来确认这一事实。您也可以使用 [`is`操作员](https://realpython.com/python-is-identity-vs-equality/)进行确认:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> a = 'I am a string'
|
|||
|
|
>>> b = a
|
|||
|
|
>>> id(a)
|
|||
|
|
55993992
|
|||
|
|
>>> id(b)
|
|||
|
|
55993992
|
|||
|
|
|
|||
|
|
>>> a is b
|
|||
|
|
True
|
|||
|
|
>>> a == b
|
|||
|
|
True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
在这种情况下,由于`a`和`b`引用同一个对象,因此`a`和`b`也应该相等。
|
|||
|
|
|
|||
|
|
不出所料,`is`的反义词是`is not`:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> x = 10
|
|||
|
|
>>> y = 20
|
|||
|
|
>>> x is not y
|
|||
|
|
True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
[*Remove ads*](/account/join/)
|
|||
|
|
|
|||
|
|
## 运算符优先级
|
|||
|
|
|
|||
|
|
考虑这个表达式:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> 20 + 4 * 10
|
|||
|
|
60
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
这里有歧义。Python 是否应该先执行加法`20 + 4`,然后将和乘以`10`?还是应该先执行乘法`4 * 10`,然后再执行加法`20`?
|
|||
|
|
|
|||
|
|
很明显,既然结果是`60`,Python 选择了后者;如果它选择了前者,结果将是`240`。这是标准的代数过程,几乎在所有编程语言中都可以找到。
|
|||
|
|
|
|||
|
|
该语言支持的所有运算符都被赋予一个优先级。在表达式中,首先执行所有优先级最高的运算符。一旦获得这些结果,就执行下一个最高优先级的运算符。如此继续下去,直到表达式被完全求值。任何优先级相同的运算符都按从左到右的顺序执行。
|
|||
|
|
|
|||
|
|
以下是到目前为止您所看到的 Python 操作符的优先级顺序,从最低到最高:
|
|||
|
|
|
|||
|
|
| | 操作员 | 描述 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| ***最低优先级*** | `or` | 布尔或 |
|
|||
|
|
| | `and` | 布尔与 |
|
|||
|
|
| | `not` | 布尔 NOT |
|
|||
|
|
| | `==`、`!=`、`<`、`<=`、`>`、`>=`、`is`、`is not` | 比较,身份 |
|
|||
|
|
| | `|` | 按位或 |
|
|||
|
|
| | `^` | 按位异或 |
|
|||
|
|
| | `&` | 按位 AND |
|
|||
|
|
| | `<<`,`>>` | 比特移位 |
|
|||
|
|
| | `+`,`-` | 加法、减法 |
|
|||
|
|
| | `*`、`/`、`//`、`%` | 乘法、除法、除法、[模](https://realpython.com/python-modulo-operator/) |
|
|||
|
|
| | `+x`、`-x`、`~x` | 一元正、一元负、按位负 |
|
|||
|
|
| ***最高优先级*** | `**` | 求幂 |
|
|||
|
|
|
|||
|
|
位于表顶部的运算符优先级最低,位于表底部的运算符优先级最高。表中同一行的任何运算符都具有相同的优先级。
|
|||
|
|
|
|||
|
|
在上面的例子中,为什么先执行乘法是显而易见的:乘法的优先级高于加法。
|
|||
|
|
|
|||
|
|
同样,在下面的例子中,`3`先被提升到`4`的幂,等于`81`,然后乘法按从左到右的顺序进行(`2 * 81 * 5 = 810`):
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> 2 * 3 ** 4 * 5
|
|||
|
|
810
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
可以使用括号覆盖运算符优先级。括号中的表达式总是首先执行,在没有括号的表达式之前。因此,会发生以下情况:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> 20 + 4 * 10
|
|||
|
|
60
|
|||
|
|
>>> (20 + 4) * 10
|
|||
|
|
240
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> 2 * 3 ** 4 * 5
|
|||
|
|
810
|
|||
|
|
>>> 2 * 3 ** (4 * 5)
|
|||
|
|
6973568802
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
在第一个示例中,首先计算`20 + 4`,然后将结果乘以`10`。在第二个例子中,首先计算`4 * 5`,然后将`3`提升到那个幂,然后将结果乘以`2`。
|
|||
|
|
|
|||
|
|
自由使用括号没有错,即使它们不需要改变求值的顺序。事实上,这被认为是很好的实践,因为它可以使代码更具可读性,并且使读者不必从记忆中回忆运算符优先级。请考虑以下情况:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
(a < 10) and (b > 30)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
这里括号是完全不必要的,因为比较操作符比`and`具有更高的优先级,而且无论如何都是先执行的。但是有些人可能认为带括号版本的意图比不带括号的版本更明显:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
a < 10 and b > 30
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
另一方面,可能有些人更喜欢后者;这是个人喜好的问题。关键是,如果你觉得括号能让代码更易读,你可以一直使用它,即使它们不需要改变求值的顺序。
|
|||
|
|
|
|||
|
|
[*Remove ads*](/account/join/)
|
|||
|
|
|
|||
|
|
## 扩充赋值运算符
|
|||
|
|
|
|||
|
|
你已经看到了一个等号(`=`)被用来给一个变量赋值。当然,赋值右边的值是包含其他变量的表达式是完全可行的:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> a = 10
|
|||
|
|
>>> b = 20
|
|||
|
|
>>> c = a * 5 + b
|
|||
|
|
>>> c
|
|||
|
|
70
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
事实上,赋值右边的表达式可以包含对被赋值变量的引用:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> a = 10
|
|||
|
|
>>> a = a + 5
|
|||
|
|
>>> a
|
|||
|
|
15
|
|||
|
|
|
|||
|
|
>>> b = 20
|
|||
|
|
>>> b = b * 3
|
|||
|
|
>>> b
|
|||
|
|
60
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
第一个例子被解释为“`a`被赋予当前值`a`加上`5`,”实际上通过`5`增加了`a`的值。第二个读数为“`b`被赋予当前值为`b`乘以`3`,实际上将`b`的值增加了三倍。
|
|||
|
|
|
|||
|
|
当然,这种赋值只有在变量已经被赋值的情况下才有意义:
|
|||
|
|
|
|||
|
|
>>>
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
>>> z = z / 12
|
|||
|
|
Traceback (most recent call last):
|
|||
|
|
File "<pyshell#11>", line 1, in <module>
|
|||
|
|
z = z / 12
|
|||
|
|
NameError: name 'z' is not defined
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Python 支持这些算术运算符和按位运算符的简化扩充赋值符号:
|
|||
|
|
|
|||
|
|
| 算术 | 按位 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| `+`
|
|||
|
|
`-`
|
|||
|
|
`*`
|
|||
|
|
`/`
|
|||
|
|
`%`
|
|||
|
|
|
|||
|
|
`**` | `&`
|
|||
|
|
`|`
|
|||
|
|
`^`
|
|||
|
|
`>>`
|
|||
|
|
T4】 |
|
|||
|
|
|
|||
|
|
对于这些运算符,以下内容是等效的:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
x <op>= y
|
|||
|
|
x = x <op> y
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
看看这些例子:
|
|||
|
|
|
|||
|
|
| 扩充的
|
|||
|
|
赋值 | | 标准
|
|||
|
|
分配 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| `a += 5` | 相当于 | `a = a + 5` |
|
|||
|
|
| `a /= 10` | 相当于 | `a = a / 10` |
|
|||
|
|
| `a ^= b` | 相当于 | `a = a ^ b` |
|
|||
|
|
|
|||
|
|
## 结论
|
|||
|
|
|
|||
|
|
在本教程中,您了解了 Python 支持的多种多样的**操作符**来将对象组合成**表达式**。
|
|||
|
|
|
|||
|
|
到目前为止,您看到的大多数例子都只涉及简单的原子数据,但是您看到了对 **string** 数据类型的简要介绍。下一篇教程将更详细地探索**字符串**对象。
|
|||
|
|
|
|||
|
|
***参加测验:****通过我们的交互式“Python 运算符和表达式”测验来测试您的知识。完成后,您将收到一个分数,以便您可以跟踪一段时间内的学习进度:*
|
|||
|
|
|
|||
|
|
*[参加测验](/quizzes/python-operators-expressions/)*
|
|||
|
|
|
|||
|
|
*[« Variables in Python](https://realpython.com/python-variables/)[Operators and Expressions in Python](#)[Strings in Python »](https://realpython.com/python-strings/)*********
|