之前有幸学过一个月时间的 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 *

与 import 模块名 的区别

import 模块名 在调用函数时,模块名.函数名()

from 模块名 import * 在调用函数时,函数名()

示例

from goed_cities import *

goed_city('3', '达州', '西安', '北京')
goed_city('4', '达州', '西安', '北京', '太原')

无论在使用第三方模块还是自己编写的模块时,要考虑模块名和函数名与项目中其他名称的冲突,否则会产生意想不到的后果,要么只导入所使用的函数,要么导入整个模块并使用句点表示法——import 模块名

评论




正在载入...
PoweredHexo
HostedAliyun
DNSAliyun
ThemeVolantis
UV
PV
BY-NC-SA 4.0