4. Python3 可复用的函数与模块

函数是一组语句的集合,用以实现某一特定的功能。函数可以简化脚本,Python本身提供了许多内置函数,极大方便了脚本的编写。例如,可以使用print()函数输出计算结果,使用input()函数接收用户的输入。除系统内置的函数外,程序员还可以根据需要编写自己的函数。

当自定义的函数很多时,为了方便地管理这些函数,可将函数分类保存到不同的模块中。因此,模块可以看作一组函数的集合。很多函数库在Python中士以模块的形式提供的,例如,系统中的os模块提供了对系统操作的一系列函数。

4.1 Python自定义函数

在编写脚本的过程中,经常要完成许多重复的工作。此时就可以将完成重复工作的语句提取出来,编写为函数。在脚本中可以方便地调用函数来完成重复的工作,而不必重复地复制、粘贴代码。

在Python中,函数必须先定义,然后才能在脚本中使用。在使用函数时,只要按照函数定义的形式向函数传递必需的参数,就可以调用函数实现所需的功能。

4.1.1 函数的定义

在Python中,使用关键字def可以定义一个函数。完整的函数是由函数名、参数及函数实现语句组成的。如果函数有返回值,那么需要在函数中使用return语句返回计算结果。定义函数的语法结构如下:

def <函数名>(参数列表):

       <函数语句>

       return<返回值>

其中,参数和返回值不是必需的。很多函数可能既不需要传递参数,又没有返回值。例如:

#hi是定义函数的函数名,以便于在脚本中使用该函数
#虽然不需要传递参数,但在函数定义的时候依然要在函数名后跟一对圆括号

def hi():
    print("hi,python!")        #缩进的语句,表示是函数内的语句
                               #函数没有使用return定义返回值

下列是简单函数的例子:

# 定义函数的基本语法:
# 函数名 (参数列表) -> 返回类型:
# 函数体

def function_name(parameters):
    """
    函数文档字符串(可选,用于说明函数功能和使用方法):
    """
    # 函数体内执行的代码
    code_to_execute
    return [expression]  # 如果有返回值

# 详细代码示例: 定义一个简单的加法函数,接受两个数作为输入并返回它们的和
def add_numbers(num1, num2):
    """
    这个函数接收两个数字作为参数,返回它们的和。

    参数:
    num1 (int或float): 第一个数字
    num2 (int或float): 第二个数字

    返回:
    int或float: 两个数字的和
    """
    result = num1 + num2
    return result

# 调用函数
sum_of_two = add_numbers(3, 5)
print("The sum of", num1, "and", num2, "is", sum_of_two)

与C语言想比,在Python中不需要定义参数和返回值的类型。python在实际调用函数的过程中也非常灵活,不需要为不同类型的参数定义多个函数,或者在处理不同类型数据的时候调用相应的函数。在大部分情况下可以用同一个函数调用不同的数据类型。

如下所示的函数,其功能是打印参数对象中的所有成员。可以看到,不管参数是一个列表还是一个元组,函数都能被正确的执行。

4.1.2 函数调用

在Python中调用自定义函数与调用系统内置函数的方法相同,只需要使用函数名指定要调用的函数,然后在函数名后的圆括号中给出函数的参数即可。如果有多个参数,则不同的参数要以“,”隔开。

需要注意的是,即使函数不需要参数,也要在参数名后使用一对空的圆括号。另外,函数调用必须在函数声明之后。函数调用代码示例:

# 定义一个简单的函数,接受一个参数并返回其平方
def square(num):
    return num ** 2

# 直接调用函数,传入一个数值
result = square(5)  # 结果是 25
print(result)

# 可选的,使用括号表示调用,即使不传递参数
print(square())  # 如果函数没有默认参数,会抛出 TypeError,因为平方函数需要一个参数

# 使用默认参数,当不提供参数时使用默认值
def greet(name="World"):
    return f"Hello, {name}!"

greet()  # 输出 "Hello, World!"
greet("Alice")  # 输出 "Hello, Alice!"

# 通过关键字参数指定参数名
greet(name="Bob")  # 输出 "Hello, Bob!"

# 函数可以作为另一个函数的参数
def apply_operation(func, x, y):
    return func(x, y)

addition = lambda a, b: a + b
subtraction = lambda a, b: a - b

print(apply_operation(addition, 5, 3))  # 输出 8
print(apply_operation(subtraction, 10, 2))  # 输出 8
4.2 参数让函数更有价值

在调用某些函数时,既可以向其传递参数,也可以不传递参数,函数依然可以正确调用。当函数中 的参数数量不确定时,可能是1个或几十个时,应该如何定义参数呢?

4.2.1 有默认值的参数

定义函数时可以预先为参数设置一个默认值。当调用函数时,如果某个参数具有默认值,则可以不向函数传递该参数,这时,函数将使用定义函数时为该参数设置的默认值。定义一个参数具有的函数形式如下:

def<函数名>(参数=默认值):

     <语句>

以下代码定义了一个函数:

如果1个函数具有多个参数,并且这些参数都具有默认值,在调用函数的时候,可能仅想向最后一个参数传递值,看下列代码:

从上述代码中可以看出,在Python中是按照定义函数时设置的参数顺序依次传递参数的。如果需要向指定参数传递值,可以使用以下方式重新定义函数。

4.2.2 参数的传递方式

参数的传递分两种,一是按顺序传递参数,二是按参数名传递参数,不必按函数定义时设置的参数顺序。

而调用函数可以同时使用按顺序传递参数和按照参数名传递参数两种方式。需要注意的是,按顺序传递的参数要位于按参数名传递的参数之前,而且不能有重复的情况,代码如下:

在具有默认参数值的函数中,使用按参数名向函数传递参数的方式,不必在函数定义时将参数默认值设置为None,可以省去函数中的判断语句。不过,为了让函数更具通用性,仍可以将参数的默认值设置为None。

4.2.3 如何传递任意数量的参数

函数可以具有多个参数,而不必在定义函数时对所有参数进行定义。在使用可变长参数的函数时,其所有参数都保存在一个元组里,在函数中可以使用for循环来处理。在定义函数时,如果参数名前加上一个星号“*”,则表示该参数是一个可变长参数,示例代码如下:

4.2.4 用参数返回计算结果

在参数中使用可变对象(如列表等),使函数中的结果返回到参数中

def ChangeValue1(x):    #此处x应该为整数
    x = x ** 2


def ChangeValue2(x):     #此处参数x应该为列表
    x[0] = x[0] ** 2


a = 2                    #a为整数,其值为2
b = [2]                  #b为列表,其第1个成员为2

ChangeValue1(a)          #使用函数改变a的值,但不成功
a
2

ChangeValue2(b)          #使用函数改变b成员的值
b
[4]                      #值被成功改变
4.3 变量的作用域

在Python脚本中,不同的函数可以具有相同的参数名。在函数中已经声明的变量名,在函数外还可以继续使用。而在脚本运行的过程中,其值并不相互影响。变量的作用域决定了变量在程序中的可见性和可访问性。换句话说,它决定了变量在哪些部分可以被引用和使用。变量的作用域可以分为四个级别:局部作用域、嵌套局部作用域、全局作用域和内置作用域。

1.局部作用域

局部作用域也称为函数作用域,是变量在函数内部定义时的范围。在函数内部定义的变量只在该函数内部可见,当函数执行完毕后,这些变量就会被销毁。

def my_function():
    local_var = "我在局部作用域内"
    print(local_var)

my_function()  # 输出:我在局部作用域内
# 尝试在函数外部访问 local_var 会导致 NameError
# print(local_var)  # NameError: name 'local_var' is not defined

2.嵌套局部作用域

嵌套局部作用域指的是在函数内部定义的另一个函数中的变量作用域。内部函数可以访问外部函数的局部变量,但外部函数无法直接访问内部函数的局部变量。

def outer_function():
    outer_var = "我在外部函数作用域内"
    
    def inner_function():
        inner_var = "我在嵌套局部作用域内"
        print(outer_var)  # 可以访问外部函数的变量
        print(inner_var)
    
    inner_function()

outer_function()  # 输出:我在外部函数作用域内,我在嵌套局部作用域内

3.全域作用域

全局作用域是指在整个程序中都可见的变量作用域。在函数外部定义的变量属于全局作用域,它们可以在程序的任何地方被访问和修改(除非在函数内部被重新定义为局部变量)。

global_var = "我在全局作用域内"

def another_function():
    print(global_var)  # 可以访问全局变量

another_function()  # 输出:我在全局作用域内

4.内置作用域

内置作用域是Python解释器自带的变量作用域,它包含了所有内置函数和异常的名字。这些名字在程序任何地方都是可见的,但通常我们不会修改它们。

例如,len()print()等内置函数就位于内置作用域中。

作用域的查找顺序

了解这四种作用域后,我们需要知道Python是如何在多个作用域中查找变量的。当我们在程序中引用一个变量时,Python会按照以下顺序进行查找:

    局部作用域:首先在当前函数或方法内部查找变量。
    嵌套局部作用域:如果当前函数内部还有嵌套函数,则继续在这些嵌套函数内部查找。
    全局作用域:如果局部变量中未找到,则到全局变量中查找。
    内置作用域:最后,如果全局变量中也未找到,则到内置作用域中查找。

这个查找顺序也被称为LEGB规则,即Local(局部)、Enclosing(嵌套局部)、Global(全局)、Built-in(内置)。

下面是一个综合了以上作用域的示例代码:

# 全局作用域
global_scope_var = "全局变量"

def outer_function():
    # 嵌套局部作用域(属于outer_function)
    nested_scope_var = "嵌套局部变量"
    
    def inner_function():
        # 局部作用域(属于inner_function)
        local_scope_var = "局部变量"
        
        # 访问嵌套局部作用域变量
        print(nested_scope_var)
        
        # 访问全局作用域变量
        print(global_scope_var)
        
        # 尝试访问内置作用域变量(例如len)
        print(len([1, 2, 3]))
        
        # 尝试访问不存在的变量会导致NameError
        # print(non_existent_var)  # NameError: name 'non_existent_var' is not defined
    
    inner_function()

outer_function()

4.4 最简单的函数:使lambda表达式定义函数

lambda 在Python编程中使用的频率非常高,我们通常提及的lambda表达式其实是python中的一类特殊的定义函数的形式,使用它可以定义一个匿名函数。即当你需要一个函数,但又不想费神去命名一个函数,这时候,就可以使用 lambda了。

g = lambda x: x+1  # 求 x+1 的和


#结果
>>> g(1)
2
>>> g(2)
3

可以这样认为,lambda作为一个表达式,定义了一个匿名函数,上例的代码x为入口参数,x+1为函数体,用函数来表示为:

def g(x):
    return x+1

lambda函数的语法只包含一个语句,如下:

lambda [arg1 [,arg2,.....argn]]:expression

其中,lambda是Python预留的关键字,arg和expression由用户自定义。

代码示例

# 普通python函数
def func(a,b,c):
    return a+b+c
 
print (func(1,2,3))
# 结果为 6
 
# lambda匿名函数
f = lambda a,b,c: a+b+c
 
print (f(1,2,3))
# 结果为 6
 
# 在代码:f = lambda a,b,c: a+b+c 中,lambda表示匿名函数,
# 冒号 “:”之前的a,b,c表示它们是这个函数的参数。
# 匿名函数不需要return来返回值,表达式本身结果就是返回值。

lambda 函数的示例

1. 无参匿名函数:

>>> t = lambda : True  # 分号前无任何参数
>>> t()
True

等价于下面的def定义的函数:

>>> def func(): 
...     return True
...
>>> func()
True

2. 带参数匿名函数

>>> lambda x: x**3  # 带有一个参数
 
>>> lambda x, y, z: x+y+z # 带有多个参数
 
>>> lambda x, y=3: x*y  # 存在默认值的参数

3. 输入任意个数的参数

>>> lambda *z: z  # *z传入的是任意个数的参数

4. 输入带键值对的参数

>>> lambda **arg: arg  # **arg传入的是带键值对的参数
4.5 可重用结构:Python模块

Python中的模块实际上就是包含函数或者类的Python脚本。对于一个大型的脚本而言,经常需要将其功能细化,将实现不同功能的代码放在不同的脚本中实现,在其他脚本中以模块的形式使用细化的功能,以便于脚本的维护和重用。

4.5.1 Python模块的基本用法

模块是包含函数和其它语句的Python脚本文件,可以通过导入模块来使用模块中提供的函数或者数据。

以下三种方法导入模块或者模块中的函数:

import 模块名

import 模块名 as 新名字

from 模块名 import 函数名

使用import是将整个模块导入,而使用from则是将模块中的某个函数或者某个名字导入,而不是导入整个模块。使用import和from导入模块还有一点不同:要想使用import导入模块中的函数,则必须以模块名+“.”+函数名的形式调用函数:而要想使用from导入模块中的某个函数,则可以直接使用函数名调用,而不需要在前面加上模块名。

4.5.2 Python在哪里查找模块

编写好的模块只有被python找到才能导入。编写的模块和调用模块的脚本位于同一目录中,因此不用设置就能被python找到并导入

4.5.3 是否需要编译模块

虽然Python是脚本语言,但它可以将脚本编译为字节码的形式,对于模块,python总是在第一次调用后就将其编译成字节码的形式,以提高脚本的启动速度。由于Python是脚本语言,如果不想发布源文件,则可以发布编译后的脚本,这样能起到一定的保护源文件的作用。

4.5.4 模块可独立运行

每个Python脚本在运行时都有一个_name_属性,在脚本中通过对_name_属性值的判断,可以让脚本在作为导入模块和独立运行时都可以正确运行。

在Python中,如果脚本作为模块被导入,则其_name_属性被设置为模块名;如果脚本独立运行,则其_name_属性被设置为_main_。因此,可以通过_name_,属性来判断脚本的运行状态。

4.5.5 如何查看模块提供的函数名

如果需要获得导入模块中所有声明的名字,函数等,则可以使用内置的函数dir()来操作。以下代码将获得sys模块中的名字和函数。

4.6 用包来管理多个模块

使用包的好处可以有效地避免名字冲突,便于包的维护和管理。python中的模块包可以通过路径导入模块。

4.6.1 包的组成

如果需要导入B目录中的a.py模块,则可以使用以下语句之一:

from A.B import a            #使用from导入模块

import A.B.a                    #使用import导入模块

只要将所有的模块放在当前目录中的某个文件夹内,然后再该文件夹中新建一个空的_init_.py文件,就可以通过目录结构的层次导入所需的模块。

4.6.2 包的内部引用

python包中的模块也可能需要相互引用。对于图4-1所示的位于C目录中的b.py,如果要引用同样 位于C目录中的a.py,则可以使用以下语句:

import a

如果位于C目录中的b.py要引用位于B目录中的a.py,则需要使用以下语句:

from A.B import a

引用说明

《Python数据分析从入门到精通》 张啸宇 李静 编著

【Python】新手入门:变量的作用域是什么?有哪几种作用域?作用域的查找顺序是什么?_变量查找顺序-CSDN博客
python中lambda的用法_lambada在python-CSDN博客
 

往期文章

1. Python3的33个保留字详解-CSDN博客

2. Python3 学习起步必备知识点-CSDN博客

3. Python3 数据类型与流程控制语句-CSDN博客

  • 23
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智驾小兵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值