{ "cells": [ { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "this is **string** example....wow!!!\n" ] } ], "source": [ "#!/usr/bin/python3\n", "\n", " \n", "exp = \"*****this is **string** example....wow!!!*****\"\n", "print (exp.strip( '*' )) # 指定字符串 *" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3abcrunoob3\n" ] } ], "source": [ "exp = \"123abcrunoob321\"\n", "print (exp.strip( '21' )) # 字符序列为 21" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3abcrunoob3\n" ] } ], "source": [ "def strip(input_string, chars=None):\n", " if chars is None:\n", " # 如果没有指定chars参数,默认为去除空格\n", " chars = \" \\t\\n\\r\"\n", " start = 0\n", " end = len(input_string)\n", "\n", " # 从字符串开头去除指定的字符\n", " while start < end and input_string[start] in chars:\n", " start += 1\n", "\n", " # 从字符串结尾去除指定的字符\n", " while start < end and input_string[end - 1] in chars:\n", " end -= 1\n", "\n", " # 返回去除开头和结尾指定字符的字符串\n", " return input_string[start:end]\n", "\n", "exp = \"123abcrunoob321\"\n", "print (exp.strip( '12' )) # 字符序列为 12" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 关于参数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "之前就提到过,从结构上来看,每个函数都是一个完整的程序,因为一个程序,核心构成部分就是输入、处理、输出:\n", "\n", "> * 它可以有**输入** —— 即,它能接收外部通过参数传递的值;\n", "> * 它可以有**处理** —— 即,内部有能够完成某一特定任务的代码;尤其是,它可以根据 “输入” 得到 “输出”;\n", "> * 它可以有**输出** —— 即,它能向外部输送返回值……" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 为函数取名" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "哪怕一个函数内部什么都不干,它也得有个名字,然后名字后面要加上圆括号 `()`,以明示它是个函数,而不是某个变量。\n", "\n", "定义一个函数的关键字是 `def`,以下代码定义了一个什么都不干的函数:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "def do_nothing():\n", " pass\n", "\n", "do_nothing()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "为函数取名(为变量取名也一样)有些基本的注意事项:\n", "\n", "> - 首先,名称不能以数字开头。能用在名称开头的有,大小写字母和下划线 `_`;\n", "> \n", "> - 其次,名称中不能有空格,要么使用下划线连接词汇,如,`do_nothing`,要么使用 [Camel Case](https://en.wikipedia.org/wiki/Camel_case),如 `doNothing` —— 更推荐使用下划线;\n", "> \n", "> - 再次,名称不能与关键字重合 —— 以下是 Python 的 Keyword List:\n", "\n", "| - | Python | Keyword | List | - |\n", "| ---------- | ---------- | ---------- | ---------- | ---------- |\n", "| `and` | `as` | `assert` | `async` | `await` |\n", "| `break` | `class` | `continue` | `def` | `del` |\n", "| `elif` | `else` | `except` | `False` | `finally` |\n", "| `for` | `from` | `global` | `if` | `import` |\n", "| `in` | `is` | `lambda` | `None` | `nonlocal` |\n", "| `not` | `or` | `pass` | `raise` | `return` |\n", "| `True` | `try` | `while` | `with` | `yield` |\n", "\n", "你随时可以用以下代码查询关键字列表:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "['False',\n", " 'None',\n", " 'True',\n", " 'and',\n", " 'as',\n", " 'assert',\n", " 'async',\n", " 'await',\n", " 'break',\n", " 'class',\n", " 'continue',\n", " 'def',\n", " 'del',\n", " 'elif',\n", " 'else',\n", " 'except',\n", " 'finally',\n", " 'for',\n", " 'from',\n", " 'global',\n", " 'if',\n", " 'import',\n", " 'in',\n", " 'is',\n", " 'lambda',\n", " 'nonlocal',\n", " 'not',\n", " 'or',\n", " 'pass',\n", " 'raise',\n", " 'return',\n", " 'try',\n", " 'while',\n", " 'with',\n", " 'yield']" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "True" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.core.interactiveshell import InteractiveShell\n", "InteractiveShell.ast_node_interactivity = \"all\"\n", "\n", "import keyword\n", "keyword.kwlist # 列出所有关键字\n", "keyword.iskeyword('if') # 查询某个词是不是关键字" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 不接收任何参数的函数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在定义函数的时候,可以定义成不接收任何参数;但调用函数的时候,依然需要写上函数名后面的圆括号 `()`:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "This is a hello message from do_something().\n" ] } ], "source": [ "def do_something():\n", " print('This is a hello message from do_something().')\n", "\n", "do_something()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 没有 return 语句的函数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "函数内部,不一定非要有 `return` 语句 —— 上面 `do_somthing()` 函数就没有 `return` 语句。但如果函数内部并未定义返回值,那么,该函数的返回值是 `None`,当 `None` 被当作布尔值对待的时候,相当于是 `False`。\n", "\n", "这样的设定,使得函数调用总是可以在条件语句中被当作判断依据:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "This is a hello message from do_something().\n", "This is a hello message from do_something().\n", "The return value of 'do_something()' is None.\n", "The return value of 'do_something()' is None.\n" ] } ], "source": [ "def do_something():\n", " print('This is a hello message from do_something().')\n", "\n", "if do_something():\n", " print(\"The return value of 'do_something()' is None.\")\n", "\n", "if not do_something(): # 由于该函数名称的缘故,这一句代码的可读性很差……\n", " print(\"The return value of 'do_something()' is None.\")\n", "\n", "if True:\n", " print(\"The return value of 'do_something()' is None.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`if not do_something(): ` 翻译成自然语言,应该是,“如果 `do_something()` 的返回值是 ‘非真’,那么:……” " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 接收外部传递进来的值" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "让我们写个判断闰年年份的函数,取名为 `is_leap()`,它接收一个年份为参数,若是闰年,则返回 `True`,否则返回 `False`。\n", "\n", "根据闰年的定义:\n", "\n", "> * 年份应该是 4 的倍数;\n", "> * 年份能被 100 整除但不能被 400 整除的,不是闰年。\n", "\n", "所以,相当于要在能被 4 整除的年份中,排除那些能被 100 整除却不能被 400 整除的年份。" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "True" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "False" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "True" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def is_leap(year):\n", " leap = False\n", " if year % 4 == 0:\n", " leap = True\n", " if year % 100 == 0 and year % 400 != 0:\n", " leap = False\n", " return leap\n", "\n", "\n", "is_leap(7)\n", "is_leap(12)\n", "is_leap(100)\n", "is_leap(400)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 另外一个更为简洁的版本,理解它还挺练脑子的\n", "# cpython/Lib/datetime.py\n", "def _is_leap(year):\n", " return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)\n", "_is_leap(300)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "函数可以同时接收多个参数。比如,我们可以写个函数,让它输出从大于某个数字到小于另外一个数字的斐波那契数列;那就需要定义两个参数,调用它的时候也需要传递两个参数:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "144 233 377 610 987 1597 2584 4181 6765 " ] } ], "source": [ "def fib_between(start, end):\n", " a, b = 0, 1\n", " while a < end:\n", " if a >= start:\n", " print(a, end=' ')\n", " a, b = b, a + b\n", " \n", "fib_between(100, 10000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "当然可以把这个函数写成返回值是一个列表:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def fib_between(start, end):\n", " r = []\n", " a, b = 0, 1\n", " while a < end:\n", " if a >= start:\n", " r.append(a)\n", " a, b = b, a + b\n", " return r\n", " \n", "fib_between(100, 10000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 变量的作用域" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "下面的代码,经常会让初学者迷惑:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n" ] } ], "source": [ "def increase_one(n):\n", " n += 1\n", " return n\n", "\n", "n = 1\n", "print(increase_one(n))\n", "# print(n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "当 `increase_one(n)` 被调用之后,`n` 的值究竟是多少呢?或者更准确点问,随后的 `print(n)` 的输出结果应该是什么呢?\n", "\n", "输出结果是 `1`。\n", "\n", "在程序执行过程中,变量有**全局变量**(Global Variables)和**局域变量**(Local Variables)之分。\n", "\n", "> 首先,每次某个函数被调用的时候,这个函数会开辟一个新的区域,这个函数内部所有的变量,都是局域变量。也就是说,即便那个函数内部某个变量的名称与它外部的某个全局变量名称相同,它们也不是同一个变量 —— 只是名称相同而已。\n", "> \n", "> 其次,更为重要的是,当外部调用一个函数的时候,准确地讲,传递的不是变量,而是那个变量的*值*。也就是说,当 `increase_one(n)` 被调用的时候,被传递给那个恰好名称也叫 `n` 的局域变量的,是全局变量 `n` 的值,`1`。\n", "> \n", "> 而后,`increase_one()` 函数的代码开始执行,局域变量 `n` 经过 `n += 1` 之后,其中存储的值是 `2`,而后这个值被 `return` 语句返回,所以,`print(increase(n))` 所输出的值是函数被调用之后的返回值,即,`2`。\n", "> \n", "> 然而,全局变量 `n` 的值并没有被改变,因为局部变量 `n`(它的值是 `2`)和全局变量 `n`(它的值还是 `1`)只不过是名字相同而已,但它们并不是同一个变量。\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**例外:** 不过,有一种情况要格外注意 —— 在函数内部处理被传递进来的值是**可变容器**(比如,列表)的时候:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1, ['What?!', 2, 3])" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def be_careful(a, b):\n", " a = 2\n", " b[0] = 'What?!'\n", "\n", "a = 1\n", "b = [1, 2, 3]\n", "be_careful(a, b)\n", "a, b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "所以,一个比较好的习惯是,如果传递进来的值是列表,那么在函数内部对其操作之前,先创建一个它的拷贝:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1, [1, 2, 3])" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def be_careful(a, b):\n", " a = 2\n", " b_copy = b.copy()\n", " b_copy[0] = 'What?!'\n", "\n", "a = 1\n", "b = [1, 2, 3]\n", "be_careful(a, b)\n", "a, b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 可以接收一系列值的位置参数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果你在定义参数的时候,在一个*位置参数*(Positional Arguments)前面标注了星号,`*`,那么,这个位置参数可以接收**一系列值**(简单来说,不止一个参数),在函数内部可以对这一系列值用 `for ... in ...` 循环进行逐一的处理。\n", "\n", "带一个星号的参数,英文名称是 “Arbitrary Positional Arguments”,姑且翻译为 “随意的位置参数”。\n", "\n", "还有带两个星号的参数,一会儿会讲到,英文名称是 “Arbitrary Keyword Arguments”,姑且翻译为 “随意的关键字参数”。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hi, ann!\n", "Hi, mike!\n", "Hi, john!\n", "Hi, zeo!\n" ] } ], "source": [ "def say_hi(*names):\n", " for name in names:\n", " print(f'Hi, {name}!')\n", "say_hi()\n", "say_hi('ann')\n", "say_hi('mike', 'john', 'zeo')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`say_hi()` 这一行没有任何输出。因为你在调用函数的时候,没有给它传递任何值,于是,在函数内部代码执行的时候,`name in names` 的值是 `False`,所以,`for` 循环内部的代码没有被执行。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在函数内部,是把 `names` 这个参数当作容器处理的 —— 否则也没办法用 `for ... in ...` 来处理。而在调用函数的时候,我们是可以将一个容器传递给函数的 Arbitrary Positional Arguments 的 —— 做法是,在调用函数的时候,在参数前面加上星号 `*`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hi, mike!\n", "Hi, john!\n", "Hi, zeo!\n" ] } ], "source": [ "def say_hi(*names):\n", " for name in names:\n", " print(f'Hi, {name}!')\n", "\n", "names = ('mike', 'john', 'zeo')\n", "say_hi(*names)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "实际上,因为以上的 `say_hi(*names)` 函数内部就是把接收到的参数当作容器处理的,于是,在调用这个函数的时候,向它传递任何容器都会被同样处理:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hi, P!\n", "Hi, y!\n", "Hi, t!\n", "Hi, h!\n", "Hi, o!\n", "Hi, n!\n", "Hi, 0!\n", "Hi, 1!\n", "Hi, 2!\n", "Hi, 3!\n", "Hi, 4!\n", "Hi, 5!\n", "Hi, 6!\n", "Hi, 7!\n", "Hi, 8!\n", "Hi, 9!\n", "Hi, 10!\n", "Hi, 9!\n", "Hi, 8!\n", "Hi, 7!\n", "Hi, 6!\n", "Hi, 5!\n", "Hi, 4!\n", "Hi, 3!\n", "Hi, 2!\n", "Hi, 1!\n", "Hi, ann!\n", "Hi, mike!\n", "Hi, joe!\n" ] } ], "source": [ "def say_hi(*names):\n", " for name in names:\n", " print(f'Hi, {name}!')\n", "\n", "a_string = 'Python'\n", "say_hi(*a_string)\n", "\n", "a_range = range(10)\n", "say_hi(*a_range)\n", "\n", "a_list = list(range(10, 0, -1))\n", "say_hi(*a_list)\n", "\n", "a_dictionary = {'ann':2321, 'mike':8712, 'joe': 7610}\n", "say_hi(*a_dictionary)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "_在定义可以接收一系列值的位置参数时,建议在函数内部为该变量命名时总是用**复数**_,因为函数内部,总是需要 `for` 循环去迭代元组中的元素,这样的时候,名称的复数形式对代码的可读性很有帮助." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**注意**:一个函数中,可以接收一系列值的位置参数只能有一个;并且若是还有其它位置参数存在,那就必须把这个可以接收一系列值的位置参数排在所有其它位置参数之后。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, Mike!\n", "Hello, John!\n", "Hello, Zeo!\n" ] } ], "source": [ "def say_hi(greeting, *names):\n", " for name in names:\n", " print(f'{greeting}, {name.capitalize()}!')\n", "\n", "say_hi('Hello', 'mike', 'john', 'zeo')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 为函数的某些参数设定默认值" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "可以在定义函数的时候,为某些参数设定默认值,这些有默认值的参数,又被称作关键字参数(Keyword Arguments)。从这个函数的 “用户” 角度来看,这些设定了默认值的参数,就成了 “可选参数”。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, mike!\n", "Hello, john!\n", "Hello, zeo!\n", "Hello, Mike!\n", "Hello, John!\n", "Hello, Zeo!\n" ] } ], "source": [ "def say_hi(greeting, *names, capitalized=False):\n", " for name in names:\n", " if capitalized:\n", " name = name.capitalize()\n", " print(f'{greeting}, {name}!')\n", "\n", "say_hi('Hello', 'mike', 'john', 'zeo')\n", "say_hi('Hello', 'mike', 'john', 'zeo', capitalized=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "可以在定义函数的时候,为某些参数设定默认值,这些有默认值的参数,又被称作关键字参数(Keyword Arguments)。从这个函数的 “用户” 角度来看,这些设定了默认值的参数,就成了 “可选参数”。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 可以接收一系列值的关键字参数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "之前我们看到,可以设定一个位置参数(Positional Argument),接收一系列的值,被称作 “Arbitrary Positional Argument”;\n", "\n", "同样地,我们也可以设定一个可以接收很多值的关键字参数(Arbitrary Keyword Argument)。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, mike!\n", "Oh, my darling, ann!\n", "Hi, john!\n" ] } ], "source": [ "def say_hi(**names_greetings):\n", " for name, greeting in names_greetings.items():\n", " print(f'{greeting}, {name}!')\n", " \n", "say_hi(mike='Hello', ann='Oh, my darling', john='Hi')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "既然在函数内部,我们在处理接收到的 Arbitrary Keyword Argument 时,用的是对字典的迭代方式,那么,在调用函数的时候,也可以直接使用字典的形式:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, mike!\n", "Oh, my darling, ann!\n", "Hi, john!\n", "Hello, mike!\n", "Oh, my darling, ann!\n", "Hi, john!\n" ] } ], "source": [ "def say_hi(**names_greetings):\n", " for name, greeting in names_greetings.items():\n", " print(f'{greeting}, {name}!')\n", " \n", "a_dictionary = {'mike':'Hello', 'ann':'Oh, my darling', 'john':'Hi'}\n", "say_hi(**a_dictionary)\n", "\n", "say_hi(**{'mike':'Hello', 'ann':'Oh, my darling', 'john':'Hi'})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "至于在函数内部,你用什么样的迭代方式去处理这个字典,是你自己的选择:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, mike!\n", "Oh, my darling, ann!\n", "Hi, john!\n" ] } ], "source": [ "def say_hi_2(**names_greetings):\n", " for name in names_greetings:\n", " print(f'{names_greetings[name]}, {name}!')\n", "say_hi_2(mike='Hello', ann='Oh, my darling', john='Hi')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 函数定义时各种参数的排列顺序" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hi, mike!\n", "Hi, john!\n", "Hi, zeo!\n", "Welcome, Mike!\n", "Welcome, John!\n", "Welcome, Zeo!\n" ] } ], "source": [ "def say_hi(greeting, *names, capitalized=False):\n", " for name in names:\n", " if capitalized:\n", " name = name.capitalize()\n", " print(f'{greeting}, {name}!')\n", "\n", "say_hi('Hi', 'mike', 'john', 'zeo')\n", "say_hi('Welcome', 'mike', 'john', 'zeo', capitalized=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果,你想给其中的 `greeting` 参数也设定个默认值怎么办?写成这样好像可以:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hi, mike!\n", "Hi, john!\n", "Hi, zeo!\n", "\n", "Welcome, Mike!\n", "Welcome, John!\n", "Welcome, Zeo!\n" ] } ], "source": [ "def say_hi(greeting='Hello', *names, capitalized=False):\n", " for name in names:\n", " if capitalized:\n", " name = name.capitalize()\n", " print(f'{greeting}, {name}!')\n", "\n", "say_hi('Hi', 'mike', 'john', 'zeo')\n", "say_hi('Welcome', 'mike', 'john', 'zeo', capitalized=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "但 `greeting` 这个参数虽然有默认值,可这个函数在被调用的时候,还是必须要给出这个参数,否则输出结果出乎你的想象:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mike, john!\n", "mike, zeo!\n" ] } ], "source": [ "def say_hi(greeting='Hello', *names, capitalized=False):\n", " for name in names:\n", " if capitalized:\n", " name = name.capitalize()\n", " print(f'{greeting}, {name}!')\n", "\n", "say_hi('mike', 'john', 'zeo')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "设定了默认值的 `greeting`,竟然不像你想象的那样是 “可选参数”!所以,你得这样写:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, mike!\n", "Hello, john!\n", "Hello, zeo!\n", "Hi, mike!\n", "Hi, john!\n", "Hi, zeo!\n" ] } ], "source": [ "def say_hi(*names, greeting='Hello', capitalized=False):\n", " for name in names:\n", " if capitalized:\n", " name = name.capitalize()\n", " print(f'{greeting}, {name}!')\n", "\n", "say_hi('mike', 'john', 'zeo')\n", "say_hi('mike', 'john', 'zeo', greeting='Hi')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "这是因为函数被调用时,面对许多参数,Python 需要按照既定的规则(即,顺序)判定每个参数究竟是哪一类型的参数:\n", "\n", "> **Order of Arguments**\n", "> 1. Positional\n", "> 1. Arbitrary Positional\n", "> 1. Keyword\n", "> 1. Arbitrary Keyword\n", "\n", "所以,即便你在定义里写成\n", "\n", "```python\n", "def say_hi(greeting='Hello', *names, capitalized=False):\n", " ...\n", "```\n", "\n", "在调用该函数的时候,无论你写的是\n", "```python\n", "say_hi('Hi', 'mike', 'john', 'zeo')\n", "```\n", "\n", "还是\n", "```python\n", "say_hi('mike', 'john', 'zeo')\n", "```\n", "\n", "Python 都会认为接收到的第一个值是 Positional Argument —— 因为在定义中,`greeting` 被放到了 Arbitrary Positional Arguments 之前。" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.4" }, "toc-autonumbering": true }, "nbformat": 4, "nbformat_minor": 2 }