Python函数使用方法实例教程

154 阅读9分钟

函数是可重复使用的代码片段。当你发现自己写了两次相同的代码时,这些代码可能应该放在一个函数中。

例如,Python 有许多内置函数,如dir()sum() 。你还导入了math 模块并使用了它的平方根函数sqrt()

在本教程中,你将学习以下内容。

  • 创建一个函数
  • 调用一个函数
  • 传递参数
  • 对参数进行类型提示
  • 传递关键字参数
  • 必备参数和默认参数
  • *args**kwargs
  • 仅限位置的参数
  • 范围

让我们开始吧!

创建一个函数

一个函数以关键字开始,def ,后面是函数的名称,两个小括号,然后是冒号。接下来,你在函数下缩进一行或多行代码,形成函数 "块"。

这里是一个空函数。

def my_function():
    pass

当你创建一个函数时,通常建议函数的名称都是小写的,单词之间用下划线隔开。这被称为蛇形大写

pass 是 Python 中的一个关键字,Python 知道要忽略它。你也可以像这样定义一个空函数。

def my_function():
    ...

在这个例子中,这个函数除了一个省略号外没有任何内容。

接下来我们来学习如何使用一个函数!

调用一个函数

现在你有了一个函数,你需要让它做一些事情。让我们先来做这件事。

def my_function():
    print('Hello from my_function')

现在,你有了一个可以打印出信息的函数,而不是省略号或关键字pass

要调用一个函数,你需要写出它的名字,后面加上圆括号。

>>> def my_function():
...     print('Hello from my_function')
... 
>>> my_function()
Hello from my_function

这真是又好又简单

现在让我们来学习如何向你的函数传递参数。

传递参数

大多数函数允许你向它们传递参数。这样做的原因是,你通常希望向一个函数传递一个或多个位置参数,以便该函数能够对它们进行处理。

让我们创建一个函数,它接受一个名为name 的参数,然后打印出一条欢迎信息。

>>> def welcome(name):
...     print(f'Welcome {name}')
... 
>>> welcome('Mike')
Welcome Mike

如果你使用过其它的编程语言,你可能知道,其中有些语言要求函数返回一些东西。如果你没有指定返回值,Python 会自动返回None

让我们试着调用函数并将其结果赋给一个叫做return_value 的变量。

>>> def welcome(name):
...     print(f'Welcome {name}')
... 
>>> return_value = welcome('Mike')
Welcome Mike
>>> print(return_value)
None

当你打印出return_value ,你可以看到它是None

类型提示你的参数

一些编程语言使用静态类型,这样当你编译你的代码时,编译器会警告你与类型有关的错误。Python 是一种动态类型的语言,所以这在运行时才会发生。

然而,在Python 3.5中,typing 模块被添加到Python中,允许开发者在他们的代码中添加类型提示。这允许你在代码中指定参数和返回值的类型,但并不强制执行。你可以使用外部工具,例如mypy(http://mypy-lang.org/) 来检查你的代码库是否遵循了你设置的类型提示。

类型提示在Python中不是必需的,也不是由语言强制执行的,但是当与开发者团队一起工作时,特别是当这些团队是由不熟悉Python的人组成时,它是非常有用的。

让我们重写最后一个例子,让它使用类型提示。

>>> def welcome(name: str) -> None:
...     print(f'Welcome {name}')
... 
>>> return_value = welcome('Mike')
Welcome Mike
>>> print(return_value)
None

这一次,当你输入name 参数时,你用冒号 (:) 结尾,然后是你期望的类型。在这个例子中,你希望传入的是一个字符串类型。在这之后,你会注意到-> None: 的代码。-> 是特殊的语法,用来表示期望的返回值是什么。对于这段代码,返回值是None

如果你想明确地返回一个值,那么你可以使用return 关键字,后面跟上你想返回的东西。

当你运行这段代码时,它的执行方式与之前完全相同。

为了证明类型提示没有被强制执行,你可以通过使用return 关键字告诉 Python 返回一个整数。

>>> def welcome(name: str) -> None:
...     print(f'Welcome {name}')
...     return 5
... 
>>> return_value = welcome('Mike')
Welcome Mike
>>> print(return_value)
5

当你运行这段代码时,你可以看到类型提示说返回值应该是None ,但是你的编码使它返回的是整数5 。Python 并没有抛出一个异常。

你可以对这段代码使用mypy工具来验证它是否遵循了类型提示。如果你这样做,你会发现它确实显示了一个问题。你将在本书的第二部分学习如何使用mypy

这里的主要收获是,Python 支持类型提示。虽然 Python 并不强制执行类型。然而,一些 Python 编辑器可以在内部使用类型提示来警告你与类型有关的问题,或者你可以手动使用mypy来发现问题。

现在我们来学习一下你还可以向函数传递什么。

传递关键字参数

Python 还允许你传入关键字参数。一个关键字参数是通过传入一个命名的参数来指定的,例如你可以传入age=10

让我们创建一个新的例子,显示一个常规参数和一个关键字参数。

>>> def welcome(name: str, age: int=15) -> None:
...     print(f'Welcome {name}. You are {age} years old.')
... 
>>> welcome('Mike')
Welcome Mike. You are 15 years old.

这个例子有一个常规参数,name 和一个关键字参数,age ,它被默认为15 。当你调用这段代码而不指定age ,你会看到它被默认为15。

为了使事情变得格外清楚,这里有一个不同的方式,你可以调用它。

>>> def welcome(name: str, age: int) -> None:
...     print(f'Welcome {name}. You are {age} years old.')
... 
>>> welcome(age=12, name='Mike')
Welcome Mike. You are 12 years old.

在这个例子中,你同时指定了agename 参数。当你这样做时,你可以以任何顺序指定它们。例如,在这里你以相反的顺序指定它们,Python 仍然理解你的意思,因为你指定了两个值。

让我们看看当你不使用关键字参数时会发生什么。

>>> def welcome(name: str, age: int) -> None:
...     print(f'Welcome {name}. You are {age} years old.')
... 
>>> welcome(12, 'Mike')
Welcome 12. You are Mike years old.

当你传入数值而不指定它们应该去哪里时,它们将被按顺序传入。所以name 变成了12age 变成了'Mike'

必需参数和默认参数

默认参数是使你的函数可以用较少的参数来调用的一种方便的方法,而必要参数是你必须传递给函数才能执行的参数。

让我们看一个例子,它有一个必需参数和一个默认参数。

>>> def multiply(x: int, y: int=5) -> int:
...     return x * y
... 
>>> multiply(5)
25

第一个参数,x 是必需的。如果你在没有任何参数的情况下调用multiply() ,你会收到一个错误。

>>> multiply()
Traceback (most recent call last):
  Python Shell, prompt 25, line 1
builtins.TypeError: multiply() missing 1 required positional argument: 'x'

第二个参数y ,不是必需的。换句话说,它是一个默认参数,其中默认的是5 。这使得你可以只用一个参数来调用multiply()!

什么是*args**kwargs

大多数时候,你会希望你的函数只接受少量的参数、关键字参数或两者都接受。你通常不希望有太多的参数,因为以后改变你的函数会变得更加复杂。

然而 Python 确实支持任何数量的参数或关键字参数的概念。

你可以在你的函数中使用这种特殊的语法。

  • *args - 任意数量的参数
  • **kwargs - 任意数量的关键字参数

你需要注意的是*** 。名字,argkwarg 可以是任何东西,但将它们命名为argskwargs 是一种惯例。换句话说,大多数 Python 开发者将它们称为*args**kwargs 。虽然你没有被强迫这样做,但你也许应该这样做,这样代码就容易识别和理解。

让我们看一个例子。

>>> def any_args(*args):
...     print(args)
... 
>>> any_args(1, 2, 3)
(1, 2, 3)
>>> any_args(1, 2, 'Mike', 4)
(1, 2, 'Mike', 4)

这里你创建了any_args() ,它接受包括零在内的任何数量的参数,并将它们打印出来。

实际上,你可以创建一个有一个必要参数加上任意数量的附加参数的函数。

>>> def one_required_arg(required, *args):
...     print(f'{required=}')
...     print(args)
... 
>>> one_required_arg('Mike', 1, 2)
required='Mike'
(1, 2)

所以在这个例子中,你的函数的第一个参数是必需的。如果你在没有任何参数的情况下调用one_required_arg() ,你会得到一个错误。

现在让我们试着添加关键字参数。

>>> def any_keyword_args(**kwargs):
...     print(kwargs)
... 
>>> any_keyword_args(1, 2, 3)
Traceback (most recent call last):
  Python Shell, prompt 7, line 1
builtins.TypeError: any_keyword_args() takes 0 positional arguments but 3 were given

糟糕!你创建的函数是接受关键字参数的。你创建了接受关键字参数的函数,但只传入了普通参数。这导致了一个TypeError

让我们试试把相同的值作为关键字参数传入。

>>> def any_keyword_args(**kwargs):
...     print(kwargs)
... 
>>> any_keyword_args(one=1, two=2, three=3)
{'one': 1, 'two': 2, 'three': 3}

这一次,它按照你所期望的方式工作。

现在让我们检查一下我们的*args**kwargs ,看看它们是什么。

>>> def arg_inspector(*args, **kwargs):
...     print(f'args are of type {type(args)}')
...     print(f'kwargs are of type {type(kwargs)}')
... 
>>> arg_inspector(1, 2, 3, x='test', y=5)
args are of type <class 'tuple'>
kwargs are of type <class 'dict'>

这意味着args 是一个tuplekwargs 是一个dict

让我们看看我们是否可以把tupledict 传递给我们的函数,让它成为*args**kwargs

>>> my_tuple = (1, 2, 3)
>>> my_dict = {'one': 1, 'two': 2}
>>> def output(*args, **kwargs):
...     print(f'{args=}')
...     print(f'{kwargs=}')
... 
>>> output(my_tuple)
args=((1, 2, 3),)
kwargs={}
>>> output(my_tuple, my_dict)
args=((1, 2, 3), {'one': 1, 'two': 2})
kwargs={}

嗯,这并不完全正确。tupledict 最后都在*args 中。不仅如此,tuple 仍然是一个元组,而不是被变成三个参数。

但是,如果你使用特殊的语法,你可以使其工作。

>>> def output(*args, **kwargs):
...     print(f'{args=}')
...     print(f'{kwargs=}')
... 
>>> output(*my_tuple)
args=(1, 2, 3)
kwargs={}
>>> output(**my_dict)
args=()
kwargs={'one': 1, 'two': 2}
>>> output(*my_tuple, **my_dict)
args=(1, 2, 3)
kwargs={'one': 1, 'two': 2}

在这个例子中,你用*my_tuple 调用output() 。Python 将提取tuple 中的各个值,并将它们分别作为参数传入。接下来你传入**my_dict ,它告诉 Python 将每个键/值对作为关键字参数传入。

最后一个例子同时传入tupledict

相当不错!

仅有位置的参数

Python 3.8 为函数增加了一个新的特性,即纯位置参数。这些参数使用一种特殊的语法来告诉 Python,一些参数必须是位置参数,一些必须是关键字参数。

让我们看一个例子。

>>> def positional(name, age, /, a, b, *, key):
...     print(name, age, a, b, key)
... 
>>> positional(name='Mike')
Traceback (most recent call last):
  Python Shell, prompt 21, line 1
builtins.TypeError: positional() got some positional-only arguments passed as 
keyword arguments: 'name'