第一章 函数

一、什么是函数

函数是组织好的,可重复使用的,用来实现单一,或者相关联功能的代码段。在高级语言程序中,函数能提高应用的模块性和代码的重复利用率。

如果在开发程序时,需要多次使用某块代码,此时为了提高编写的效率和代码的重用,把实现某一特定功能的相关语句按某种格式组织在一起形成一个程序单位,并给程序单位取一个相应的名称,这样的一个程序单位就叫函数(Function)。给程序单位所起的名称称为函数名。

Python 语言的函数分为:用户自定义函数、系统内置函数和Python标准库(模块中定义的)中的函数。

系统内置函数:是用户可直接使用的函数。比如基本输入输出语句中所提到的input()和print()。

Python标准库中的函数:要导入相应的标准库,才能使用其中的函数。比如requests、DrissionPage

用户自定义函数是用户自己定义的函数:只有定义了这个函数,用户才能调用。

一个函数被使用就是指这个函数被调用。函数调用通过调用语句实现,调用语句所在的程序或函数称为调用程序或调用函数;被调用的函数简称为被调函数。调用语句需要指定被调用函数的名字和调用该函数所需要的信息(参数)。

使用函数有两个目的:

分解问题,降低编程难度;

代码重用。

函数必须先定义再调用

1、定义函数的语法:

(1)函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。

如:def add()

函数的命名规则和对象变量相同,一般用小写字母和单下划线、数字等组合,不能以数字开头,不得与关键字同名,名字最好能体现函数的功能。

避免与内置函数重名(内置函数不需要定义即可直接使用)

(2)任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数

根据函数功能,括号里可以有多个参数,也可以不带参数,命名规则与函数名相同

(3)函数的第一行语句可以选择性地使用文档字符串,用于存放函数说明。

文档字符串是一种良好的编程习惯,它可以帮助其他开发者(或未来的你)更容易地理解和使用这个函数。文档字符串通常包括函数的功能描述、参数说明、返回值说明等信息

(4)函数内容以冒号 “: ”起始,并且缩进。

(5)return [表达式] 结束函数,选择性地返回一个值给调用方,不带表达式的 return 相当于返回 None

2、函数调用

定义一个函数只给了函数一个名称,指定了函数里包含的参数,和代码块结构。

这个函数的基本结构完成以后,你可以通过另一个函数调用执行,也可以直接从Python提示符执行。

def 函数名 (参数表):

函数体

return 表达式

函数定义后,本身不会加载执行,必须要有相关的调用。

通过函数名([参数列表])调用函数

如下实例调用了printme()函数:

# 定义函数
def printme( str ):
   "打印任何传入的字符串"
   print str
   return
 
# 调用函数
printme("我要调用用户自定义函数!")
printme("再次调用同一函数")

二、函数参数

函数可以不使用参数,但不使用参数的函数其使用范围就会受到限制,局限性很大。为了让函数能具有比较高的通用性,需要为函数添加参数。

以下是调用函数时可使用的正式参数类型:必备参数、命名参数、默认参数、不定长参数。

1.必备参数

必备参数须以正确的顺序传入函数。调用时的参数个数必须和声明时的一样。如果调用时没有必备参数,则程序会提示错误。

def print_ info(str):"打印任何传入的字符串”
    print(str)
return;
#调用 printme函数
print info();

2. 命名参数

命名参数和函数调用关系紧密,调用方用参数的命名确定传入的参数值。Python会跳过不传的参数或者乱序传参,因为Python解释器能够用参数名匹配参数值。例如,用命名参数调用printme()函数,

def printme( str ):#"打印任何传入的字符串"
    print(str)
    return;
#调用 printme函数
printme( str = "My string");

命名参数传值顺序

def printinfo( name, age ):
    #"打印任何传入的字符串"
    print("Name: ", name)
    print("Age ", age)
    return;
#调用 printinfo函数
printinfo( age=50, name="miki" )

3.默认参数

定义函数时,可以给函数的参数设置为默认值。也就是说,Python 中,对于形参,还可以使用默认值。

如果函数定义中存在带有默认值的参数,该参数及其所有后续参数都是可选的。如果没有给函数定义中的所有可选参数赋值,就会引发SyntaxError异常。调用函数时,默认参数的值如果没有传入,则被认为是默认值。

def printinfo( name, age=15 ):
    #"打印任何传入的字符串"
    print("Name: ", name)
    print("Age ", age)
    return;
#调用 printinfo函数
printinfo( age=50, name="miki" )
printinfo(name='miki')

三、Python全局变量与局部变量

1.变量作用域

变量的使用范围就是变量作用域。

一个程序的所有的变量并不是在哪个位置都可以访问的。

a=1
def second():
    b=1+a
    def thirth():
        c=2+a
        d=3+b
        print(a,b,c,d)
    thirth()
    print(a,b)
second()
print(a) 

1 2 3 5
1 2
1

变量的作用域决定了在哪一部分程序访问哪个特定的变量名称。两种最基本的变量作用

域为:全局变量和局部变量。

四、Python内置函数


自定义函数。除了内置函数,还可以自定义函数,格式如下,注意四个而空格缩进。

函数返回值 return

函数运行完成后,没有写返回值,默认返回值为 None。

print、append、len、sum等函数的返回值均为None

自定义函数返回值示例

def calculate_sector(central_angle, radius):#接下来是一些定义函数的代码
    sector_area =central_angle /360 *3.14 * radius ** 2
    print(f"此扇形面积为:{sector_area}")
    return sector_area

sector_area1=calculate_sector(160,30)
sector_area2=calculate_sector(60,15)
sector_area3=calculate_sector(30,16)


此扇形面积为:1256.0
此扇形面积为:117.75
此扇形面积为:66.98666666666666

第二章、模块

Python自带了功能丰富的标准库,另外还有数量庞大的各种第三方库。

使用这些功能的基本方法就是使用模块。

通过函数,可以在程序中重用自己的代码;

通过模块,则可以重用别的程序中的代码。

模块是一个保存了Python代码的文件。模块能定义函数、类和变量。模块中也能包含可执行的代码。模块分为三种:自定义模块、内置标准模块、开源模块(第三方)。

在Python解释器上不能直接使用函数库中的函数,因为这些函数都封装在某一函数库中,要使用库中的某一函数,先要导入相应的函数库,或者称导入模块。

Python含有很多的函数模块,不同模块内存不同功能的函数,可以在程序里面引入使用。

1、import 语句

import 加上模块名 **** 引入模块

需要使用模块的函数或变量的时候格式如下:

模块名.函数名

模块名.变量名

statistics 是 Python 的标准库模块,提供多种统计计算功能,包括均值、中位数、众数、方差等常见统计指标的计算。

import statistics

print(statistics.median([19, -5, 36]))   #median(data) 获取中位数

print(statistics.mean([19, -5, 36]))  #mean(data) 计算算术平均值

19
16.666666666666668

导入数学模块

import math
print_info(math.sqrt(2))

2、from…import… 语句

from 模块名 import 函数名,变量名 **** 引入模块的某些函数或变量

from后面添加模块名,import后面添加函数名或变量名,需要引入多个的时候用逗号进行分隔。

此方法在使用时不需要再带上模块名。

from statistics import median, mean

print(median([19, -5, 36]))

print(mean([19, -5, 36]))

3、from…import * 语句]()

from **模块名 import *** **** 引入模块的所有内容

from后面添加模块名,import后面加*号,将模块里面的所有内容进行引入,在使用时全都不需要在前面添加模块名。

优点:需要使用同一个模块的不同函数或变量可以直接使用,不需要再加模块名。

缺点:如果因引用多个模块,不同模块内可能包含相同名称的函数,运行时可能出现错误。

此方法不建议使用。

from statistics import *

print(median([19, -5, 36]))

print(mean([19, -5, 36]))

4、其他

在PyCharm软件内查看Python模块函数代码方法,按住ctrl,再点击代码中的函数,即会显示源代码。

除了引入官方的模块以外,还可以引入第三方模块,引入方法与引用官方模块一样,前提是引入之前需要提前安装好第三方的模块。

第三方库搜索网站:PyPI:The Python Package Index

如果安装第三方库,在PyCharm的【终端】窗口输入 pip install 后面加上库的名字 。安装完成后即可使用库的函数。

5、常用模块介绍

time

既有与时间处理相关的,也有用于转换时间格式的

import time
print(time.time())

输出当前时间戳

使用localtime可以获取当前时间,获取的时间使用时间元组方式展示。从返回浮点数的时间戳方式向时间元组转换,只要将浮点数传递给如localtime之类的函数。

import time
print(time.time())
print(time.localtime(time.time()))

时间元组

获取格式化的时间

import time
localtime = time.asctime( time.localtime(time.time()))
print("当前时间是:",localtime)

获取某月日历

import calendar
cal=calendar.month(2025,11)
print(cal)

第三章 进阶函数

程序编写有两种方式:一种是面向过程的语言,典型的如C语言;

一种是面向对象的语言,典型的如Java、C++、C#等语言。

面向对象:将数据与函数绑定到一起,进行封装,这样能够更快速地开发程序,减少重复代码的重写过程。

面向过程编程最易被初学者接受,其往往用一长段代码来实现指定功能,开发过程的思路是将数据与函数按照执行的逻辑顺序组织在一起,数据与函数分开考虑。

一、类

1、类的定义 class

2、类的命名

Python常用类的命名方法为 Pascal命名法 ,即 首字母大写

class Stu:
    name ="张三"
print(Stu.name)

3、实例对象

程序想要完成具体的功能,仅有类是不够的,需要根据类创建实例对象,通过实例对象完成具体的功能。

实例对象,就是为类创建一个具体的实例化的对象,以使用类的相关属性和方法。

Python 中,创建类的实例化对象不需使用new,可以直接赋值,语法如下:

对象名=类名()

class Stu:
    #定义一个属性
    name ="张三"
    age=19
#创建 Stu 类的对象
stu =Stu()
print("学生姓名:%s,年龄:%d"%(stu.name,stu.age))

4、类的构造函数

类的构造函数,主要作用是定义实例对象的属性

必须命名为 init 前后必须有两个下划线。括号内可以放任意数量的参数,但是第一个永远是被占用的,用于表示自身,约定俗称为 self ,它能把属性绑定在实列的对象上。

class CuteCat:
    def __init__(self):
        self.name="Lambton"
cat1 = CuteCat()
print(cat1.name)

Lambton

从参数中获取值,在创建对象时,在括号内写入要传入的参数, init 函数将会绑定传入的参数。

class CuteCat:
    def __init__ (self,cat_name,cat_age,cat_color):
        self.name = cat_name
        self.age = cat_age
        self.color = cat_color

cat1 = CuteCat("allen",2,"黄色")
print(f"小猫{cat1.name}的年龄是{cat1.age}岁,花色是{cat1.color}")

小猫allen的年龄是2岁,花色是黄色

5、类的方法

定义类的方法与创建普通函数差不多。

有两点区别:

(1)需要创建在class里面,前面需要有缩进,来表示属于该类的方法。

(2)和 init 一样,第一个参数被占用,用于表示对象自身,约定俗成为 self 。

在类的内部,使用def关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数self,且为第一个参数。

构造方法

构造方法init_()是一种特殊的方法,被称为类的构造函数或初始化方法,用来进行一些初始化的操作,在对象创建时就设置好属性。如果用户没有重新定义构造函数,则系统自动执行默认的构造方法。这个方法不需要显式调用,当创建了这个类的实例时就会调用该方法。在构造方法_init_()中,init前后用两个下画线开头和结尾,是Python内置的方法,用于在对象实例化时对实例进行的初始化工作。

class CuteCat:
    def __init__(self,cat_name,cat_age, cat_color):
        self.name = cat_name
        self.age = cat_age
        self.color =cat_color
    def speak(self):
        print("喵"*self.age)
    def think(self, content):
        print(f"小猫{self.name}在思考{content}")
cat1 = CuteCat("Jojo",1,"橙色")
cat1.think("现在去抓沙发还是去撕纸箱")

小猫Jojo在思考现在去抓沙发还是去撕纸箱 ....

6、类的继承

面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制实现的。

继承完全可以理解成类之间的类型和子类型关系。

类的继承是指在一个现有类的基础上,构建一个新的类。

构建的新类能自动拥有原有类的属性和方法。

构建出来的新类叫子类,原有类称为父类。也可以理解成类之间的类型和子类型关系。

#定义父类
class Parent:
    parentAttr=100
    def __init__(self):
        print("调用父类构造函数")
    def parentMethod(self):
        print("调用父类方法")
    def setAttr(self,attr):
        Parent.parentAttr=attr

    def getAttr(self):
        print("父类属性:", Parent.parentAttr)

class Child(Parent):  # 定义子类
    def __init__(self): print("调用子类构造方法")

    def childMethod(self):
        print("调用子类方法child method")
# 实例化子类
c = Child()
# 调用子类的方法
c.childMethod()
# 调用父类方法
c.parentMethod()
# 再次调用父类的方法
c.setAttr(200)
# 再次调用父类的方法
c.getAttr()

子类可以继承父类的属性和方法。

在子类名称后的括号中写上父类的名称,此时父类的方法就会继承到子类中。

调用函数和方法时,优先看所属的类是否有没有该方法函数,如果过有,则执行该类的方法,如果没有,会往上找父类的同名方法使用。

如果子类程序中有 init 方法,则创建子类实例时优先调用子类的构造函数,导致实例只有子类的 init 中的属性参数,而父类的 init 属性参数不会被调用。

此时在子类中用 supper() 方法,返回当前类的父类;

在子类的构造函数中写 supper().init() ,则会调用父类的构造函数,子类将会继承父类的构造函数中的属性。

什么时候可以用继承?

如果说A是B,那么就可以把A写成是B的子类。

二、Python文件路径

不同操作系统文件存储路径,Linux、macOS等操作系统所有文件存储在根目录下,根目录用 斜杠(/) 表示。

Windows操作系统每个磁盘分区都有根目录,用 分区名 加 反斜杠() 表示。

定义文件位置的方法有两种, 绝对路径 和 相对路径 。

1、绝对路径

从根目录出发的路径。

对于Linux、macOS等系统,绝对路径以斜杠(/)开头,路径中每个目录之间用斜杠进行分隔,最后以目标文件或目标目录结尾。

对于Windows系统,绝对路径以分区名加反斜杠(\)开头,路径中每个目录之间用反斜杠进行分隔,最后以目标文件或目标目录结尾。

2、相对路径

从一个参考位置出发。表示从指定文件位置出发,其他文件处于什么路径。

用相对路径时,用点(.)表示参照物所在的目录,用点点(…)表示更上一层的父目录。

如果继续往上走,就用点点(…)跟上斜杠或者反斜杠进行分隔,后面跟上点点(…)表示当前文件所属目录的父目录的父目录。

如果是往更下层走,同样以斜杠或反斜杠来分隔路径中的目录。

相对路径都是以点(.)或点点(…)开头。

./(.) 是可以省略的,在同一路径下相互用相对路径找到彼此,可以直接使用文件名。

三、Python文件操作

1、 open() 方法,打开文件

括号内放文件路径,可以是相对路径也可以是绝对路径。

示例:open(“./data.txt”) 相对路径

open(“/usr/demo/data.txt”) 绝对路径

open() 方法的 第二个参数表示模式,模式是一个字符串,常见的字符串有 “r” ,表示读取模式; “w” 表示写入模式。

示例:open(“/usr/demo/data.txt”,”r”)

注:第二个参数可以不写,不写时默认为读取模式 ”r”

读取模式下,如果找不到文件,就会报FileNotFoundError的错误,表示文件不存在。

open() 方法还有一个可选参数 encoding 表示编码方式。一般文件编码方式为 UTF-8 。

示例: f = open(“./data.txt”, ”r”, encoding=”utf-8”)

如果open()方法执行成功,将会返回一个文件对象,后续可以对这个对象进行读取或写入操作。

2、 read() 方法,读取文件方法一

运行read()方法会一次性读取文件里面的所有内容,并以字符串的形式返回。一般操作的文件以文本为主。其他类似word文件内有字体颜色、字号区分的文件,很可能读取有误。

f = open("./data.txt",encoding="utf-8")
print(f.read())

运行read()方法后, 再次调用时返回的结果为空 ,因为程序会记录文件读取到哪个位置,第一次运行read()时已经读到结尾,第二次运行时后面已经没有内容。

f = open("./data.txt",encoding="utf-8")
print(f.read())
print(f.read())

** 文件太大时不建议使用read()方法,因为读出来的内容会占用很大的内存。如果不需要一次性读取整个文件,可以 在read()传一个数字 ,表示读多少个字节,下次调用read时会从上次结束位置继续往下读。

3、 readline() 方法,读取文件方法二

只读取文件一行内容,下次调用时继续读取下一行,根据换行符来判断什么时候结束本行。并且换行符会被当成读到的内容的一部分。读取到结尾后,继续读取将会返回空字符串,表示后面已经无内容。

一般采用while循环判断,只要返回的不是空字符串就继续读取下一行,否则退出循环。

4、 readlines() 方法,读取文件方法三

读取文件内容,并将每行作为 列表元素 返回。

一般和for循环结合使用。先调用readlines方法把每行读取出来,再逐行循环列表所有内容。

5、 close() 方法,关闭文件

调用close方法后将会释放系统资源,每次调用文件完成后都应该关闭文件。

with open(“./data.txt”)asf:

print(f.read()) #对文件的操作

如上方式可以不用调用close方法关闭文件,文件操作完成后会自动关闭文件。注意对文件操作的代码需要缩进,缩进的代码块执行完毕后会自动关闭文件。

6、 write() 方法,写文件

写文件与读文件类似,都需要先打开文件,并且在操作完成后关闭文件。同样可以使用 with open() as f: 方式。

with open(“./data.txt”,”w”, encoding=”utf-8”) as f:

open的第二个参数传入 w ,即为写文件模式,写文件模式找不到文件,不会像读文件模式那样报错,而是自动创建传入文件名的那个文件。第三个参数为encoding编码格式。

注:使用 w 模式打开文件进行写入时,如果文件已存在,将会把原有文件内容全部清空。

如果不想重写文件内容,而是增加文件内容,则open()的第二个参数需要传入附加模式”a”参数。如果文件名不存在,会直接创建文件。

无论是”w”模式,还是”a”模式,都不能读取文件原本的内容,如果在这两个模式下调用read()方法,程序会报错不支持读操作(UnsupporttedOperation)。

7、 r+,同时支持读写文件

open()方法的第二个参数传入”r+”参数,就可以同时支持读写文件。

os模块

os 模块在使用之前需要导入,导入后建议使用dir 命令查看 os 里面的子模块和方法

os模块的方法区分文件系统,在Windows和Linux下方法功能相同,都用于提供系统级别的操作。

import os
#获取当前文件夹的绝对路径
file_DIR =os.path.dirname(__file__)
print(file_DIR)
#获取当前文件夹内的Test.txt文件
file_path =os.path.join(file_DIR,'Test.txt')
#读取文件
Test_Data=open(file_path,'r')
for line in Test_Data:
    Test_Data= open(file_path, "r")
    print(line)
# 关闭文件
Test_Data.close()

四、Python异常处理

程序报错类型有很多。在Python中,异常处理是一种非常重要的机制,它允许程序在遇到错误或问题时,能够优雅地处理这些情况,而不是直接崩溃。可以通过 try/except 语句捕捉异常。

在Python中,异常是继承自BaseException类的,而常见的异常类型有:

  • SyntaxError:解析错误
  • NameError:未找到变量
  • TypeError:类型错误
  • IndexError:索引错误
  • KeyError:字典中找不到键
  • ValueError:值错误
  • ZeroDivisionError:除以零
  • IOError(在Python 3中已更名为OSError
  • FileNotFoundError:文件未找到
  • PermissionError:权限错误

try 后加冒号,换行后在缩进的代码块里放入可能产生错误的代码,接下来在except后面跟上需要捕捉的错误名字以及冒号。

try:
    # 尝试执行的代码块
    risky_code()  # 这里放置可能引发异常的代码
except SomeException as e:
    # 当SomeException类型的异常被抛出时执行的代码块
    print(f"处理异常: {e}")
else:
    # 如果try块中没有发生异常,则执行else块
    print("没有发生异常")
finally:
    # 无论是否发生异常,finally块中的代码都会被执行
    print("清理工作")

以上程序,用户输入的内容可能不是浮点数,将会报错ValueError错误,就可以在except后面放入ValueError去捕捉。再在下一行放入出现错误代码后需要执行的代码。

可以分别捕捉多个错误代码,如果不知道可能出现什么错误,可以写except:即为除了以上列出的错误代码以外的任何错误都执行此代码。

需要注意的是,try/except语句在捕捉错误时,依次从上往下运行,如果第一个except已经捕捉到错误,之后的except将不会执行,与if/elif语句类似,只有第一个符合条件的分支会运行。

else: 表示没有出现任何错误时执行的语句。

finally: 无论是否发生错误,都会执行的语句。

五、Python测试

1、 assert 断言

assert condition, message

condition:一个布尔表达式。如果该表达式为 True,程序继续执行;如果为 False,程序抛出 AssertionError 异常。

message:(可选)在抛出 AssertionError 时输出的错误信息,用于描述断言失败的原因。

x = int(input("请输入x值:"))
assert x > 10, "x should be greater than 0"
print(x)


请输入x值:-2
Traceback (most recent call last):
  File "E:/test.py", line 14, in <module>
    assert x > 10, "x should be greater than 0"
AssertionError: x should be greater than 0

一般会使用专门做测试的库,能一次性跑多个测试用例,并且能直观展示哪些测试通过。

def test_add():
    assert add(2, 3) == 5, "2 + 3 应该等于 5"
    assert add(-1, 1) == 0, "-1 + 1 应该等于 0"
    assert add(-1, -1) == -2, "-1 + -1 应该等于 -2"

每个assert语句都检查add函数的输出是否符合预期。如果任何一个assert失败(即表达式的值为假),则会抛出AssertionError,并且可以附带一个错误消息,帮助诊断问题。

2、 unittest 单元测试

unittest对软件中最小可测试单元进行验证。比如验证某函数的某个方面表现是否符合预期。unittest库是Python自带的,不需要额外安装。

因unittest是一个库,需要用import语句引用到测试程序内。

一般把测试代码放到独立文件内。而不是和要测试的功能放到一起。这样更清晰地划分实现代码和测试代码。测试代码中还需要将需要测试的函数或类引入进来,如果测试文件和被测试文件位于同一文件夹下,引入语法是:

from 文件名 import 函数名

from 文件名 import 类名

3、 测试代码

引入测试模块 unittest单元测试模块,并且引入需要测试的函数。创建一个测试的类,作为unittest.TestCase的子类,继承unittest.TestCase的各种测试功能。在此类下面定义各种需要测试的用例,每个测试用例都是类的一个方法。

测试方法命名必须以 test_ 开头。因为 unittest 库只把 test_ 开头的方法当作测试用例,

import unittest

def add(a, b):
    return a + b

class TestAddFunction(unittest.TestCase):
    def test_add_positive_numbers(self):
        self.assertEqual(add(2, 3), 5, "2 + 3 should equal 5")
    
    def test_add_negative_numbers(self):
        self.assertEqual(add(-1, 1), 0, "-1 + 1 should equal 0")
    
    def test_add_negative_and_positive(self):
        self.assertEqual(add(-1, -1), -2, "-1 + -1 should equal -2")

if __name__ == '__main__':
    unittest.main()

运行结束后,会提示一共运行了几个测试用例,并且展示测试结果, 一个 · 表示一个测试一个测试通过, F 表示一个测试不通过。

如果有测试不通过,unittest会详细输出哪个文件下面的哪个方法造成了测试失败,以及为什么失败。

六、Python高阶和匿名函数

1、 高阶函数

一个函数里面有多个运行逻辑,每次增加逻辑时需要在这个函数继续增加程序,此写法不优雅直观。

def calculate_and_print(num,power):
    if power == 2:
        result =num * num
    elif power == 3:
        result =num*num*num
    else:
        print("只支持计算二次方和三次方")
        return
    print(f"""
    数字参数 |{num}|
    计算结果| {result} |
    """)
calculate_and_print(3,3)

为了优雅直观的表示函数,可以将函数的各种计算方式独立成单独的函数。运行计算方式时直接调用传入的函数将计算结果显示出来。

这种将函数作为参数的函数()叫做 高阶函数

def calculate_square(num):
    return num*num

def calculate_cube(num):
    return num*num*num
def calculate_plus_10(num):
    return num+10
def calculate_and_print(num,calculator):
    result=calculator(num)
    print(f"""
    #     数字参数 |{num}|
    #     计算结果| {result} |
    #     """)

calculate_and_print(3,calculate_square)
calculate_and_print(10,calculate_plus_10)


    #     数字参数 |3|
    #     计算结果| 9 |
    #     

    #     数字参数 |10|
    #     计算结果| 20 |
    #    

作为参数的函数是直接用函数名进行传入,表示函数其本身,后面不能带括号和参数,如果带括号,即表示调用此函数,从而传入的是函数的执行结果。

高阶函数还可以同时调用多个函数。

def calculate_square(num):
    return num*num

def calculate_cube(num):
    return num*num*num
def calculate_plus_10(num):
    return num+10
def calculate_and_print(num,calculator,formatter):
    result=calculator(num)
    formatter(num,result)

def print_with_vertical_bar(num,result):
    print(f"""
    #     数字参数 |{num}|
    #     计算结果| {result} |
    #     """)

calculate_and_print(3,calculate_square,print_with_vertical_bar)



    #     数字参数 |3|
    #     计算结果| 9 |

2、 匿名函数

匿名函数不需要写名字,即用即仍,可以把匿名函数当作没有名字且占用行数更少的函数。以lambda作为关键字。

可以直接在高阶函数的括号内写上关键字 lambda ,再跟上 变量名,后面再跟上冒号 : ,冒号后面直接写上需要返回的结果,无需写return.

calculate_and_print(7,lambda num:num*5,print_with_vertical_bar)

    #     数字参数 |7|
    #     计算结果| 35 |

如果需要给匿名函数增加参数,直接用逗号分隔即可。

以上匿名函数写成普通函数,如下图所示:

除了作为高阶函数的参数,匿名函数还可以定义好后直接被调用。调用方式和普通函数一样,都是括号,然后括号里面传入参数,唯一的区别是前面的匿名函数也是要被括住,表示这是一个整体。

print((lambda num1,num2:num1+num2)(2,3))

5

匿名函数也有局限性,冒号后没法有多个语句或表达式,只适用于比较简单的场景,对于多步骤的复杂逻辑或者设计循环递归等,它可能不灵活,没法用它写出来;即使写出来,可读性会很差。


 

第四章 函数的应用

一、闭包

定义:在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数的变量的内部函数称为闭包。

1、闭包的作用

(1)在全局作用域对局部变量的间接访问

(2)可以形成装饰器

2.闭包的构成条件

闭包的构成条件三步走:

(1)有嵌套 (函数里面再定义函数)

(2)有引用(内部函数使用了外部函数的变量)

(3)有返回(外部函数返回了内部函数)

嵌套函数中,内层函数引用外层函数的变量,且外层函数返回内层函数:

def make_multiplier(factor):
    def multiplier(number):
        return number * factor
    return multiplier
 
double = make_multiplier(2)
print(double(5))  # 输出10

二、装饰器

1、什么是装饰器

在不改变现有函数源代码以及函数调⽤⽅式的前提下,实现给函数增加额外的功能。

2、装饰器的作用:

(1)扩展功能:在不修改原函数代码的情况下添加新功能

(2)代码复用:将多个函数共用的功能封装成装饰器

(3)保持简洁:避免在每个函数中重复相同的代码

(4)分离关注点:将核心逻辑与附加功能分离

3、装饰器的应用场景

通用装饰器

# 定义一个简单的装饰器函数outer,它接收一个函数fun作为参数
def outer(fun):
    # 定义内部函数inner,它将替代原始函数
    def inner(*args, **kwargs):
        # 调用原始函数并保存结果
        res = fun(*args, **kwargs)
        # 返回原始函数的执行结果
        return res
    # 返回inner函数对象(注意不是调用它)
    return inner
 
# 使用@outer装饰器语法糖装饰my_fun函数
# 等价于:my_fun = outer(my_fun)
@outer
def my_fun(*args, **kwargs):
    print('函数内容')
 
# 调用被装饰后的函数
my_fun()

使用装饰器传递参数

# 定义一个带参数的装饰器工厂函数
# times参数指定函数需要重复执行的次数
def repeat(times):
    # 实际的装饰器函数,接收被装饰的函数作为参数
    def decorator(func):
        # 包装函数,用来替代原始函数
        def wrapper(*args, **kwargs):
            # 循环执行被装饰的函数
            for _ in range(times):
                # 保存最后一次调用的结果
                result = func(*args, **kwargs)
            # 返回最后一次调用的结果
            return result
 
        # 返回包装函数
        return wrapper
 
    # 返回装饰器函数
    return decorator
 
# 使用装饰器,指定重复3次
@repeat(3)  # 等价于 greet = repeat(3)(greet)
def greet(name):
    print(f"Hello, {name}!")
 
# 调用被装饰后的函数
greet("Alice")

更多推荐