From ac060eedb7bd07ab7eb5491482dcb0cb137c4c25 Mon Sep 17 00:00:00 2001 From: wizardforcel <562826179@qq.com> Date: Sat, 18 Nov 2017 15:56:04 +0800 Subject: [PATCH] ch7. --- 7.md | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 7.md diff --git a/7.md b/7.md new file mode 100644 index 0000000..46a0e74 --- /dev/null +++ b/7.md @@ -0,0 +1,243 @@ +# + + +通过使用 Python 中已有的函数,我们正在建立一个使用的技术清单,用于识别数据集中的规律和主题。 现在我们将探索Python编程语言的核心功能:函数定义。 + +我们在本书中已经广泛使用了函数,但从未定义过我们自己的函数。定义一个函数的目的是,给一个计算过程命名,它可能会使用多次。计算中有许多需要重复计算的情况。 例如,我们常常希望对表的列中的每个值执行相同的操作。 + +## 定义函数 + +`double`函数的定义仅仅使一个数值加倍。 + +```py +# Our first function definition + +def double(x): + """ Double x """ + return 2*x +``` + +我们通过编写`def`来开始定义任何函数。 下面是这个小函数的其他部分(语法)的细分: + + + +当我们运行上面的单元格时,没有使特定的数字加倍,并且`double`主体中的代码还没有求值。因此,我们的函数类似于一个菜谱。 每次我们遵循菜谱中的指导,我们都需要以食材开始。 每次我们想用我们的函数来使一个数字加倍时,我们需要指定一个数字。 + +我们可以用和调用其他函数完全相同的方式,来调用`double`。 每次我们这样做的时候,主体中的代码都会执行,参数的值赋给了名称`x`。 + +```py +double(17) +34 +double(-0.6/4) +-0.3 +``` + +以上两个表达式都是调用表达式。 在第二个里面,计算了表达式`-0.6 / 4`的值,然后将其作为参数`x`传递给`double`函数。 每个调用表达式最终都会执行`double`的主体,但使用不同的`x`值。 + +`double`的主体只有一行: + +```py +return 2*x +``` + +执行这个`return`语句会完成`double`函数体的执行,并计算调用表达式的值。 + +`double`的参数可以是任何表达式,只要它的值是一个数字。 例如,它可以是一个名称。 `double`函数不知道或不在意如何计算或存储参数。 它唯一的工作是,使用传递给它的参数的值来执行它自己的主体。 + +```py +any_name = 42 +double(any_name) +84 +``` + +参数也可以是任何可以加倍的值。例如,可以将整个数值数组作为参数传递给`double`,结果将是另一个数组。 + +```py +double(make_array(3, 4, 5)) +array([ 6, 8, 10]) +``` + +但是,函数内部定义的名称(包括像double的x这样的参数)只存在一小会儿。 它们只在函数被调用的时候被定义,并且只能在函数体内被访问。 我们不能在`double`之外引用`x`。 技术术语是`x`具有局部作用域。 + +因此,即使我们在上面的单元格中调用了`double`,名称`x`也不能在函数体外识别。 + +```py +x +--------------------------------------------------------------------------- +NameError Traceback (most recent call last) + in () +----> 1 x + +NameError: name 'x' is not defined +``` + +文档字符串。 虽然`double`比较容易理解,但是很多函数执行复杂的任务,并且没有解释就很难使用。 (你自己也可能已经发现了!)因此,一个组成良好的函数有一个唤起它的行为的名字,以及文档。 在 Python 中,这被称为文档字符串 - 描述了它的行为和对其参数的预期。 文档字符串也可以展示函数的示例调用,其中调用前面是`>>>`。 + +文档字符串可以是任何字符串,只要它是函数体中的第一个东西。 文档字符串通常在开始和结束处使用三个引号来定义,这允许字符串跨越多行。 第一行通常是函数的完整但简短的描述,而下面的行则为将来的用户提供了进一步的指导。 + +下面是一个名为`percent`的函数定义,它带有两个参数。定义包括一个文档字符串。 + +```py +# A function with more than one argument + +def percent(x, total): + """Convert x to a percentage of total. + + More precisely, this function divides x by total, + multiplies the result by 100, and rounds the result + to two decimal places. + + >>> percent(4, 16) + 25.0 + >>> percent(1, 6) + 16.67 + """ + return round((x/total)*100, 2) +percent(33, 200) +16.5 +``` + +将上面定义的函数`percent`与下面定义的函数`percents`进行对比。 后者以数组为参数,将数组中的所有数字转换为数组中所有值的百分数。 百分数都四舍五入到两位,这次使用`round`来代替`np.round`,因为参数是一个数组而不是一个数字。 + +```py +def percents(counts): + """Convert the values in array_x to percents out of the total of array_x.""" + total = counts.sum() + return np.round((counts/total)*100, 2) +``` + +函数`percents`返回一个百分数的数组,除了四舍五入之外,它总计是 100。 + +```py +some_array = make_array(7, 10, 4) +percents(some_array) +array([ 33.33, 47.62, 19.05]) +``` + +理解 Python 执行函数的步骤是有帮助的。 为了方便起见,我们在下面的同一个单元格中放入了函数定义和对这个函数的调用。 + +```py +def biggest_difference(array_x): + """Find the biggest difference in absolute value between two adjacent elements of array_x.""" + diffs = np.diff(array_x) + absolute_diffs = abs(diffs) + return max(absolute_diffs) + +some_numbers = make_array(2, 4, 5, 6, 4, -1, 1) +big_diff = biggest_difference(some_numbers) +print("The biggest difference is", big_diff) +The biggest difference is 5 +``` + +这就是当我们运行单元格时,所发生的事情。 + +## 多个参数 + +可以有多种方式来推广一个表达式或代码块,因此一个函数可以有多个参数,每个参数决定结果的不同方面。 例如,我们以前定义的百分比`percents`,每次都四舍五入到两位。 以下两个参的数定义允许不同调用四舍五入到不同的位数。 + +```py +def percents(counts, decimal_places): + """Convert the values in array_x to percents out of the total of array_x.""" + total = counts.sum() + return np.round((counts/total)*100, decimal_places) + +parts = make_array(2, 1, 4) +print("Rounded to 1 decimal place: ", percents(parts, 1)) +print("Rounded to 2 decimal places:", percents(parts, 2)) +print("Rounded to 3 decimal places:", percents(parts, 3)) +Rounded to 1 decimal place: [ 28.6 14.3 57.1] +Rounded to 2 decimal places: [ 28.57 14.29 57.14] +Rounded to 3 decimal places: [ 28.571 14.286 57.143] +``` + +这个新定义的灵活性来源于一个小的代价:每次调用该函数时,都必须指定小数位数。默认参数值允许使用可变数量的参数调用函数;在调用表达式中未指定的任何参数都被赋予其默认值,这在`def`语句的第一行中进行了说明。 例如,在`percents`的最终定义中,可选参数`decimal_places`赋为默认值`2`。 + +```py +def percents(counts, decimal_places=2): + """Convert the values in array_x to percents out of the total of array_x.""" + total = counts.sum() + return np.round((counts/total)*100, decimal_places) + +parts = make_array(2, 1, 4) +print("Rounded to 1 decimal place:", percents(parts, 1)) +print("Rounded to the default number of decimal places:", percents(parts)) +Rounded to 1 decimal place: [ 28.6 14.3 57.1] +Rounded to the default number of decimal places: [ 28.57 14.29 57.14] +``` + +## 注:方法 + +函数通过将参数表达式放入函数名称后面的括号来调用。 任何独立定义的函数都是这样调用的。 你也看到了方法的例子,这些方法就像函数一样,但是用点符号来调用,比如`some_table.sort(some_label)`。 你定义的函数将始终首先使用函数名称,并传入所有参数来调用。 + +## 在列上应用函数 + +我们已经看到很多例子,通过将函数应用于现有列或其他数组,来创建新的表格的列。 所有这些函数都以数组作为参数。 但是我们经常打算,通过一个函数转换列中的条目,它不将数组作为它的函数。 例如,它可能只需要一个数字作为它的参数,就像下面定义的函数`cut_off_at_100`。 + +```py +def cut_off_at_100(x): + """The smaller of x and 100""" + return min(x, 100) +cut_off_at_100(17) +17 +cut_off_at_100(117) +100 +cut_off_at_100(100) +100 +``` + +如果参数小于或等于 100,函数`cut_off_at_100`只返回它的参数。但是如果参数大于 100,则返回 100。 + +在我们之前使用人口普查数据的例子中,我们看到变量`AGE`的值为 100,表示“100 岁以上”。 以这种方式将年龄限制在 100 岁,正是`cut_off_at_100`所做的。 + +为了一次性对很多年龄使用这个函数,我们必须能够引用函数本身,而不用实际调用它。 类似地,我们可能会向厨师展示一个蛋糕的菜谱,并要求她用它来烤 6 个蛋糕。 在这种情况下,我们不会使用这个配方自己烘烤蛋糕, 我们的角色只是把菜谱给厨师。 同样,我们可以要求一个表格,在列中的 6 个不同的数字上调用`cut_off_at_100`。 + +首先,我们创建了一个表,一列是人,一列是它们的年龄。 例如,`C`是 52 岁。 + +```py +ages = Table().with_columns( + 'Person', make_array('A', 'B', 'C', 'D', 'E', 'F'), + 'Age', make_array(17, 117, 52, 100, 6, 101) +) +ages +``` + +| Person | Age | +| --- | --- | +| A | 17 | +| B | 117 | +| C | 52 | +| D | 100 | +| E | 6 | +| F | 101 | + +### 应用 + +要在 100 岁截断年龄,我们将使用一个新的`Table`方法。 `apply`方法在列的每个元素上调用一个函数,形成一个返回值的新数组。 为了指出要调用的函数,只需将其命名(不带引号或括号)。 输入值的列的名称必须是字符串,仍然出现在引号内。 + +```py +ages.apply(cut_off_at_100, 'Age') +array([ 17, 100, 52, 100, 6, 100]) +``` + +我们在这里所做的是,将`cut_off_at_100`函数应用于`age`表的`Age`列中的每个值。 输出是函数的相应返回值的数组。 例如,17 还是 17,117 变成了 100,52 还是 52,等等。 + +此数组的长度与`age`表中原始`Age`列的长度相同,可用作名为`Cut Off Age`的新列中的值,并与现有的`Person`和`Age`列共存。 + + +```py +ages.with_column( + 'Cut Off Age', ages.apply(cut_off_at_100, 'Age') +) +``` + +| Person | Age | Cut Off Age | +| --- | --- | --- | +| A | 17 | 17 | +| B | 117 | 100 | +| C | 52 | 52 | +| D | 100 | 100 | +| E | 6 | 6 | +| F | 101 | 100 | + +### 作为值的函数 + -- GitLab