【Python编程从入门到实践】函数、传参、lambda、导入函数模块

第八章.函数

通过使用函数,程序的编写、阅读、测试和修复都将更容易

Python常用函数说明文档

8.1 定义函数def

使用关键词def告诉Python你要定义一个函数

函数定义:def 函数名(参数):

函数定义代码放在调用代码前面

# 定义函数
def greet_user(usename):
    """显示简单的问候语"""
    print("Hello, " + usename.title() + "!")

# 调用函数
greet_user('jesse') 

文档字符串(docstring),描述函数是做什么的,文档字符串用三引号括起来,如"""显示简单的问候语"""

在greet_user(‘jesse’)中,实参’jesse’传递给了函数greet_user(),这个值被存储在形参username中

8.3 返回值 直接return

函数可以返回任何类型的值,包括列表和字典等较复杂的数据结构

8.3.1 返回简单值

在函数中,可使用return语句将值返回到调用函数的代码行

def my_abs(x):
    if (x >= 0):
        return x
    else:
        return -x

res = my_abs(-3)
print(res) # 输出3

8.3.2 让实参变成可选的

给形参middle_name指定一个默认值——空字符串,并将它放在形参列表的末尾

这样调用时可以传两个参数,也可以传3个参数

传参数有两种方式

get_formatted_name('jimi', 'hendrix')
get_formatted_name(first_name='jimi', last_name='hendrix')
# 实参可选
def get_formatted_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()


print(get_formatted_name('jimi', 'hendrix'))  # 输出 Jimi Hendrix
print(get_formatted_name('john', 'hooker', 'lee'))  # 输出 John Lee Hooker

8.3.3 返回字典

def build_person(first_name, last_name, age=''):
    """返回一个字典,其中包含有关一个人的信息"""
    person = {'first': first_name, 'last': last_name}
    if age:
        person['age'] = age
    return person


musician = build_person('jimi', 'hendrix', 27)
# 或 musician = build_person(first_name='jimi', last_name='hendrix', age=27) 
# 这是其实参数的顺序可以随意

print(musician)  # 输出 {'first': 'jimi', 'last': 'hendrix', 'age': 27}

注意:这里虽然默认age=''是字符串,但是你可以输入一个int数值,相当于age = 27

8.3.4 pass空语句 用来占位

pass语句在函数中的作用:当你在编写一个程序时,执行语句部分思路还没有完成,这时你可以用pass语句来占位,保证格式完整,也可以当做是一个标记,标记这是我之后要来完成的函数

def isDigit():
    pass  # do nothing

定义一个函数isDigit,但函数体部分暂时还没有完成,又不能空着不写内容,因此可以用pass来替代占个位置

Python中的pass 类似C++中;

if(a == 2) {
; // do nothing
} else {
cout << a << endl; 
}

8.4 传递列表

8.4.1 在函数中修改列表

需要打印的设计存储在一个列表中,打印后移到另一个列表中

编写两个函数,实现上面的需求

# 定义函数-传递列表
def print_models(unprinted_designs, completed_models):
    """
    模拟打印每个设计,直到打印没有未打印的设计为止
    打印每个设计后,都将其移到列表completed_models中
    """
    while unprinted_designs:
        current_design = unprinted_designs.pop()
        print("Printing model: " + current_design)
        completed_models.append(current_design)


def show_completed_models(completed_models):
    """显示打印好的所有模型"""
    print("\nThe following models have been printed:")
    for completed_model in completed_models:
        print(completed_model)


unprinted_designs = ['iphone', 'robot pendant', 'dodecahedron']
completed_models = []

# 调用上面定义的两个函数
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)

描述性的函数名让别人阅读这些代码时更清晰

用了函数的好处:如果我们需要对打印代码进行修改,只需修改这些代码一次,就能影响所有调用该函数的地方,效率很高

理念:每个函数都应只负责一项具体工作,这由于使用一个函数来完成两项工作

8.4.2 禁止函数修改原列表

有时需要禁止函数修改原列表,可以像函数传递列表的副本而不是原件,这样函数所做的任何修改都只影响副本,而丝毫不影响原件

切片表示法[:]创建列表副本
如可以把上面例子中的调用修改为

print_models(unprinted_designs[:], completed_models)

这样原列表unprinted_designs不会被修改

但是一般不建议创建副本,除非特别需要。因为让函数使用现成的列表可避免花时间和内存创建副本,从而提高效率,在处理大型列表时尤其如此

8.5 传任意多的实参*toppings元组

有时你预先不知道函数需要接受多少个实参,好在Python允许函数从调动语句中收集任意数量的实参

以制作披萨为例
我们无法预先知道顾客要多少种配料

形参*toppings中的星号让Python创建一个名为toppings的空元组,并将接收的所有值都封装到这个元组中

def make_pizza(*toppings):
    """打印顾客点的所有配料"""
    print(toppings)


make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')

输出

('pepperoni',)
('mushrooms', 'green peppers', 'extra cheese')

8.6 传递任意多的实参**user字典

有时你想收集有关用户的信息,但不确定会是什么样的信息。

在下面的例子中,函数build_profile()接受名first_name和姓last_name,同时还接受任意数量的关键字实参:

形参**user_info中的两个星号让Python创建一个名为user_info的空字典,并将收到的所有键-值对,如field='physics',都封装在这个字典里

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('albert', 'einstein', location='princeton', field='physics')
print(user_profile)  
# 输出 {'first_name': 'albert', 'last_name': 'einstein', 'location': 'princeton', 'field': 'physics'}

8.7 lambda表达式

python 使用 lambda 来创建匿名函数

使用lambda比普通的def定义函数要简单,不需要写def和return

lambda函数一般功能简单:单行expression决定了lambda函数不可能完成复杂的逻辑,只能完成非常简单的功能

lambda 函数可以接收任意多个参数 (包括可选参数) 并且返回单个表达式的值

带参数

f = lambda x, y: 2 * x + y
print(f(4, 3))  # 输出11

不带参数

R = lambda : print("Hi")
R() # 输出 Hi

例:

# 输入样例:
# 4 3
# 0 0 2 1
# 2 0 1 1

R = lambda: map(int, input().split())
# R()读入一行数,R() = map(int, input().split()) # 迭代器
n, m = R() # n = 4, m = 3
list_a, list_b = list(R()), list(R())  # 读入两行数
# list_a = [0, 0, 2, 1]

说明:函数名可以赋给变量,简化有些赋值的函数名,当然这会降低可读性

s = sorted
print(s([2,3,1])) # 输出 [1, 2, 3]

8.8 将函数存储在模块中

导入模块相对于把模块中的代码隐形地复制到当前程序中,所以如果模块开头有一句print(“Hello”),则当运行import语句时就会输出Hello

强力推荐的写法
用多少func.py中函数,就导入多少

from func import function_1 as f1, function_2 as f2, function_3 as f3

将函数存储在被称为模块的独立文件中,再将模块导入主程序中,import语句运行在当前运行的程序文件中使用模块中的代码

将函数存储在独立的文件中后,可与其他程序员共享这些文件而不是整个程序,还可以让你知道如何使用其他程序员编写的函数库

一个标准库文件中的import语句示例:

import _collections_abc
from operator import itemgetter as _itemgetter, eq as _eq
from keyword import iskeyword as _iskeyword
import sys as _sys
import heapq as _heapq
from _weakref import proxy as _proxy
from itertools import repeat as _repeat, chain as _chain, starmap as _starmap
from reprlib import recursive_repr as _recursive_repr

sys.path.append()
当我们需要添加自己的搜索目录时,可以通过列表的append()方法,特别是模块和自己写的程序不在同一个目录的情况

import sys
sys.path.append('/Users/macos/Documents/Wilson79/Python/Program')

8.8.1 导入整个模块import(同一目录)

模块是扩展名为.py的文件

下面例子中,Python读取文件时,代码行import models让Python打开同一目录下的models.py,并将其中的所有函数都复制到这个程序中。你看不到复制的代码,因为这是幕后复制的,你只需要知道你可以使用models.py中定义的所有函数

调用方法:模块名.函数名(参数)

main.py

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Author    :   Wilson79
@Filename  :   main.py.py  
@Date      :   2020/1/22 下午06:45 
"""
import models  # 让Python打开models.py(必须在同一目录下),并将其中的所有函数复制到当前程序中

unprinted_designs = ['iphone', 'robot pendant', 'dodecahedron']
completed_models = []

# 调用方法:模块名.函数名(参数)
models.print_models(unprinted_designs[:], completed_models)  # 创建列表副本(一般不建议)
models.show_completed_models(completed_models)

models.py

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Author    :   Wilson79
@Filename  :   models.py  
@Date      :   2020/1/22 下午06:44 
"""


def print_models(unprinted_designs, completed_models):
    """
    模拟打印每个设计,直到打印没有未打印的设计为止
    打印每个设计后,都将其移到列表completed_models中
    """
    while unprinted_designs:
        current_design = unprinted_designs.pop()
        print("Printing model: " + current_design)
        completed_models.append(current_design)


def show_completed_models(completed_models):
    """显示打印好的所有模型"""
    print("\nThe following models have been printed:")
    for completed_model in completed_models:
        print(completed_model)

搜索路径
当你导入一个模块,Python 解析器对模块位置的搜索顺序是:

1、当前目录
2、如果不在当前目录,Python 则搜索在 shell 变量 PYTHONPATH 下的每个目录。
3、如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/。
模块搜索路径存储在 system 模块的 sys.path 变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。

8.8.2 导入特定函数from module import fun()

有时不需要导入全部的函数,可以只导入特定的函数

语法:
from module_name import function_1, function2, function3
注意:只需写出函数名,不用加()

调用方法
直接使用函数,不需使用句号,因为import语句显式地导入了要用的函数

示例:
new.py

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Author  :   Wilson79
@File    :   new.py  
@Time    :   2020/1/19 下午03:45 
"""

from func import function_1, function_2

function_1()  # import显式导入了函数,故可直接使用
function_2()
# function_3() # error,因为没有导入这个函数

func.py

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Author    :   Wilson79
@Filename  :   func.py
@Date      :   2020/1/22 下午07:00 
"""


def function_1():
    print("星期一")


def function_2():
    print("星期二")


def function_3():
    print("星期三")

8.8.3 用as给模块或函数指定别名

给模块指定别名
import module_name as p
说明:方便你更轻松地调用模块中的函数 p.function(),让你更专注于描述性的函数名,而不是模块名

给函数指定别名
from module_name import function_name as fn
说明:防止导入函数与当前程序冲突,或函数的名字太长

8.8.4 导入模块中的所有函数from module_name import *

使用*运算符可以让Python导入模块中的所有函数
from module_name import *

由于导入了所有函数,不需要使用句点表示法,而是直接使用函数

new.py

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Author  :   Wilson79
@File    :   new.py  
@Time    :   2020/1/19 下午03:45 
"""

from func import *  # 导入func所有函数,且可以直接使用

function_1()  # import显式导入了所有函数,故可直接使用
function_2()
function_3()

特别注意:如果不是自己编写的函数库,不建议使用这种导入方式
因为模块中有函数名称与你的项目中的函数使用相同的名称时,会出现意想不到的结果:Python遇到相同名称的函数会覆盖函数,而不是导入所有的函数

8.8.5 Python会覆盖原先定义的函数

from func import *  # 导入func所有函数,且可以直接使用

# import显式导入了所有函数,故可直接使用
function_1()  # 输出 星期一


# Python会覆盖原先定义的函数
def function_1():
    print("你好")  # 输出 你好


function_1()


def function_1():
    print("函数")


function_1()  # 输出 函数

写在最后:我的博客主要是对计算机领域所学知识的总结、回顾和思考,把每篇博客写得通俗易懂是我的目标,分享技术和知识是一种快乐 ,非常欢迎大家和我一起交流学习,有任何问题都可以在评论区留言,也期待与您的深入交流(^∀^●)

发布了239 篇原创文章 · 获赞 80 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq_43827595/article/details/104293814