7

Python学习之函数

 8 months ago
source link: https://www.biaodianfu.com/python-functions.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Python的函数是一种在编程中用来封装一组操作的机制。函数可以接收输入参数,并根据这些参数执行一系列操作,最后可以返回一个值。使用函数可以提高代码的重用性和可读性。以下为整理的相关资料。

python-functions.png

函数的定义

普通函数的定义

在Python中,你可以通过def关键字来定义一个函数。函数定义后不会被执行,只有被调用的函数才会被执行。函数名称只能是由字母、数字、下划线组成,其不能以数字开头。

# 例1:加法函数
def add(a,b):
c = a+b
print("加法结果:",c) # 拓展:print(add) 输出结果为方法 add函数的储存空间位置
# 例2:减法函数
def sub(a,b):
c = a-b
print("减法结果:",c)
# 例1:加法函数
def add(a,b):
    c = a+b
    print("加法结果:",c)   # 拓展:print(add) 输出结果为方法 add函数的储存空间位置
    
# 例2:减法函数
def sub(a,b):
    c = a-b
    print("减法结果:",c)

函数的调用

要调用这个函数,你只需要使用函数名和括号,并传入相应的参数。例如:

# 调用加法函数
add(2,1)
# 调用加法函数
add(2,1) 

函数的返回(return)

def add(a,b):
c = a+b
print(c)
return c # 返回值,哪个调用就给哪个返回c
sum = add(1,2) # 调用函数
print("sum = " , sum)
# 例:
def  add(a,b):
    c = a+b
    print(c)
    return c   # 返回值,哪个调用就给哪个返回c

sum = add(1,2)   # 调用函数
print("sum = " , sum)
  • 当函数执行到return的时候,就会马上终止函数执行
  • 函数中可以出现多个return,但有且只有一个return会被执行
  • return 后面可以不跟值,return 单独使用等价于return None

函数多返回值

在Python中,函数可以返回多个值。这是通过返回一个元组(tuple)实现的,元组中可以包含多个值。在Python中,元组是用圆括号括起来的一系列值,但在返回多个值时,圆括号通常可以省略。

下面是一个函数返回多个值的示例:

def get_user_info():
name = "Alice"
age = 30
return name, age, email # 返回一个元组
user_info = get_user_info()
print(user_info) # 输出:('Alice', 30, '[email protected]')
def get_user_info():
    name = "Alice"
    age = 30
    email = "[email protected]"
    return name, age, email  # 返回一个元组

user_info = get_user_info()
print(user_info)  # 输出:('Alice', 30, '[email protected]')

在这个例子中,get_user_info函数返回了三个值:name、age和email。尽管在return语句中没有使用圆括号,Python会自动将这三个值打包成一个元组。

当一个函数返回多个值时,你可以直接在一行代码中解包(unpack)这些值:

name, age, email = get_user_info()
print(name) # 输出:Alice
print(age) # 输出:30
print(email) # 输出:[email protected]
name, age, email = get_user_info()
print(name)   # 输出:Alice
print(age)    # 输出:30
print(email)  # 输出:[email protected]

这种方式使得处理多个返回值变得非常方便和直观。

函数返回多个值在很多场景下都非常有用,例如:

  • 当函数需要产生多个相关的结果时。
  • 在需要返回复杂数据,但又不想创建一个全新的类或数据结构的情况下。
  • 在进行多任务操作,如同时返回一个结果和一个状态码时。

返回多个值的功能使Python函数更加灵活和强大,允许你以清晰、简洁的方式编写代码。

匿名函数lambda

在Python中,匿名函数是一种使用lambda关键字定义的简洁函数形式。它们通常用于定义简单的、一次性的函数,而不是复杂的、多次调用的函数。由于它们是匿名的,即没有明确的函数名,因此它们通常在需要函数对象的地方被直接使用。

  • lambda表达式可以用来声明匿名函数,即没有函数名字的临时使用的小函数。
  • lambda是一种以表达式形式生成函数的方法,和def语句类似用于创建一个函数。
  • def常用来设计功能复杂的一般性函数,而lambda用于简单的函数,以适应更灵活的应用,也被称为函数功能速写。
  • lambda定义的函数没有函数名,生成的是一个表达式形式的无名函数,表达式的结果是lambda的返回值。

以下是一些使用Lambda函数的例子:

一个无参数的Lambda函数:

f = lambda: True
print(f()) # 输出: True
f = lambda: True
print(f())  # 输出: True

带有两个参数的Lambda函数:

add = lambda x, y: x + y
print(add(5, 3)) # 输出: 8
add = lambda x, y: x + y
print(add(5, 3))  # 输出: 8

Lambda函数通常用于需要函数对象的场合,但又不想费神去定义一个完整的函数。常见的使用场景包括:

  • 作为其他函数的参数,特别是那些需要小函数作为参数的高阶函数,比如map()、filter()、sorted()等。
  • 在需要简单函数的地方提供一个快速、简洁的函数定义。
  • Lambda函数仅限于单个表达式,不能包含命令式的多行代码或复杂逻辑。
  • 如果函数足够复杂,最好还是定义一个完整的函数,以提高代码的可读性。

函数的参数

形参与实参

在Python中,函数的参数分为形式参数(形参)和实际参数(实参)两种:

形式参数(形参):

  • 形参是在函数定义时指定的变量名称,它们是函数内部的占位符,表示将要接收的数据。
  • 形参没有具体的值,它们只有在函数被调用,并且有了实参传递进来时,才会被赋予实际的值。
  • 形参使得函数更加灵活,可以在不同的地方以不同的实参调用,执行不同的操作。

例如,在下面的函数中,name是一个形式参数:

def greet(name):
return f"Hello, {name}!"
def greet(name):
    return f"Hello, {name}!"

实际参数(实参):

  • 实参是在函数调用时传递给函数的具体值。
  • 实参的值会被赋给对应的形参,然后函数会根据这些值执行操作。
  • 实参可以是常量、变量或者表达式,只要它们的值可以被计算并传递给形参即可。

例如,在下面的函数调用中,”Alice”是一个实际参数:

message = greet("Alice")
print(message) # 输出: Hello, Alice!
message = greet("Alice")
print(message)  # 输出: Hello, Alice!

理解形参和实参的区别对于编写和理解函数非常重要。形参使得函数定义变得通用,而实参则是在具体调用时赋予函数实际执行能力的具体数据。

必备参数(位置匹配)

必备参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。

def sub(a,b):
c = a-b
print("减法结果:",c)
sub(1,2) # 按形参顺序传值 a=1 b=2 输出结果:-1
def sub(a,b):
    c = a-b
    print("减法结果:",c)

sub(1,2)    # 按形参顺序传值   a=1  b=2 输出结果:-1

关键字参数

关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值

def sub(a,b):
c =a-b
print(c)
sub(b=2,a=1) # 输出结果:-1
#b=2,a=1 关键字参数
#关键字传值与顺序无关
def sub(a,b):
    c =a-b
    print(c)

sub(b=2,a=1)    # 输出结果:-1 
#b=2,a=1  关键字参数
#关键字传值与顺序无关

注意:调用函数时既传递位置参数又传递关键字参数时,位置参数要在关键字参数前面如: add(200,300,400,num5=100)

默认参数(调用时省略传值)

调用函数时,默认参数的值如果没有传入,则被认为是默认值

def 函数名(... , ... , 形参名=默认值):
def 函数名(... , ... , 形参名=默认值):
    函数体` 

注意:默认值参数必须出现在函数参数列表最右端,且任何一个默认参数右边不能有非默认参数。例:

def func2(a,b=2,c=3):
print(a,b,c)
func(1) #输出结果:1 2 3
func(1,2) #输出结果:1 2 3
func(1,c=5) #输出结果:1 2 5`
def func2(a,b=2,c=3):
    print(a,b,c)

func(1)      #输出结果:1 2 3
func(1,2)    #输出结果:1 2 3
func(1,c=5)  #输出结果:1 2 5` 

默认值参数必须出现在函数参数列表的最右端,如果某一个位置的形参设置了默认参数,那么该位置之后的所有参数都必须设置为默认参数

不定长参数(定义函数时,定义可接收多实参数据的形参)

需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数。定义一个可以接收任意数量参数的函数时需使用不定长参数: *args与 **kwargs

*args

*args用来接收多个实参并将其放在一个元组中;*args表示多参数传值,*号为关键格式,后面名称可随意改

# 定义计算2门学科求平均成绩的方法
def avg(score1,score2):
return(score1+score2)/2
# 定义计算n门学科求平均成绩的方法
def avg(*scores): #定义形参时,形参前加*号,表示可接收任意数量实参并将其置于一个元组中
return sum(scores)/len(scores) # 使用该形参时前面不需要加*号
result = avg(98.2,88.1,70,65)
print(result) # 输出80.325
scores=(88,89,90)
result2 = avg(*scores) # scores变量前必须加*解包,表示将元组各个元素分离出来,不然报错
print(result2)
def test1(*args): # 形参格式为:* + 形参名
print(args) # 输出多个参数的元组数据类型
# 调用函数,不能关键字传值:
test1(1,2,3,"xzh",18)
# 输出元组数据类型:
(1,2,3,"xzh",18)`
# 例1:
# 定义计算2门学科求平均成绩的方法
def avg(score1,score2):
    return(score1+score2)/2

# 定义计算n门学科求平均成绩的方法
def avg(*scores): #定义形参时,形参前加*号,表示可接收任意数量实参并将其置于一个元组中
    return sum(scores)/len(scores)  # 使用该形参时前面不需要加*号

result = avg(98.2,88.1,70,65)
print(result)    # 输出80.325

scores=(88,89,90)
result2 = avg(*scores)  # scores变量前必须加*解包,表示将元组各个元素分离出来,不然报错
print(result2)

# 例2:
def test1(*args):       # 形参格式为:* + 形参名
    print(args)         # 输出多个参数的元组数据类型

# 调用函数,不能关键字传值:
test1(1,2,3,"xzh",18)     
# 输出元组数据类型: 
(1,2,3,"xzh",18)`

**kwargs

**kwargs接收类似关键参数一样显示赋值形式的多个实参并将其放入字典中。**kwargs 表示多参数传值,**号为关键格式,后面名称可随意改。

def display(**employee):
print(employee)
emp={'name':'Tom','age':22,'job':'dev'}
display(name='Tom', age=22, job='dev')
#输出结果:{name:'Tom',age:22, job:'dev'}
display(**emp) #输出结果:{'job':'dev','name':'Tom','age':22} 必须加**解包
def test2(**kwargs): # 形参格式为:** + 形参名
print(kwargs) # 输出多个参数的字典数据类型
# 调用函数,只能关键字传值:
test2(name="Tom" , age=18 ,height=188.8)
# 输出字典数据类型:
{"name" : "Tom" , "age" : 18 , "height" : 188.8}`
# 例1:
def display(**employee):
    print(employee)
    
emp={'name':'Tom','age':22,'job':'dev'}

display(name='Tom', age=22, job='dev')  
#输出结果:{name:'Tom',age:22, job:'dev'}

display(**emp)   #输出结果:{'job':'dev','name':'Tom','age':22}   必须加**解包

# 例2:
def test2(**kwargs):    # 形参格式为:** + 形参名
    print(kwargs)       # 输出多个参数的字典数据类型

# 调用函数,只能关键字传值:
test2(name="Tom" , age=18 ,height=188.8)
# 输出字典数据类型:
{"name" : "Tom" ,  "age" : 18 , "height" : 188.8}`
  • 单个*号表示使用元组传值,以元组形式传入的实参数不定
  • **号表示使用字典传值,以字典形式传入的实参数不定

普通参数和不定长数参的混合使用

def test3(a,b,*args):
print("输出a和b:" ,a,b)
print("输出可变长度参数:" , args)
# 调用函数:
test3(1,2,3,4)
# 输出结果:
输出a和b:1 2
输出可变长度参数:(3,4)
# 例:
def test3(a,b,*args):
    print("输出a和b:" ,a,b)
    print("输出可变长度参数:" , args)

# 调用函数:
test3(1,2,3,4)

# 输出结果:
输出a和b:1 2
输出可变长度参数:(3,4)

变量的作用域

在Python中,变量的作用域是指一个变量可以被访问的区域。Python中主要有两种类型的变量作用域:局部作用域和全局作用域。

局部作用域(Local Scope)

局部作用域是指在函数或块内部声明的变量的作用域。这些变量只能在其定义的函数或块内部被访问和修改。

  • 当你在函数内部定义一个变量时,它就处在局部作用域内。
  • 这个局部变量仅在函数内部可见,函数外部无法访问。
  • 每次函数被调用时,都会为局部变量创建新的作用域。

全局作用域(Global Scope)

全局作用域是指在函数外部定义的变量的作用域。这些变量可以在整个代码中被访问。

  • 在脚本或程序的最顶层定义的变量属于全局作用域。
  • 全局变量可以在函数内部被读取,但如果要在函数内修改它们,需要使用global关键字。
  • 变量遮蔽(Shadowing):当局部变量和全局变量同名时,局部变量会遮蔽全局变量。在函数内部,将会优先使用局部变量。
  • global关键字:如果你需要在函数内部修改全局变量,可以使用global关键字。
  • 尽量避免使用全局变量,因为它们可能导致代码难以维护和理解。
  • 通常最好将需要的数据作为参数传递给函数,而不是依赖于全局变量。
  • 如果必须使用全局变量,确保其使用方式清晰明确。

作用域范围:Built_in > Global > Enclousure > Local。作用范围采用就近原则。

内置函数变量Built_in

def len():
print("我自己定义的方法")
len("abcdef") # 输出结果报错,并不是执行Python的内置函数len()计算长度
# 自己定义的len()函数覆盖Python的内置函数len()
def len():
    print("我自己定义的方法")
    
len("abcdef")  # 输出结果报错,并不是执行Python的内置函数len()计算长度

# 自己定义的len()函数覆盖Python的内置函数len()

全局变量Global

  • 在函数外部定义的变量,全局指的是该变量在当前python文件范围内是可见的,全局变量可以被当前python文件内所有函数直接使用。
  • 局部变量的优先级大于全局变量(变量就近原则)
a = 10 # 全局变量,全局变量作用域是所有地方
def test1():
a=20 # 函数局部变量只能在自己的函数内使用
print(a)
test1() # 输出结果 : 20
print(a) # 输出结果 : 10
# 例:
a = 10             # 全局变量,全局变量作用域是所有地方
c = 20
def test1():
    a=20           # 函数局部变量只能在自己的函数内使用
    print(a)

test1()       # 输出结果 : 20 
print(a)      # 输出结果 : 10

封装变量Enclousure

def func1():
x=100
def nested1():
print(x)
nested1()
print(x)
func1()
#输出结果:
# 修改封装变量:nonlocal
def func2():
x=100
def nested2():
nonlocal x
print(x)
nested2()
print(x)
func()
#输出结果:
def func1():
    x=100
    def nested1():
        x=99
        print(x)
    nested1()
    print(x)

func1()
#输出结果:
# 99
# 100

# 修改封装变量:nonlocal
def func2():
    x=100
    def nested2():
        nonlocal x
        x=99
        print(x)
    nested2()
    print(x)
      
func()

#输出结果:
# 99
# 99

局部变量Local

  • 在函数内部定义的变量,该变量只能在定义的函数内部使用
  • 全局变量能够同时作用于函数内外,如果想要在函数内部给一个定义在函数外的变量赋值,那么这个变量就不能是局部的,其作用域必须为全局的,可以通过global来定义。
  • 函数内定义全局变量: global 命令
def test1():
global a # 定义函数内的全局变量,
a=10 # 函数内全局变量定义格式:先定义,再赋值,不可写成一行,否则报错
print(a)
#调用函数后,全局变量才能起作用
print(a)
test1()
print("在函数外调用test1函数的全局变量:",a) # 输出结果:在函数外调用test1函数的全局变量:10
#输出结果:
#1在这里插入代码片
a=1
def test1():
    global  a       # 定义函数内的全局变量,
    a=10           # 函数内全局变量定义格式:先定义,再赋值,不可写成一行,否则报错
    print(a)
    
#调用函数后,全局变量才能起作用
print(a)
test1()                                    
print("在函数外调用test1函数的全局变量:",a)   # 输出结果:在函数外调用test1函数的全局变量:10

#输出结果:
#1在这里插入代码片
#10
#10
  • 一个变量已在函数外定义,如果在函数内需要为这个变量赋值,并要将这个赋值结果反映到函数外,可以在函数内用global声明这个变量,将其定义为全局变量。
  • 在函数内部直接将一个变量声明为全局变量,在函数外没有声明,在调用这个函数之后,将增加为新的全局变量。

使用global和nonlocal声明变量的作用域

在Python中,global和nonlocal关键字用于在函数内部声明变量的作用域。理解它们的使用对于管理变量的作用域非常重要,尤其是在涉及到嵌套函数和全局变量时。

使用global

global关键字用于在函数内部声明全局变量。当你想在函数内修改全局变量时,需要使用global关键字。

x = 10 # 全局变量
def myfunc():
global x # 声明x为全局变量
x = 20 # 修改全局变量x
myfunc()
print(x) # 输出:20
x = 10  # 全局变量

def myfunc():
    global x  # 声明x为全局变量
    x = 20  # 修改全局变量x

myfunc()
print(x)  # 输出:20

在这个例子中,通过使用global x,函数myfunc内的x指的是在函数外部定义的全局变量x,而不是局部变量。

使用nonlocal

nonlocal关键字用于在嵌套函数中声明非局部变量。它主要用于在内部函数中修改封闭函数的局部变量。

def outer():
x = 10 # 外部函数的局部变量
def inner():
nonlocal x # 声明x为非局部变量
x = 20 # 修改外部函数的局部变量x
inner()
print(x) # 输出:20
outer()
def outer():
    x = 10  # 外部函数的局部变量

    def inner():
        nonlocal x  # 声明x为非局部变量
        x = 20  # 修改外部函数的局部变量x

    inner()
    print(x)  # 输出:20

outer()

在这个例子中,nonlocal x允许内部函数inner修改外部函数outer中的局部变量x。

  • 作用域规则:global用于全局作用域,nonlocal用于封闭作用域(通常是嵌套函数中)。
  • 避免滥用:过度使用global和nonlocal可能导致代码难以理解和维护。它们应该在需要时谨慎使用。
  • 局部优先:在没有声明global或nonlocal的情况下,对变量的赋值总是创建或修改局部变量。

理解和正确使用global和nonlocal对于管理函数中的变量作用域非常关键,特别是在复杂的函数嵌套和多层作用域的情况下。

函数式编程

Python作为一门多范式编程语言,支持函数式编程(Functional Programming)。函数式编程是一种编程范式,它将计算视为函数的评估,并避免使用程序状态以及易变对象。在Python中,虽然不是纯函数式编程语言,但提供了许多函数式编程的特性和工具。

将函数作为另一函数的参数进行传递

def hello_chinese(name):
print('您好:', name)
def hello_english(name):
print('Hello', name)
def hello_japanese(name):
print('こんにちは', name)
def hello(action,name):
action(name)
hello(hello_chinese,"Tom")
hello(lambda name : print('Прывітанне!',name),"Tom")
l = list(range(1,21))
def add(x):
return x+5
#将列表l中所有元素加5
result = list(map(add,l))
result = list(lambda x:x=5,l)
#例1
def hello_chinese(name):
    print('您好:', name)

def hello_english(name):
    print('Hello', name)
    
def hello_japanese(name):
    print('こんにちは', name)
    
def hello(action,name):
    action(name)
    
hello(hello_chinese,"Tom")
hello(lambda name : print('Прывітанне!',name),"Tom")

#例2
l = list(range(1,21))
def add(x):
    return x+5
#将列表l中所有元素加5
result = list(map(add,l))
result = list(lambda x:x=5,l)

Python的高阶函数

在Python中,高阶函数是指那些可以接受函数作为参数,或者返回函数作为结果的函数。这是函数式编程的一个重要特性,允许函数操作其他函数,提供了强大的抽象能力。Python提供了许多内置的高阶函数,同时也允许你定义自己的高阶函数。

常用的Python高阶函数

map()

用途:对序列中的每个元素应用一个函数,并返回一个迭代器。

numbers = [1, 2, 3, 4, 5]
squared = map(lambda x: x * x, numbers)
print(list(squared)) # [1, 4, 9, 16, 25]
numbers = [1, 2, 3, 4, 5]
squared = map(lambda x: x * x, numbers)
print(list(squared))  # [1, 4, 9, 16, 25]

filter()

用途:对序列中的每个元素应用一个布尔函数(返回True或False),并返回所有使函数返回True的元素的迭代器。

numbers = [1, 2, 3, 4, 5]
even = filter(lambda x: x % 2 == 0, numbers)
print(list(even)) # [2, 4]
numbers = [1, 2, 3, 4, 5]
even = filter(lambda x: x % 2 == 0, numbers)
print(list(even))  # [2, 4]

reduce()

用途:对序列中的元素进行累积操作,例如累加、累乘等。注意:reduce()函数在Python 3中被移到functools模块中。

from functools import reduce
numbers = [1, 2, 3, 4, 5]
sum = reduce(lambda x, y: x + y, numbers)
print(sum) # 15
from functools import reduce
numbers = [1, 2, 3, 4, 5]
sum = reduce(lambda x, y: x + y, numbers)
print(sum)  # 15

sorted()

用途:对序列进行排序,并可接受一个函数来自定义排序逻辑。

data = [("Alice", 25), ("Bob", 30), ("Eve", 22)]
sorted_data = sorted(data, key=lambda x: x[1])
print(sorted_data) # 按年龄排序
data = [("Alice", 25), ("Bob", 30), ("Eve", 22)]
sorted_data = sorted(data, key=lambda x: x[1])
print(sorted_data)  # 按年龄排序

自定义高阶函数

你也可以创建自己的高阶函数。例如,一个接受另一个函数作为参数的函数:

def apply_twice(func, arg):
return func(func(arg))
def add_five(x):
return x + 5
result = apply_twice(add_five, 10)
print(result) # 20
def apply_twice(func, arg):
    return func(func(arg))

def add_five(x):
    return x + 5

result = apply_twice(add_five, 10)
print(result)  # 20

在这个例子中,apply_twice是一个高阶函数,因为它接受另一个函数add_five作为参数。

使用高阶函数的好处

  • 代码简洁:高阶函数可以帮助你写出更简洁和更表达性的代码。
  • 功能强大:它们可以抽象出通用的模式,使你能够用更简洁的方式处理数据结构。
  • 可重用性:可以创建通用的、可复用的函数工具,增加代码的模块性。

Python的高阶函数是其强大的函数式编程特性之一,它们在数据处理和抽象表达方面极为强大。

函数委托(别名)

在Python中,函数委托(也称为别名)是指给一个函数赋予另一个名称。这是一种简单的技术,可以增加代码的可读性和灵活性,尤其是在处理高阶函数或回调函数时。

在Python中,函数是一等公民,这意味着它们可以像任何其他对象一样被赋给变量、作为参数传递给其他函数,或者作为函数的返回值。这也意味着你可以简单地通过将一个函数赋值给另一个变量来创建函数的别名。

def original_function():
print("这是原始函数")
# 创建函数的别名
alias_function = original_function
# 使用别名调用函数
alias_function() # 输出: 这是原始函数
def original_function():
    print("这是原始函数")

# 创建函数的别名
alias_function = original_function

# 使用别名调用函数
alias_function()  # 输出: 这是原始函数

在这个示例中,alias_function成为original_function的一个别名。通过alias_function()调用实际上调用了original_function。

函数委托的应用

  • 提高代码的可读性:当函数名过于技术性或不直观时,可以通过更具描述性的别名来调用。
  • 灵活的代码重构:如果需要修改函数名,但又不想影响依赖于旧函数名的代码,可以简单地将新函数赋给旧函数的别名。
  • 在回调和高阶函数中的应用:在处理事件监听器、回调函数或高阶函数时,可以使用别名来简化对函数的引用。
  • 当你创建一个函数的别名时,原始函数和别名实际上指向内存中的同一个函数对象。这意味着对函数对象的任何修改都会反映在其别名上。
  • 在使用函数别名时,应确保它不会引起代码可读性的降低,特别是在复杂的项目中。

函数委托是Python中一个简单但强大的特性,它为代码的组织和重构提供了额外的灵活性。

列表解析 List Comprehension

列表解析(List Comprehension)是Python中一种简洁且高效的方法,用于创建列表。它提供了一种更为简洁和可读的方式来构建列表,特别是当你需要对每个元素应用某个操作或者基于某种条件过滤元素时。

列表解析的基本语法结构如下:

[表达式 for item in 可迭代对象 if 条件]
[表达式 for item in 可迭代对象 if 条件]
  • 表达式:这是你希望每个元素通过列表解析进行转换的结果。它可以是简单的表达式,也可以是复杂的表达式。
  • 可迭代对象:这是列表解析将遍历的对象。例如列表、元组、字典、文件对象等。
  • 条件:这是一个可选部分,用于指定哪些元素应该被包括在新的列表中。

简单的列表解析:

# 将每个数字乘以2
numbers = [1, 2, 3, 4, 5]
doubled = [x * 2 for x in numbers]
print(doubled) # 输出:[2, 4, 6, 8, 10]
# 将每个数字乘以2
numbers = [1, 2, 3, 4, 5]
doubled = [x * 2 for x in numbers]
print(doubled)  # 输出:[2, 4, 6, 8, 10]

带条件的列表解析:

# 仅选择偶数并乘以2
numbers = [1, 2, 3, 4, 5]
doubled_evens = [x * 2 for x in numbers if x % 2 == 0]
print(doubled_evens) # 输出:[4, 8]
# 仅选择偶数并乘以2
numbers = [1, 2, 3, 4, 5]
doubled_evens = [x * 2 for x in numbers if x % 2 == 0]
print(doubled_evens)  # 输出:[4, 8]

复杂的表达式:

# 创建一个字符串列表,其中每个字符串表示原始数字及其平方
numbers = [1, 2, 3, 4, 5]
squares = [f"Number: {x}, Square: {x ** 2}" for x in numbers]
print(squares)
# 输出:['Number: 1, Square: 1', 'Number: 2, Square: 4', ..., 'Number: 5, Square: 25']
# 创建一个字符串列表,其中每个字符串表示原始数字及其平方
numbers = [1, 2, 3, 4, 5]
squares = [f"Number: {x}, Square: {x ** 2}" for x in numbers]
print(squares)
# 输出:['Number: 1, Square: 1', 'Number: 2, Square: 4', ..., 'Number: 5, Square: 25']

列表解析与循环的比较

使用列表解析通常比使用传统的循环语句更为简洁。例如,上面的第一个示例如果使用循环来写,代码将会是这样的:

numbers = [1, 2, 3, 4, 5]
doubled = []
for x in numbers:
doubled.append(x * 2)
numbers = [1, 2, 3, 4, 5]
doubled = []
for x in numbers:
    doubled.append(x * 2)

相比之下,列表解析的方式更为简洁和直观。

  • 虽然列表解析在很多情况下都非常有用,但在处理非常复杂的逻辑或非常长的表达式时,可能会降低代码的可读性。在这些情况下,使用传统的循环可能更为适合。
  • 列表解析创建一个新的列表,如果原始列表非常大,可能会消耗大量内存。

递归函数是一种在自己的函数体内调用自身的函数。在Python中,递归是一种常见的编程技巧,用于解决可以分解为相似子问题的问题,尤其是在处理树形结构、搜索算法和分治算法时非常有用。

递归函数通常包括两个主要部分:

  • 基本情形(Base Case):这是递归停止的条件。没有基本情形,递归将无限进行下去,最终导致栈溢出错误。
  • 递归步骤(Recursive Step):在这一步,函数调用自身来解决更小的问题。

示例:以下是一个计算阶乘的递归函数的例子。阶乘函数是递归的经典例子,因为n的阶乘可以定义为n乘以(n-1)的阶乘。

def factorial(n):
# 基本情形
if n == 1:
return 1
# 递归步骤
else:
return n * factorial(n - 1)
print(factorial(5)) # 输出:120
def factorial(n):
    # 基本情形
    if n == 1:
        return 1
    # 递归步骤
    else:
        return n * factorial(n - 1)

print(factorial(5))  # 输出:120

在这个例子中,当n为1时,函数不再调用自身,返回1。对于大于1的n,函数继续递归调用自己。

递归函数的使用注意事项

  • 确保有基本情形:每个递归函数都必须有一个明确的基本情形。
  • 确保递归是向基本情形靠近:递归步骤应该逐渐减少问题的规模,最终达到基本情形。
  • 堆栈溢出的风险:由于每个递归调用都使用堆栈空间,递归过深可能导致堆栈溢出。Python默认的递归深度限制较低,可以通过setrecursionlimit来调整。

递归通常可以被迭代(循环)结构替代。迭代通常比递归更高效,因为它不涉及额外的函数调用开销和堆栈操作。然而,在处理某些问题(如树形结构的遍历)时,递归提供了更为直观和清晰的解决方案。

在Python中,偏函数(Partial Function)是一种通过固定原函数的一些参数值,从而创建一个新函数的技术。这在你希望简化函数调用,特别是对于经常使用相同参数的函数时非常有用。偏函数的使用可以使代码更加简洁和清晰。

使用functools.partial

Python的functools模块提供了一个名为partial的工具,用于创建偏函数。通过partial,你可以预先设置一些参数,生成一个新的函数。

以下是使用functools.partial的一个例子:

from functools import partial
def multiply(x, y):
return x * y
# 创建一个新函数,预设第一个参数x为2
double = partial(multiply, 2)
# 使用新的偏函数
print(double(4)) # 输出:8
print(double(5)) # 输出:10
from functools import partial

def multiply(x, y):
    return x * y

# 创建一个新函数,预设第一个参数x为2
double = partial(multiply, 2)

# 使用新的偏函数
print(double(4))  # 输出:8
print(double(5))  # 输出:10

在这个示例中,double函数是multiply函数的一个偏函数版本,它固定了multiply的第一个参数x为2,因此只需要提供第二个参数。

偏函数的应用场景

  • 简化函数调用:当你需要重复调用一个函数,并且大多数参数都是相同的,偏函数可以减少重复代码。
  • 提高代码可读性:偏函数可以帮助清晰地表达代码的意图。
  • 在回调函数中的应用:偏函数在处理事件处理程序或回调函数时特别有用,因为它们允许你将额外的状态传递给这些函数。
  • 使用偏函数时,应确保其用法和目的在代码中是清晰和明确的,避免造成阅读代码的困难。
  • 偏函数虽然提供了便利,但过度使用或不当使用可能会导致代码难以理解和维护。

偏函数是Python中一个非常实用的特性,它在减少代码重复和增强函数灵活性方面提供了极大的便利。

类型注解和提示

类型注解(Type Annotations)和类型提示(Type Hints)是Python 3引入的特性,旨在提供更清晰的代码和更好的支持静态类型检查。这些特性不会影响Python代码的运行时行为,但它们可以帮助程序员更好地理解代码,并利用工具如Mypy进行类型检查,从而提高代码质量。

函数参数和返回值:

在函数定义中,可以为参数和返回值指定类型。

def greet(name: str) -> str:
return f"Hello, {name}"
def greet(name: str) -> str:
    return f"Hello, {name}"

变量注解:

对变量进行类型注解。

age: int = 20
age: int = 20

类型提示的好处

  • 提高代码清晰度:类型注解使函数的预期用法和返回类型更加明确。
  • 便于重构:在大型代码库中,类型注解可以帮助开发者快速理解函数或方法的使用方式。
  • 提高静态类型检查效率:使用类型检查工具(如Mypy)时,类型注解可以帮助工具更准确地识别问题。
  • 改善IDE和编辑器支持:类型注解可以增强IDE和编辑器的自动补全、代码分析和重构功能。

类型注解的限制

  • Python是动态类型语言:类型注解不会强制类型检查,运行时仍然可以传递任何类型的参数。
  • 兼容性:老版本的Python不支持类型注解。
  • 可读性:在一些情况下,过度使用类型注解可能会降低代码的可读性。

类型注解的高级应用

  • 泛型(Generics):使用typing模块中的泛型类,如List[T]或Dict[K, V]。
  • 可选类型(Optional):表示变量可以是特定类型或None。
  • 联合类型(Union):表示变量可以是多种类型中的一种。
  • 类型别名:为复杂的类型注解创建简洁的别名。
  • 自定义类型:创建自定义的类型类。
  • 逐步应用:在现有代码中逐步引入类型注解,而不是一次性重构。
  • 重点应用:首先在公共API和复杂函数上应用类型注解。
  • 保持简洁:避免过度复杂的类型注解,以保持代码的可读性。

类型注解和提示是Python中用于提高代码可读性和可维护性的强大工具,尤其在大型项目和团队协作中显得尤为重要。

主函数与函数入口

Python作为一种解释型语言,与C、C++等编译型语言在程序结构上有所不同。在C或C++中,main函数作为程序的入口点是必需的,因为编译器需要一个明确的起点来开始执行程序。然而,在Python中,这种明确的“主函数”并不是必需的。

Python程序的入口点

脚本执行:当你运行一个Python脚本时,Python解释器会从脚本的第一行开始执行,一直到最后一行,除非遇到指令让它提前停止。因此,脚本的“入口”就是其第一行。

条件__name__ == “__main__”:虽然Python没有强制的main函数,但它提供了一种模拟这种行为的机制。每个Python模块都有一个内置的变量__name__,当模块被直接运行时,__name__被设置为”__main__”。因此,通过检查__name__ == “__main__”,你可以确定一个模块是被直接运行还是被导入到其他代码中。

def main():
print("Hello, World!")
if __name__ == "__main__":
main()
def main():
    print("Hello, World!")

if __name__ == "__main__":
    main()

在这个例子中,如果这个文件作为脚本直接运行,main()函数将被执行。如果这个文件被其他文件导入,main()函数不会被自动执行。

为什么Python采用这种方式?

  • 灵活性:Python不强制使用严格的程序结构,给予程序员更多的灵活性来组织代码。
  • 简洁性:对于简单的脚本,可以直接编写代码而无需定义main函数。
  • 模块化:通过__name__ == “__main__”的检查,Python代码可以方便地作为脚本运行,也可以作为模块导入,这增强了代码的可重用性。

总的来说,Python的这种设计使得编写脚本和模块更加直观和灵活,同时也支持更复杂的程序结构。

其他参考资料:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK