172.md 13.0 KB
Newer Older
W
wizardforcel 已提交
1
# Python 功能
W
init  
wizardforcel 已提交
2 3 4

> 原文: [https://javabeginnerstutorial.com/python-tutorial/python-function/](https://javabeginnerstutorial.com/python-tutorial/python-function/)

W
wizardforcel 已提交
5
现在,我们学习了 Python 编程的基础知识:变量,集合和循环。 如果您按照示例和教程进行操作,您可能会觉得有时我们会使用过多的代码,因此可以利用它。 但是也许您不知道该怎么做。
W
init  
wizardforcel 已提交
6

W
wizardforcel 已提交
7
大多数时候,解决方案是引入功能。 我们遇到了其他模块的功能,就像字符串的 isupper()或 random 模块的 choice()一样。
W
init  
wizardforcel 已提交
8 9 10

如果您学习过数学,那么您已经知道函数。 数学家可以与他们一起做非常讨厌的事情,但是程序员也可以。 函数的目的是产生仅由传递给函数的参数确定的结果。

W
wizardforcel 已提交
11
在编程语言中,我们可以将函数视为黑盒,在其中我们发送定义数量的参数(0 和更多),然后该函数返回结果。 在 Python 中,即使您没有显式编写或查看 return 语句,函数始终会返回结果。 这意味着,如果您知道另一种编程语言(例如 C++ 或 Java),那么您将了解 void 函数。 在 Python 中没有这种类型-但是,当我告诉您返回值时,我们将在本文后面看到。
W
init  
wizardforcel 已提交
12 13 14

## 定义功能

W
wizardforcel 已提交
15
使用 def 语句创建一个函数。 通用语法如下所示:
W
init  
wizardforcel 已提交
16 17

def function_name(参数列表):
W
wizardforcel 已提交
18
function_body 的语句
W
init  
wizardforcel 已提交
19 20 21

参数列表包含零个或多个元素。 如果您调用函数,那么您说的是参数而不是参数,但这是术语,在我看来,即使您调用函数也只说参数也是可以的。 如果您不将其声明为可选参数,则这些参数是必需的。 我们将在下一节中查看可选参数。

W
wizardforcel 已提交
22
每次调用函数时,都会执行其主体中的语句。 当然,您可以使用函数体内的 pass 语句不执行任何操作-但在这种情况下,pass 语句也将执行。
W
init  
wizardforcel 已提交
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

如您所知:函数必须在其主体中至少包含一个语句。 没有它,您会得到一个错误:

```java
>>> def no_body_function():
 ...
 File "<stdin>", line 2
```

```java
^
 IndentationError: expected an indented block
```

嗯,这个错误消息并不是最能说明问题的,但是在这种情况下,编译器缺少缩进的块-至少有一个针对函数体的语句。

因此,我们编写一个简单的函数,它是交换计算器。 它获得两个参数:值和汇率,并返回更改后的值(值乘以汇率)。

```java
def exchange(value, rate):
 return value*rate
```

W
wizardforcel 已提交
46
因此,每次定义函数时,请确保您具有缩进的主体。 如果您在 Python 的交互式解释器中遵循本文,则定义将如下所示:
W
init  
wizardforcel 已提交
47 48 49 50 51 52 53

```java
>>> def exchange(value, rate):
 ...     return value*rate
 ...
```

W
wizardforcel 已提交
54
正如我之前提到的,您也可以使用没有 return 语句的函数。 但是,大多数情况下,您不会使用这些功能,但是为了简洁起见,我们在这里也看一个示例:
W
init  
wizardforcel 已提交
55 56 57 58 59 60 61 62 63 64 65

```java
>>> def no_return():
 ...     print("This function has no return statement")
 ...
```

## 通话功能

我认为这很容易。 您已经知道如何调用函数,但让我们快速进行介绍。 如果您有一个函数定义,则可以通过传递正确的参数作为参数来调用它,这很不错。

W
wizardforcel 已提交
66
正如我之前告诉您的,我们已经调用了函数。 我们首先调用的基本函数是 print()。 您可以不带任何参数地调用它,在这种情况下,它将在输出中输出换行符(空行)。 另外,我们可以传递任意数量的参数,每个参数之间用逗号(,)分隔,它们也将被打印到输出中。
W
init  
wizardforcel 已提交
67 68 69 70 71 72 73 74 75 76 77 78 79 80

现在,我们调用上一节中定义的两个函数。

```java
>>> no_return()
 This function has no return statement
 >>> exchange(123, 1.12)
 137.76000000000002
```

如您所见,调用函数没有什么复杂的。

## 返回值

W
wizardforcel 已提交
81
之前我曾说过,函数会返回值-即使您未显式编写 return 语句。 现在是时候验证我的语句了,因此我将向您展示即使 no_return()函数也返回一个值,并且该值为 None。
W
init  
wizardforcel 已提交
82

W
wizardforcel 已提交
83
要查看函数的 return 语句,我们只需将函数调用包装到 print()函数调用中即可。
W
init  
wizardforcel 已提交
84 85 86 87 88 89 90 91 92

```java
>>> print(no_return())
 This function has no return statement
 None
 >>> print(exchange(123,1.12))
 137.76000000000002
```

W
wizardforcel 已提交
93
在这里您可以看到,即使没有 return 语句的函数也返回 None。 这意味着在这种情况下,您必须小心使用返回值,因为使用 None 时,仅在布尔表达式中使用它就几乎无能为力-当然要小心。
W
init  
wizardforcel 已提交
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

```java
>>> result_value = no_return()
 This function has no return statement
 >>> result_value + 5
 Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
```

```java
>>> if result_value == False:
 ...     print("Nothing useful...")
 ... else:
 ...     print("Wow, we have got True back!")
 ...
 Wow, we have got True back!
```

W
wizardforcel 已提交
113
如您在上面的示例中看到的,例如,您不能在数学运算中使用 None,并且 None 不为 False。
W
init  
wizardforcel 已提交
114 115 116 117 118 119 120 121 122 123 124 125

要修复示例的第二部分,我们可以像这样更改代码:

```java
>>> if not result_value:
 ...     print("Nothing useful...")
 ... else:
 ...     print("Wow, we have got True back!")
 ...
 Nothing useful...
```

W
wizardforcel 已提交
126
只使用没有任何值的 return 也是如此。 返回结果与没有返回语句的结果相同。 为什么要这么好? 例如,如果条件求值为 true,并且您想不返回任何内容,则希望终止函数。 自然地,您可以使用 return None,但是更多的 pythonic 解决方案将是简单地使用 return。
W
init  
wizardforcel 已提交
127 128 129 130 131 132 133 134 135 136 137 138 139 140

## 可选参数

您可以使用可选参数创建函数。 这意味着不必将这些参数传递给函数。 在这种情况下,将使用其默认值-如果可选参数获得其默认值,有时会跳过语句块。

可选参数必须遵循强制性参数,并且必须具有默认值。 当您调用函数并且不提供此参数时,将使用此值。

以前面介绍的交换功能为例。 快速提醒一下,这里是定义:

```java
def exchange(value, rate):
 return value * rate
```

W
wizardforcel 已提交
141
如果我们尝试仅使用 1 个参数(带有值)来调用此交换函数,则会从解释器中收到错误消息:
W
init  
wizardforcel 已提交
142 143 144 145 146 147 148 149

```java
>>> exchange(42)
 Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 TypeError: exchange() missing 1 required positional argument: 'rate'
```

W
wizardforcel 已提交
150
现在,将 rate 变量设为可选,并将其默认值设置为 1,以便能够以相同的货币调用此函数而无需进行任何兑换。
W
init  
wizardforcel 已提交
151

W
wizardforcel 已提交
152
因此,解决方案是为函数中的 rate 参数设置默认值,并将该值设置为 1。
W
init  
wizardforcel 已提交
153 154 155 156 157 158 159 160 161

要查看其工作原理,我还对代码进行了一些更改以显示当前汇率:

```java
>>> def exchange(value, rate=1):
 ...     print('Current exchange rate is', rate)
 ...     return value * rate
```

W
wizardforcel 已提交
162
现在 rate 参数是可选的,我们可以调用带有或不带有 rate 的函数:
W
init  
wizardforcel 已提交
163 164 165 166 167 168 169 170 171 172 173 174 175

```java
>>> exchange(124,0.78)
 Current exchange rate is 0.78
 96.72
 >>> exchange(325,1)
 Current exchange rate is 1
 325
 >>> exchange(42)
 Current exchange rate is 1
 42
```

W
wizardforcel 已提交
176
可选参数和必需参数的顺序很重要。 例如,如果我们更改顺序并将 rate = 1 作为 value 之前的第一个参数添加,我们将得到一个错误:
W
init  
wizardforcel 已提交
177 178 179 180 181 182 183 184 185 186 187 188 189

```java
>>> def exchange(rate=1, value):
 ...     return value * rate
 ...
 File "<stdin>", line 1
 SyntaxError: non-default argument follows default argument
```

如果您考虑一下,您将了解为什么会这样:如果我们提供一个论点,该怎么办。 是可选的还是必需的? 好吧,口译员无法分辨,也许您最终会得到错误的结果。

## 关键字参数

W
wizardforcel 已提交
190
在学习 Python 时,您可能会遇到术语“关键字参数”。 实际上,它们与可选参数相同:它们具有名称和默认值。 这个名称是关键字,您可以使用它为该参数分配一个新值。
W
init  
wizardforcel 已提交
191

W
wizardforcel 已提交
192
让我们再次看一下前面的示例:rate 是函数的关键字参数。 由于 exchange 只有一个可选参数,因此您可以同时使用两种参数来调用它:
W
init  
wizardforcel 已提交
193 194 195 196 197 198 199 200 201 202 203 204

```java
>>> exchange(42, 1.25)
 52.5
 >>> exchange(42, rate=1.25)
 52.5
```

第二种情况是我们使用关键字参数。

现在再来看一个示例,其中有多个可选参数,以便您了解它如何与关键字参数一起实际使用。

W
wizardforcel 已提交
205
该示例将非常基础:我们定义一个函数,该函数采用四个参数 a,b,c,d 并执行以下计算:a + b – c + d。 要使其工作,它仅需要 2 个参数,两个是可选的。
W
init  
wizardforcel 已提交
206 207 208 209 210 211 212 213 214

```java
>>> def sub_sum(a, b, c=0, d=0):
 ...   return a + b - c + d
 ...
 >>> sub_sum(12,33)
 45
```

W
wizardforcel 已提交
215
现在,我们可以选择传递变量 c 和 d 的值。 如果我们已经知道为 c 提供值有两种选择。
W
init  
wizardforcel 已提交
216 217 218 219 220 221 222 223

```java
>>> sub_sum(12,33,0,10)
 55
 >>> sub_sum(12,33,d=10)
 55
```

W
wizardforcel 已提交
224
如您所见,不必提供所有值,在调用函数时为 d 分配值就足够了。 这就是为什么它们被称为“关键字参数”的原因。 您可能会想到:有些函数带有很多参数,大多数时候只需要它们的默认值即可。 因此,您无需传递它们(因此您不必知道默认值是什么),并且可以使用关键字的列表中某个位置的单个参数对函数调用进行微调。
W
init  
wizardforcel 已提交
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251

进一步讲这个概念,我们可以用一种关键字语法调用函数,其方式是您在其他语言中无法想象的:您可以随意对值进行排序,直到为所有必需的参数提供它们的名称。

```java
>>> sub_sum(b=12,a=33,d=10)
 55
 >>> sub_sum(d=10, 8, 11)
 File "<stdin>", line 1
 SyntaxError: positional argument follows keyword argument
```

如您所见,如果您弄乱了顺序,则不能省略必需参数的名称。 在这种情况下,需要命名它们,以使解释器知道您想要设置这些值。

### 带有关键字参数的陷阱

上面我们已经看到了一种使用关键字参数的方法。 但是,每个硬币都有两个面。 分配这些默认值后,让我们再深入一点。 它是在创建函数时完成的(因此,在解释器解析函数定义时),而不是在调用函数时完成。 这意味着只要将不可变类型用于命名参数/关键字参数,我们就看不到任何区别。

但是,当我们使用可变变量(例如列表)时,可能会出现问题:

```java
>>> def append_if_short(s, lst=[]):
 ...     if len(s) < 4:
 ...         lst.append(s)
 ...     return lst
 ...
```

W
wizardforcel 已提交
252
在上面的示例中,如果 s 的长度最大为 3,则将参数 s 附加到参数 lst 上。如果将两个参数都传递给函数,这似乎很好。 但是让我们有时调用此函数...
W
init  
wizardforcel 已提交
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286

```java
>>> append_if_short("one")
 ['one']
 >>> append_if_short("two")
 ['one', 'two']
 >>> append_if_short("three")
 ['one', 'two']
 >>> append_if_short("four")
 ['one', 'two']
 >>> append_if_short("five")
 ['one', 'two']
 >>> append_if_short("six")
 ['one', 'two', 'six']
```

如您所见,这会导致意外的行为。 我们传入一个字符串,并返回一个包含比预期更多的元素的列表。

但是,这还不是全部。 我们会让事情变得更糟:

```java
>>> def append_if_short(s, lst=[]):
 ...     if len(s) < 4:
 ...         lst.append(s)
 ...     return lst
 ...
 >>> result = append_if_short("one")
 >>> result
 ['one']
 >>> result.append('five')
 >>> append_if_short('two')
 ['one', 'five', 'two']
```

W
wizardforcel 已提交
287
在上面的示例中,我们向列表中添加了一个元素,该元素显然超过了 3 个字符-这又可能导致意外行为。
W
init  
wizardforcel 已提交
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310

要解决此问题,请更改函数定义:

```java
>>> def append_if_short(s, lst=None):
 ...     if lst is None:
 ...         lst = []
 ...     if len(s) < 4:
 ...         lst.append(s)
 ...     return lst
 ...
 >>> result = append_if_short("one")
 >>> result
 ['one']
 >>> result.append('five')
 >>> append_if_short('two')
 ['two']
```

## 字串

有时(我希望每次)您都会有记录功能的冲动。 您可以使用围绕函数定义的简单注释来完成此操作。

W
wizardforcel 已提交
311
但是,您应该遵循一种常见的做法:*文档字符串*。 这些是简单的文档字符串,位于函数定义之后。 它们具有特殊的三引号格式,因为它们是描述您的函数的多行字符串。
W
init  
wizardforcel 已提交
312 313 314

有时文档比函数本身更长。 约定是使文档字符串的第一行成为简短的单行描述,然后在空白行后加上完整的描述,然后给出一些示例(如在交互式解释器中键入的内容)。

W
wizardforcel 已提交
315
因此,让我们使用此指南并将文档添加到我们的 append_if_short 函数中。
W
init  
wizardforcel 已提交
316 317 318 319 320 321

```java
def append_if_short(s, lst=None):
 """ Returns a list containing s if the length of s is smaller than 4.
```

W
wizardforcel 已提交
322
s 是任何字符串,lst 是可选的列表类型参数。 如果未提供 lst,则新的 l​​st 将分配一个新的空列表。 如果 len < 4,则将 s 附加到 lst 并返回 lst。
W
init  
wizardforcel 已提交
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340

```java
>>> append_if_short('one')
 ['one']
 >>> append_if_short('two', ['one'])
 ['one', 'two']
 >>> append_if_short('three', [])
 []
 >>> append_if_short('four')
 []
 """
 if lst is None:
 lst = []
 if len(s) < 4:
 lst.append(s)
 return lst
```