这章内容较多,开始进入高阶部分。
定义函数
先看一个没有参数的函数:
$ cat bell.py
def bell():
""" voice of my doorbell"""
print("Ding Dong!")
bell()
$ python3 bell.py
Ding Dong!
函数的定义以def
开始,以:
结束。函数体从其下带缩进的行开始,直到遇到没有缩进的行函数体结束。
"""
和"""
之间是注释,用于生成文档,这和#
不同。
参数定义在(
和)
之间,用逗号分割,只要参数名,无需类型。
$ cat echo.py
def echo(msg):
print(msg)
echo("Hello!")
echo("See you!")
$ python3 echo.py
Hello!
See you!
在函数定义中的参数称为parameters(形参),在函数调用时传入的参数称为arguments(实参)。
传递参数
第一种方式为按照位置传递,最常见。但注意参数顺序不要搞错,否则可能出现逻辑错误。
第二种方式按照形参的关键字传递,定义时并无不同,但调用时如下:
func(parameter1=value1, parameter2=value2, ...)
参数可定义默认值。默认值使此参数变为可选:
def func(parameter1, parameter2=default_value)
例如:
def echo(msg, where='BJ'):
print(where + ":" + msg)
echo("Hello!")
echo("See you!", 'SH')
带默认值参数的定义只能在非默认值参数定义之后:
$ cat echo.py
def echo(msg='empty', where):
print(where + ":" + msg)
$ python3 echo.py
File "echo.py", line 1
def echo(msg='empty', where):
^
SyntaxError: non-default argument follows default argument
有一个通用的默认值就是None,相对于SQL中的Null,例如:
def func(a, b=None, c=None)
然后在函数体中可以结合条件表达式判断:
if b: # 如果b有赋值,如果b非空
看一个测试:
>>> a=not True
>>> a
False
>>> a=not None
>>> a
True
>>> a=not ''
>>> a
True
>>> a=not 'value'
>>> a
False
返回值
返回值通过return
返回,在函数定义时没有区别。
$ cat sum.py
def sum(a, b):
return a + b
print(f"result is: {sum(1, 2)}")
$ python3 sum.py
result is: 3
返回值可返回简单类型,如数值,字符串。也可返回复杂类型,如List,字典,Tuple,Set等。
例如:
$ cat returntest.py
def returntest():
#return set({1,2,3})
#return {1,2,3}
#return 1,2,3
return {"k1":1, "k2":2, "k3":3}
print(returntest())
输出分别为:
{1, 2, 3}
{1, 2, 3}
(1, 2, 3)
{'k1': 1, 'k2': 2, 'k3': 3}
传递列表
列表作为参数时,传递的是reference,而非value,因此列表在函数中可修改。
$ cat friends.py
def add_friends(friendslist, friendname):
friendslist.append(friendname)
def del_friends(friendslist, friendname):
friendslist.remove(friendname)
my_friends = ['tom', 'jerry']
add_friends(my_friends, 'flora')
add_friends(my_friends, 'grace')
del_friends(my_friends, 'jerry')
print(my_friends)
$ python3 friends.py
['tom', 'flora', 'grace']
如果你希望列表不被改变,则可以传递列表的拷贝,例如:
add_friends(my_friends[:], 'flora')
add_friends(my_friends[:], 'grace')
del_friends(my_friends[:], 'jerry')
这里说的List是广义的,传递字典也是by reference:
$ cat dictparam.py
def addobject(dict):
dict['kn'] = 'vn'
d = {'k1':'v1', 'k2':'v2'}
addobject(d)
print(d)
$ python3 dictparam.py
{'k1': 'v1', 'k2': 'v2', 'kn': 'vn'}
传递不定数量参数
有两种方式,*
和**
。
第一种方式其实挺常见的,例如C语言中的main( int argc, char *argv[] )
用*parameter
定义,实际上传递的是tuple(元组),因此在函数体中可以用for...in
遍历。
不定数量参数只能定义在参数列表的最后。为不定数量参数指定默认值是错误的,也是不必要的。
$ cat friends.py
def add_friends(friendslist, *friendsname):
for friend in friendsname:
friendslist.append(friend)
my_friends = ['tom', 'jerry']
add_friends(my_friends, 'flora', 'grace')
print(my_friends)
$ python3 friends.py
['tom', 'jerry', 'flora', 'grace']
第二种方式用**parameter
定义,实际传递的是一系列键值对,如key=value, key=value, ...
$ cat cars.py
def cars(vendor, model, **carinfo):
carinfo['vendor'] = vendor
carinfo['model'] = model
return carinfo
print(cars('BMW', 'SUV'))
print(cars('TOYOTA', 'pickup', color='white', price=1000));
$ python3 cars.py
{'vendor': 'BMW', 'model': 'SUV'}
{'color': 'white', 'price': 1000, 'vendor': 'TOYOTA', 'model': 'pickup'}
在本节Pizza示例中,提到一个单词topping,意为配料,翻译为浇头(或湖南人说的码子)就更好理解了。
在模块中存放函数
函数可重用,使程序变得简洁。
函数可存于单独的文件,称为module
,然后通过import
引入调用其的文件。
import modulename
调用时使用以下形式:
module_name.function_name()
示例:
$ cat cars_module.py
def cars(vendor, model, **carinfo):
carinfo['vendor'] = vendor
carinfo['model'] = model
return carinfo
$ cat main.py
import cars_module
print(cars_module.cars('BMW', 'SUV'))
print(cars_module.cars('TOYOTA', 'pickup', color='white', price=1000));
$ python3 main.py
{'vendor': 'BMW', 'model': 'SUV'}
{'color': 'white', 'price': 1000, 'vendor': 'TOYOTA', 'model': 'pickup'}
也可以import指定的函数:
from module_name import function_0[, function_1, function_2 ...]
如果import的函数命名与本文件冲突,则可以指定别名:
from module_name import function_0 as alias
module也可指定别名:
import modulename as module_alias
import所有函数:
from module_name import *
这种方式和import module_name
有区别,在调用时无需指定module名称和.
。
$ cat main.py
from cars_module import *
print(cars('BMW', 'SUV'))
print(cars('TOYOTA', 'pickup', color='white', price=1000));
但这种方式是不建议的,特别是比较大的项目,还是建议import指定的函数或模块。
函数的风格
以下都是最佳建议,即约定,不按照做并不会导致错误。
函数名应准确描述其功能,只应包含小写和下划线(_
)。
函数必须包括说明,使用docstring
格式,也就是"""
。
参数赋予默认值时,等号两侧不要留空格。例如param=None
而非param = None
。
函数的参数可另起一行,带两个<tab>
缩进。
函数与函数间带两个空行。
import语句应放在文件首,除非有注释。