之前有幸学过一个月时间的 Python,到了函数就不行了,什么传参这那的一堆,给我整个懵逼住了,那时候 shell 还是一知半解呢,函数对于我来说太深奥了,知道搞懂 shell 的函数之后,再回想起 Python 的这些内容,会好理解一点。
我这真的是靠着 Shell 打拼天下啊。
函数在每个语言中的功能都类似,比如修改代码容易,重复使用容易,减少代码量等等。
定义函数
Python 中使用 def
关键字来高速 Python 即将定义一个函数,Shell 中是 function
def hello():
print('Hello World!')
hello()
---
# 运行结果
Hello World!
def
后的 hello()
是自定义的函数名,print('Hello World!')
是函数体内的一行代码
执行 hello()
将会调用自定义函数,并运行
向函数传参
参数将会传递到函数名的括号中,比如 print('Hello World!')
,其中 Hello World!就是 print() 的参数
def hello(name):
print('Hello,' + name.title())
hello('feiyi')
---
# 运行结果
Hello,Feiyi
def hello(name):
name 看作一个变量名,但还没有值,需要调用函数时传递值给变量 name
print('Hello,' + name.title())
函数体内的代码,将会调用 name 参数的值
hello('feiyi')
将 'feiyi'
传递给函数 hello(name) 中的 name ,并执行函数
实参和形参
调用函数时要传递的值叫实参,如:hello('feiyi')
中的 'feiyi'
就是实参。
定义函数时,定义的变量叫形参,如:def hello(name):
中的 name
就是形参。
其实函数中的变量和值就是实参和形参,也有很多人将这两个概念搞混,无所谓,认识就行了。
传递实参
在复杂的函数中可能包含多个形参,在函数调用中也包含多个实参。
传递参数的方式有很多种,可使用位置实参/关键字实参/列表和字典。
位置实参:实参顺序和形参顺序相同
关键字实参:每个实参都由变量名和值组成
位置实参
很好理解,在定义函数时,按照形参的顺序传递实参
def like_city(name, city):
print(city + ' 是 ' + name.title() + ' 最喜欢的地方')
like_city('feiyi' ,'四川达州')
---
# 运行结果
四川达州 是 Feiyi 最喜欢的地方
如上所示,调用函数传实参时,必须对应定义函数的形参的顺序,函数体内使用参数的顺序无所谓。
关键字实参
关键字实参其实就是对形参进行直接赋值。
还是位置实参的例子,先看一下将位置实参调换位置的效果
def like_city(name, city):
print(city + ' 是 ' + name.title() + ' 最喜欢的地方')
like_city('四川达州', 'feiyi')
---
# 运行结果
feiyi 是 四川达州 最喜欢的地方
关键字实参就可以完全的避免这种搞笑的操作,如下
def like_city(name, city):
print(city + ' 是 ' + name.title() + ' 最喜欢的地方')
# 调用函数时,直接对形参赋值
like_city(city='四川达州', name='feiyi')
---
# 运行结果
四川达州 是 Feiyi 最喜欢的地方
实参默认值
在定义函数时,直接给形参一个默认值,意思是在没有给该形参传实参时,默认的值
def like_city(name, city='四川达州'):
print(city + ' 是 ' + name.title() + ' 最喜欢的地方')
# 调用两次函数,使用位置实参和关键字实参的方法
like_city('feiyi', '北京')
like_city(name='feiyi')
---
# 运行结果
北京 是 Feiyi 最喜欢的地方
四川达州 是 Feiyi 最喜欢的地方
如上所示的两次调用函数中,给默认值的形参重新赋值,是可以覆盖默认值的。
返回值
函数并非总是直接显示输出,相反,它可以处理一些数据,并返回一个或一组值。函数返回的值被称为返回值。在函数中,可使用 return 语句将值返回到调用函数的代码行。返回值能够将程序的大部分繁重的工作已到函数中去完成,从而简化主程序。
首先说明, retrun 和 print 是不一样的,print 可以直接将内容打印,return 必须借助 print 才会看到返回值
def get_name(name):
return name.title()
a = get_name('feiyi')
print(a)
---
# 运行结果
Feiyi
可选形参
在获取名字这中需求中,如果是外国人的名字中,会分为姓、中间名、名,怎么才能让函数即支持姓、名,又支持姓、中间名、名呢,前面提到形参可以设置一开始的默认值,同样也可以给形参的默认值为空。这样可以让调用函数时,某一个形参为
def get_name(first_name, last_name, middle_name=""):
if middle_name:
full_name = first_name + ' ' + middle_name + ' ' + last_name
else:
full_name = first_name + ' ' + last_name
return full_name.title()
Get_name = get_name('chai', 'feiyi')
print(Get_name)
Get_name = get_name('chai', 'yi', 'fei')
print(Get_name)
---
# 运行结果
Chai Feiyi
Chai Fei Yi
python 将非空字符串解读为 True,所以在 if middle_name:
判断时,默认是 False,赋予实参时,则为 True。
返回字典
函数可以返回任何类型的值,包括列表和字典等
def get_name(first_name, last_name, age=''):
message = {'first': first_name, 'last': last_name}
if age:
message['age'] = age
return message
Get_name = get_name('chai', 'feiyi', age=15)
print(Get_name)
---
# 运行结果
{'first': 'chai', 'last': 'feiyi', 'age': 15}
函数结合 while 循环
自定义函数加 input 函数和 while循环,可以做到交互
def get_name(name):
return name.title()
while True:
my_name = input('请输入你的名字(输入q退出):')
if my_name == 'q':
break
else:
full_name = get_name(my_name)
print('Hello, ' + full_name)
---
# 运行结果
请输入你的名字(输入q退出):feiyi
Hello, Feiyi
请输入你的名字(输入q退出):mupei
Hello, Mupei
请输入你的名字(输入q退出):q
返回值的练习
编写一个名为 city_country() 的函数,返回值为城市和国家
def city_country(city, country):
fromcity = city.title() + ', ' + country.title()
return fromcity
result = city_country('北京', 'china')
print(result)
result = city_country('达州', 'china')
print(result)
result = city_country('西安', 'china')
print(result)
编写一个名为 make_album() 的函数,返回值为字典类型,返回值为歌手、专辑名、专辑歌曲数量,专辑歌曲数量为可选参数
def make_album(sing_name, album_name, album_num=''):
album_info = {'singer': sing_name, 'album': album_name}
if album_num:
album_info['al_num'] = album_num
return album_info
while True:
print('输入q退出')
singer = input('请输入歌手名:')
if singer == 'q':
break
album = input('请输入 ' + singer + ' 的专辑名:')
if album == 'q':
break
num = input('请输入 《' + album + '》 的歌曲数量(不知道回车跳过):')
if num == 'q':
break
elif num:
result = make_album(singer, album, num)
else:
result = make_album(singer, album)
print('\n-----------------------------------------------------')
print(result)
print('-----------------------------------------------------\n')
传递列表
使用函数提高处理列表的效率。将列表传递给函数后,函数就能直接访问列表内容
# 一个用户列表,通过函数向列表中每位用户打招呼
def greet_user(names):
for name in names:
msg = "Hello, " + name.title() + "!"
print(msg)
usernames = ["feiyi", "chai", "pei"]
greet_user(usernames)
---
# 运行结果
Hello, Feiyi!
Hello, Chai!
Hello, Pei!
函数修改列表
函数中对列表的修改是永久性的,可以高效地处理大量数据。
准备一个列表用于存储想要去的城市,另一个列表用于存储已经去过的城市,不使用函数如下:
want_cities = ['达州', '西安', '北京', '太原']
cities_visited = []
while want_cities:
current_city = want_cities.pop()
print("我已经去过 " + current_city + " 了")
cities_visited.append(current_city)
print("\n以下是我去过的城市:")
for city in cities_visited:
print("\t" + city)
---
# 运行结果
我已经去过 太原 了
我已经去过 北京 了
我已经去过 西安 了
我已经去过 达州 了
以下是我去过的城市:
太原
北京
西安
达州
不使用函数的情况用了2种循环,while 和 for,所以就使用2个函数来做到以上效果
def a_cities(want_cities, cities_visited):
while want_cities:
current_city = want_cities.pop()
print("我已经去过 " + current_city + " 了")
cities_visited.append(current_city)
def b_cities(cities_visited):
print("\n以下是我去过的城市:")
for city in cities_visited:
print("\t" + city)
want_cities = ['达州', '西安', '北京', '太原']
cities_visited = []
a_cities(want_cities, cities_visited)
b_cities(cities_visited)
---
# 运行结果
我已经去过 太原 了
我已经去过 北京 了
我已经去过 西安 了
我已经去过 达州 了
以下是我去过的城市:
太原
北京
西安
达州
其实大部分代码是相同的,有点在于效率更高,程序容易理解,后期扩展维护方便快捷。
该程序也遵循了函数的理念,每个函数只负责一项具体的工作。方便重复使用某一项工作。
禁止修改列表
以上面的例子为前提,将城市输出之后,保留原有变量中的内容,以供备份。
这个问题可以向函数传递列表的副本而不是原件;这样函数做的修改只会影响到副本。只需要在使用函数传参时使用切片的表示符号创建列表的副本
a_cities(want_cities[:], cities_visited)
注意
虽然可以使用副本保留原变量的内容不变,没有必要的理由还是不建议使用该方法,如果一个大的列表做副本的话,程序运行起来会消耗内存,从而降低效率。
任意数量的列表实参
在前面函数的使用中,函数的形参和实参的数量在没有默认形参的情况下是相等数量的,有时候,你预先不知道函数需要接受多少个实参,Python 允许函数从调用语句中收集任意数量的实参。
例如:统计每个人去过的城市,但每个人去过的城市数量又都不一样,下面的函数只有一个形参 *cities
,不管有多少实参(一个或者多个),该形参都将保存,也就是会形成一个列表。
def goed_city(*cities):
print('我去过的城市有以下几个:')
for city in cities:
print('- ' + city)
goed_city('达州', '西安', '北京')
---
# 运行结果
我去过的城市有以下几个:
- 达州
- 西安
- 北京
这样其实只是省去了上面示例中的定义列表的步骤,*cities
的星号让 Python 创建一个名为 cities
的空列表,将函数接受的所有实参都加入进去
结合多种形式实参
结合这两种形式的实参,任意数量实参的形参必须放在函数定义形参的最后一个。Python 先匹配位置实参和关键字实参,再将剩余所有的任意数量实参收集到最后一个形参中。
def goed_city(num, *cities):
print('我去过的城市有以下 ' + num + ' 个:')
for city in cities:
print('- ' + city)
goed_city('3', '达州', '西安', '北京')
---
# 运行结果
我去过的城市有以下 3 个:
- 达州
- 西安
- 北京
任意数量的字典实参
除了上面提到的将任意数量的实参会传递给列表之外,也可以通过 **cities
中两个星号让 Python 创建一个名为 cities
的空字典
例如:收集个人信息,字典中存储所在地以及年龄
def build_profile(first, last, **user_info):
profile = {}
profile['first_name'] = first
profile['last_name'] = last
for key, value in user_info.items():
profile[key] = value
return profile
user_profile = build_profile('柴', '艳江', Location='北京', age=25)
print(user_profile)
---
# 运行结果
{'first_name': '柴', 'last_name': '艳江', 'Location': '北京', 'age': 25}
将函数存储在模块中
函数也可以和主程序分离,通过 imiport
将函数所在的文件导入到主程序中运行,这样也可以隐藏主程序的细节,这样的文件以 .py
为后缀,就被叫做模块
导入整个模块
以上面去过的城市为例,创建一个 goed_cities.py
的文件并将函数放进去
goed_cities.py
--------------
def goed_city(num, *cities):
print('我去过的城市有以下 ' + num + ' 个:')
for city in cities:
print('- ' + city)
在该文件同级目录创建主程序文件 main.py
,然后导入刚创建的模块,使用函数时,需要加模块名
main.py
-------
import goed_cities
goed_cities.goed_city('3', '达州', '西安', '北京')
goed_cities.goed_city('4', '达州', '西安', '北京', '太原')
---
# 运行结果
我去过的城市有以下 3 个:
- 达州
- 西安
- 北京
我去过的城市有以下 4 个:
- 达州
- 西安
- 北京
- 太原
import goed_cities
让 Python 打开 goed_cities.py
文件,并将该文件所有函数都复制到程序中,该复制是不可见的,只需要知道可以使用 goed_cities.py
中的所有函数即可
导入模块中特定函数
如果一个模块中函数数量较大,但用到的函数很少,可以只导入模块中的特定函数
格式
from 模块名 import 函数名, 函数名1, 函数名2
示例
from goed_cities import goed_city
goed_city('3', '达州', '西安', '北京')
goed_city('4', '达州', '西安', '北京', '太原')
多个特定模块,只需要用逗号分隔即可
from goed_cities import goed_city, goed_city1, goed_city2
as 给函数指定别名
如果导入的函数名称可能与程序中其他名称有冲突的情况下,可以指定别名来使用
格式
from 模块名 import 函数名 as 别名
示例
from goed_cities import goed_city as a
a('3', '达州', '西安', '北京')
a('4', '达州', '西安', '北京', '太原')
as 给模块指定别名
格式
import 模块名 as 别名
示例
import goed_cities as c
c.goed_city('3', '达州', '西安', '北京')
c.goed_city('4', '达州', '西安', '北京', '太原')
导入模块所有函数
使用星号(*)可以 Python 导入模块中的所有函数
格式
from 模块名 import *
示例
from goed_cities import *
goed_city('3', '达州', '西安', '北京')
goed_city('4', '达州', '西安', '北京', '太原')
无论在使用第三方模块还是自己编写的模块时,要考虑模块名和函数名与项目中其他名称的冲突,否则会产生意想不到的后果,要么只导入所使用的函数,要么导入整个模块并使用句点表示法——import 模块名