Python学习笔记(二):函数

python英文官方文档:https://docs.python.org/3.8/tutorial/index.html
比较不错的python中文文档:https://www.runoob.com/python3/python3-tutorial.html

1. 写在前面

这几周从实践角度又学习了一遍python,温故而知新,还是有蛮多心得的, 周末再看之前记的python笔记,总觉得零零散散, 未成体系,所以后面这段时间,陆续对之前的python笔记做一次整合, 使得内容更加清晰,成体系,做到简单可依赖,既是复习,也方便以后回看回练。希望能帮助到更多的伙伴啦。

这是第二篇文章,主要整理python函数相关的内容,函数初识到高级特性再到函数式编程,层层递进。既有基础,又有新知识,这样更有意思一些。

文章很长,内容很多,各取所需即可 😉

大纲如下:

  • 1. 写在前面
  • 2. 函数基础
    • 2.1 函数初识
    • 2.2 global和nonlocal
    • 2.2 参数总结
      • 2.2.1 默认参数
      • 2.2.2 可变参数
      • 2.2.3 关键字参数
      • 2.2.4 命名关键字参数
      • 2.2.5 使用总结
      • 2.2.6 逆向参数收集
    • 2.3 值传递和引用传递
    • 2.4 高级特性
      • 2.4.1 切片、迭代、列表生成
      • 2.4.2 生成器
      • 2.4.3 迭代器
    • 2.5 给函数写说明文档
  • 3. 函数式编程
    • 3.1 高阶函数
      • 3.1.1 map/reduce
      • 3.1.2 filter
      • 3.1.3 sorted
      • 3.1.4 使用小总
    • 3.2 函数作为返回值
      • 3.2.1 函数懒加载
      • 3.2.2 闭包
    • 3.3 匿名函数
    • 3.4 装饰器
    • 3.5 偏函数
  • 4. 常用内置函数
    • 4.1 数学运算
    • 4.2 逻辑运算
    • 4.3 进制转化
    • 4.4 类型相关
    • 4.5 类相关
    • 4.6 eval和exec
  • 5. 小总

Ok, let’s go!

2. 函数基础

2.1 函数初识

函数,完成特定功能的代码封装, 一次编写多次调用,避免反复写代码。这里先整理一些基础操作。

# 1. 函数起别名: 函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量
a = abs # 变量a指向abs函数
a(-1) # 所以也可以通过a调用abs函数
1
# 这个在用Pytorch搭建神经网络的时候经常遇到这种起别名的操作# 2. 空函数 Python中有个比较骚的操作就是可以定义一个函数,但是什么都不写
# # 那有什么用? 写程序的时候,我们往往喜欢先搭一个简单的框架出来, 把各种函数定义好放那,至于作用可能一时半会还没想好,这时候就可以放一个pass, 让代码运行起来再说
def nop():passif age >= 18:pass          # 如果这时候缺少pass,就会报语法错误# 3. Python貌似可以返回多个值呢? 但是真的是这样吗?
def move(x, y, step, angle=0):nx = x + step * math.cos(angle)ny = y - step * math.sin(angle)return nx, ny# 调用
a, b = move(x,y)
# 上面代码看起来,好像python的return可以同时返回多个值, 其实这是一种假象, python函数返回的仍然是单一值,只不过这个单一值是一个元组。 在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便

2.2 global和nonlocal

在Python中,globalnonlocal关键字用于在不同作用域内访问和修改变量。这两个关键字在处理嵌套函数或全局变量时特别有用

# 1. global关键字用于在局部作用域中声明全局变量。当你在一个函数内部想要修改外部定义的全局变量时,就需要使用global。
x = 5
def func():global x  # 声明x是全局变量x = 10  # 修改全局变量x的值
func()
print(x)  # 输出 10# 2. nonlocal关键字用于在闭包或嵌套函数中声明一个变量指向非全局作用域,例如指向外层(但非全局)作用域的变量。它使得我们能够修改位于嵌套作用域中的变量。
def outer():y = 5def inner():nonlocal y  # 声明y不是局部变量,而是外层函数的变量y = 10inner()print(y)  # 输出 10outer()# 注意:# 1. global关键字使得局部作用域可以修改全局作用域中的变量。# 2. nonlocal关键字确保变量指向最近的外层作用域(非全局作用域),并且可以在那个作用域中修改它。# 3. 不应当滥用global和nonlocal关键字,因为它们会使得代码变得难以理解和维护。在可能的情况下,最好使用函数参数和返回值来传递和接收数据,而非依赖外部状态。# 4. nonlocal不能用于访问全局作用域的变量,它只适用于嵌套的局部作用域中。

如下,函数 f 里嵌套一个函数auto_increase。实现功能:不大于 10 时自增,否则置零后,再从零自增。

# 函数 f 里嵌套一个函数auto_increase。实现功能:不大于 10 时自增,否则置零后,再从零自增。
def f():i = 0def auto_increase():nonlocal i    # 使用 nonlocal 告诉编译器,i 不是局部变量 , 如果没有这句话, 函数会报错, 说i没有在函数里面声明赋值if i >= 10:i = 0 i += 1ret = []for _ in range(28):auto_increase()ret.append(i)print(ret)f() # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8]

2.2 参数总结

2.2.1 默认参数

python函数定义的时候可以给出默认参数, 这样简化了函数调用的难度, 无论是简单调用还是复杂调用, 函数只需要定义一个即可。 举个例子体会一下:

# 一个一年级小学生注册的函数, 需要传入name和gender两个参数:
def enroll(name, gender):print(name)print(gender)# 这样调用的时候只需要传入两个参数
enroll('Sarah', 'F')              # 就会输出名字和性别

但是如果我想传入年龄, 城市等信息怎么办, 上面那个就不能用了, 所以看看有默认参数的函数:

def enroll(name, gender, age=6, city='Beijing'):print('name:', name)print('gender:', gender)print('age:', age)print('city:', city)

这样, 大多数学生注册的时候不需要提供年龄和城市,只提供必须的两个参数性别和姓名。 依然可以:

enroll('Sarah', 'F') # 但这个还可以更复杂的调用
enroll('Bob', 'M', 7)
enroll('Adam', 'M', city='Tianjin')

所以默认参数的使用还是非常方便的,在定义神经网络函数的时候,就通常会有默认的学习率, 迭代次数等参数。 但也有一个注意的点: 首先就是设置的时候, 必选参数要放在前面,默认参数要放后面, 否则解释器不知道你使用的是默认参数还是必选参数。 其次就是默认参数的顺序, 变化大的放前面, 变化小的放后面。 最后,有多个默认参数时, 调用的时候既可以按顺序提供默认参数,也可以不按顺序提供, 但此时要把默认参数的参数名加上。(上面那个例子)

关于默认参数的使用, 还会有坑:默认参数必须指向不变对象!, 这是啥意思, 下面演示一个例子:我定义了一个函数, 传入一个list

def add_end(L=[]):L.append('END')return L# 我正常调用
add_end([1, 2, 3])    # [1, 2, 3, 'END']
add_end(['x', 'y', 'z'])    # ['x', 'y', 'z', 'END']# 我使用默认参数调用, 加了三次end
a = add_end()
b = add_end()
c = add_end()# 你知道执行之后, a, b, c都是什么值了吗?
print(a)     # ['END', 'END', 'END'] 
print(b)     # ['END', 'END', 'END'] 
print(c)     # ['END', 'END', 'END'] 

为什么默认参数是[], 但是函数似乎每次都“记住了”上次添加了’END’后的list呢? 这是因为Python函数在定义的时候, 默认参数L的值就被计算出来了,即[], 因为默认参数L也是一个变量, 它指向对象[], 每次调用该函数, 如果改变了L的内容, 则下次调用的时候, 默认参数的内容就变了,不再是函数定义时的[]了。所以, 默认参数必须指向不变对象!。 要修改上面例子,我们可以使用不变对象None来实现:

def add_end(L=None):if L is None:L = []L.append('END')return La = add_end()
b = add_end()
c = add_end()
print(a)   # ['END']
print(b)   # ['END']
print(c)    # ['END']# 可以和上面进行一个对比

为什么要设计str、None这样的不变对象呢? 因为不变对象一旦创建, 对象内部的数据就不能修改, 这样就减少了由于修改数据导致的错误。 此外,由于对象不变, 多任务环境下同时读取对象不需要加锁,同时读问题不会产生。 我们编写程序时,如果可以设计一个不变对象, 那就尽量设计成不变的对象。 如果想查看一个函数使用了什么默认值,可以用fun_name.__defaults__查看。

2.2.2 可变参数

在Python函数中,还可以定义可变参数。顾名思义,可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。 这个之前确实不知道是啥意思,所以在这里整理一下, 以一个数学题为例子:

给定一组数字a, b, c…, 计算 a 2 + b 2 + c 2 . . . . . a^2+b^2+c^2..... a2+b2+c2.....

要定义出这个函数,我们必须确定输入的参数。由于参数个数不确定,我们首先想到可以把a,b,c……作为一个list或tuple传进来

def calc(numbers):sum = 0for n in numbers:sum += n * nreturn sum# 分分钟搞定, 然后我们调用, 得先组装成一个数组
calc([1, 2, 3])    # 14
calc((1, 3, 5, 7))   # 84

如果利用可变参数的话,我们就可以不用先组装成数组往里面传:

def calc(*numbers):sum = 0for n in numbers:sum = sum + n * nreturn sum# 这时候调用
calc(1, 2, 3)
calc(1, 3, 5, 7)
# 甚至不传都可以
calc()

定义可变参数和定义一个list或者tuple参数相比,仅仅在参数前面加了一个*(可千万别认为这成了指针了, python中就没有指针一说),在函数内部, 参数numbers接收的是一个tuple, 因此函数代码完全不变,调用该函数时,可以传入任意个参数, 包括0个参数。

如果此时有了一个list或者tuple, 要调用一个可变参数怎么办?

# 可以这样做
nums = [1, 2, 3]
calc(nums[0], nums[1], nums[2])# 但上面这样更加繁琐,所以可以直接在nums前加*, 把list或tuple元素变成可变参数传入:
calc(*nums)

这种写法也是非常常见, *nums表示把nums这个list的所有元素作为可变参数传进去。

2.2.3 关键字参数

可变参数允许传入0和或者任意个参数, 这些可变参数在调用时自动组装成一个tuple。 而关键字参数允许传入0个或者任意个含参数名的参数, 这些关键字参数在函数内部自动封装成一个dict, 就是带名字的参数,可以传入任意个, 看例子就明白了:

def person(name, age, **kw):print(name, age, kw)person('Michael', 30)    # Michael  30  {}
person('Bob', 35, city='Beijing')   # Bob  35  {'city':'Beijing'}
person('Adam', 45, gender='M', job='Engineer')   # Adam 45 {'gender':'M', 'job':'Engineer'}

关键字参数有什么用呢? 它可以扩展函数的功能。比如,在person函数里,我们保证能接收到name和age这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:

extra = {'city':'Beijing', 'job':'Engineer'}
person('Jack', 34, **extra)   # name: Jack age: 34 other: {'city': 'Beijing', 'job': 'Engineer'}

**extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra

2.2.4 命名关键字参数

对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数,如果要限制关键字参数的名字,就可以用命名关键字参数:

def person(name, age, *, city, job):print(name, age, city, job)

和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符**后面的参数被视为命名关键字参数。调用的时候,必须传入参数名, 否则,解释器会报错。

person('Jack', 24, city='Beijing', job='Engineer')
person('jack', 24, 'Beijing', 'Enginneer')   # 这种会报错, 解释器会看成四个位置参数, 但其实person函数有两个位置参数, 后面的两个叫做命名关键字参数

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:

def person(name, age, *args, city, job):print(name, age, args, city, job)

使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个*作为特殊分隔符。如果缺少*,Python解释器将无法识别位置参数和命名关键字参数。

在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数

def f1(a, b, c=0, *args, **kw):print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)def f2(a, b, c=0, *, d, **kw):print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)

虽然可以组合多达5种参数,但不要同时使用太多的组合,否则函数接口的可理解性很差。 多了,自己都不知道咋调用了! 会出现这样的一些报错:

  • SyntaxError: positional argument follows keyword argument,位置参数位于关键字参数后面
  • TypeError: f() missing 1 required keyword-only argument: 'b',必须传递的关键字参数缺失
  • SyntaxError: keyword argument repeated,关键字参数重复
  • TypeError: f() missing 1 required positional argument: 'b',必须传递的位置参数缺失
  • TypeError: f() got an unexpected keyword argument 'a',没有这个关键字参数
  • TypeError: f() takes 0 positional arguments but 1 was given,不需要位置参数但却传递 1 个

2.2.5 使用总结

在实际编程的时候,最常用的是可变参数系列。可变参数可以让函数的参数统一起来, 使得代码更加美观, 我在编程的时候非常喜欢使用**kwargs

# *args:  可以处理任意数量的位置参数, args这个名字约定俗成
# 这里的args将会是一个元组(tuple),包含了所有传递给函数的位置参数
def print_args(*args):for arg in args:print(arg)
nums = [1, 'two', 3.0]
print_args(nums[0], nums[1], nums[2])
print_args(*nums)  # *nums表示把nums这个list的所有元素作为可变参数传进去# **kwargs: 可处理任意数量的关键字参数, kwargs这个名字约定俗成
# 这里的kwargs将会是一个字典,其中包含了所有传递给函数的关键字参数。
# 
def print_kwargs(**kwargs):for key, value in kwargs.items():print(f"{key} = {value}")print_kwargs(a=1, b='two', c=3.0)
test = {"a": 1, "b": 'two', "c": 3.0}
print_kwargs(**test)  # **test表示把test这个dict的所有key-value用关键字参数传入到函数的**kwargs参数# 实际场景使用# 有一个平台审批流程有3条, 每一条对应的审批表单有些额外的信息不同,但基础信息一样,这时候我就希望把基础的信息写成位置参数,额外的信息用可变关键字参数使得3条审批流代码统一
def create_test_artifact_process(app_token: str, artifact: artifact_dal.ArtifactModel, start_user: str, **kwargs) -> dict:
def create_sop_release_process(app_token: str, artifact: artifact_dal.ArtifactModel, start_user: str, **kwargs) -> dict:
def create_sop_father_process(app_token: str, artifact: artifact_dal.ArtifactModel, start_user: str, **kwargs) -> dict:
# 这时候,拿额外参数的时候,只需要kwargs.get("xxx") 去拿就好

2.2.6 逆向参数收集

在Python中,逆向参数收集是指过程中的相反操作:当你有一个序列(比如列表、元组)或字典,并希望将它们的元素作为单独的参数传递给函数时使用。这可以通过*(对于序列)和**(对于字典)操作符实现,它们在调用函数时使用,将序列或字典拆解成单独的参数

# 1. 使用*对序列进行逆向参数收集: 可以将序列中的每个元素都转化为位置参数传递给函数
# 以逆向参数收集的方式,还可以给拥有可变位置参数的函数传参, 见上面
def add(a, b, c):return a + b + cnumbers = [1, 2, 3]# 使用*操作符传递numbers列表中的元素作为单独的参数
result = add(*numbers)
print(result)  # 输出6# 2. 使用**对字典进行逆向参数收集: 可以将字典中的每个键值对转化为关键字参数和对应的值传递给函数。
# 以逆向参数收集的方式,还可以给拥有可变关键字参数的函数传参, 见上面
def introduce(first_name, last_name, age):print(f"Hello, my name is {first_name} {last_name} and I am {age} years old.")person_info = {"first_name":

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/43550.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python技巧:使用enumerate函数增强你的for循环

在Python编程中,我们经常需要遍历列表、元组或其他可迭代对象。然而,在某些情况下,我们可能还需要知道当前元素的索引。这时,enumerate函数就派上了用场。以下我们将深入探讨enumerate函数的使用方法,并通过几个示例来…

Java---数组

乐观学习,乐观生活,才能不断前进啊!!! 我的主页:optimistic_chen 我的专栏:c语言 欢迎大家访问~ 创作不易,大佬们点赞鼓励下吧~ 前言 无论c语言还是java数组都是重中之重&#xff0…

LangChain 入门案例教程

LangChain 是一个基于 transformer 模型的语言链模型,它可以根据输入文本生成相应的回答。下面是一个简单的入门案例教程,旨在帮助您快速上手 LangChain。 1. 安装 LangChain 首先,您需要安装 LangChain。可以使用 pip 安装: p…

【简历】湖南某一本大学:JAVA实习简历指导,面试通过率比较低

注:为保证用户信息安全,姓名和学校等信息已经进行同层次变更,内容部分细节也进行了部分隐藏 简历说明 这个同学的学校是重点一本院校,这种学校背景我们建议大家尝试投一下大厂,然后投递主体在中厂。但是因为项目经历…

旷野之间12 - 内容创作用的最佳大模型评测

​​​​​​ 我正在做一个项目,需要我找出最适合内容创作的 LLM。我查看了 lmsys 排行榜上的顶级模型,阅读了其他人对这些模型的评价,查看了顶级 LLM 的模型卡,在没有明确答案后,我决定对所有这些 LLM 进行测试,以完成不同的内容创作任务。 评估模型 我想要评估的模型…

在iPhone / iPad上轻松模拟GPS位置 AnyGo for Mac

在iPhone / iPad上轻松模拟GPS位置 AnyGo for Mac AnyGo for Mac是一款专为Mac电脑用户设计的虚拟定位工具。它可以模拟你的GPS位置,让你的设备显示你在任何世界上的任何地方。无论你是想在游戏中虚拟移动,还是在社交媒体上分享虚拟的旅行照片&#xff0…

Flask+Layui开发案例教程

基于 Python 语言的敏捷开发框架_DjangoAdmin敏捷开发框架FlaskLayui版本_开发文档 软件产品基于 Python 语言,采用 Flask2.x、Layui、MySQL 等技术栈精心打造的一款集模块化、高性能、组件化于一体的企业级敏捷开发框架,本着简化开发、提升开发效率的初…

C 语言中如何实现字符串的拼接?

🍅关注博主🎗️ 带你畅游技术世界,不错过每一次成长机会! 📙C 语言百万年薪修炼课程 【https://dwz.mosong.cc/cyyjc】通俗易懂,深入浅出,匠心打磨,死磕细节,6年迭代&…

Objective-C 中的 isa 不再是简单的结构体指针

了解 Objective-C 中的 isa 指针内存结构 在 Objective-C 中,isa 指针是对象和类之间的重要桥梁。它不仅帮助运行时系统识别对象的类型,还参与了一些内存和性能优化。本文将深入讲解 isa 指针的内存结构,包括其在早期和现代实现中的演变。 …

Linux使用python调用串口<Ubuntu>

要在 Ubuntu 上使用 /dev/ttyUSB0 设备编写一个简单的串口收发程序,你可以使用 Python,结合 pyserial 库来实现。这种方法相对简单,适用于各种串行通信任务。以下是如何在 Python 中编写串口收发程序的步骤及代码示例: 步骤 1: 安…

JWT重放漏洞攻防策略

JWT重放漏洞概述 概念:JWT(JSON Web Token)是Web应用广泛使用的身份验证令牌。重放攻击:攻击者截获JWT后,利用其有效性冒充用户执行操作。 重放攻击的危害 权限滥用:攻击者可越权操作,如非法…

ffmpeg新旧函数对比

搬运博客园“kn-zheng”大佬博客 从FFmpeg 3.0 开始 , 使用了很多新接口,对不如下: 1、avcodec_decode_video2() 原本的解码函数被拆解为两个函数avcodec_send_packet()和avcodec_receive_frame() 具体用法如下: old: avcodec_d…

MySQL8之mysql-community-embedded-compat的作用

MySQL8中的mysql-community-embedded-compat包的作用主要是提供MySQL服务器作为嵌入式库时的兼容性支持,特别是对于那些使用库版本18的应用程序。嵌入式MySQL服务器允许开发者将MySQL数据库直接嵌入到他们的应用程序中,而无需运行独立的MySQL服务器进程。…

Transformer 论文通俗解读:FFN 的作用

在经过前面3节关于 Transformer 论文的解读之后,相信你对提出 Transformer 架构的这篇论文有了一定的了解了,你可以点击下面的链接复习一下前3节的内容。 《Attention is all you need》通俗解读,彻底理解版:part1 《Attention …

合合信息“大模型加速器”亮相2024世界人工智能大会

文章目录 📑引言一、大模型发展的挑战数据稀缺问题 二、大模型“加速器”解决方案概述文档解析引擎的特征 三、文档解析引擎的优势3.1 高速处理能力3.2 智能理解文档结构3.3 多种数据类型支持3.4 高精度数据提取3.5 应用广泛,适应性强 四、复杂图表解析4…

Auslogics Disk Defrag Pro v11激活版下载、安装、使用教程 (磁盘碎片整理工具)

前言 Auslogics Disk Defrag Pro 是一款支持 FAT16 文件系统的磁盘碎片整理工具,它可以快速整理磁盘碎片,使磁盘空间更加整洁,显著提升电脑的运行速度。该软件无需任何分析阶段,并且速度比大多数其他碎片整理软件更快。它可以帮助…

stm32 开发板可以拿来做什么?

STM32开发板可以用来做许多不同的事情,具体取决于您的应用需求和编程能力。我收集归类了一份嵌入式学习包,对于新手而言简直不要太棒,里面包括了新手各个时期的学习方向编程教学、问题视频讲解、毕设800套和语言类教学,敲个22就可…

详解太阳能控制器PWM / MPPT极简方案其设计要点,台湾远翔FP7209升压24V,30V,36V,42V,48V

文章目录 前言 一、单节电池升压9V、12V、24V方案 二、单节电池升压30V,36V,42V,48V方案 三、芯片介绍 FP7209X与FP7209M的区别: 四、单节电池升压成为市面上太阳能控制器首选的原因? 总结 前言 太阳能是一种环保…

定时器TIM配置微妙延时函数

定时器TIM配置微妙延时函数 文章目录 定时器TIM配置微妙延时函数开胃小菜(BOOT0、BOOT1)Boot0Boot1(如果有) 三种定时器高级控制定时器(TIM1,TIM8)通用定时器(TIM2, TIM3, TIM4, TIM…

linux命令: rsync的使用

使用rsync同步本地文件夹到远程目录 rsync -zvrt /localdir/ -e ssh -p 30000 rootip:/remotedir/rsync -zvrt /localdir/ /remotedir/ 远程备份 rsync -avlR --delete -e ssh -p 3231 tangboqi221.122.114.27:backup ~/backup/ R表示保留目录结构,如果不保留…