用户自定义函数

Python实用教程_spiritx的博客-CSDN博客

用户自定义函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

函数能提高应用的模块性,和代码的重复利用率。Python提供了许多内建函数,比如print()。也可以自己创建函数,这被叫做用户自定义函数。

函数定义

你可以定义一个由自己想要功能的函数,以下是简单的规则:

  • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()
  • 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
  • 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  • 函数内容以冒号起始,并且缩进。
  • return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。

语法:

def 函数名(函数参数列表):
    函数体
    return 函数返回值

与C、Java类似,函数通过函数名进行调用

函数参数

参数类型

python是弱类型语言,变量的类型是由其存储的值决定的,参数也是一种变量,所以函数在定义参数时,可以不定义其类型,根据实际调用时的赋值来决定其类型,这充分体现了python的灵活性。

但是,我们在做一个较大的项目时,代码量可能比较大,我们很容易忘记了某一个方法的参数类型是什么,一旦传入了错误类型的参数,再加上python是解释性语言,只有运行时候才能发现问题, 这对大型项目来说是一个巨大的灾难。

于是,自python3.5开始,PEP484为python引入了类型注解(type hints) 机制。 主要作用如下:

  • 类型检查,防止运行时出现参数和返回值类型、变量类型不符合。
  • 作为开发文档附加说明,方便使用者调用时传入和返回参数类型。
  • 该模块加入后并不会影响程序的运行,不会报正式的错误,只有提醒。pycharm目前支持typing检查,参数类型错误会黄色提示

类型注解就是在变量后面加上冒号和类型,返回值类型是在括号后用->标识,如:

def foo(a:int, b:int) ->int:
    return a+b

foo('a', 'b') #pychram会提示类型错误

类型错误并不会影响解释器的运行,但是可以让pycharm提示错误。

类型注释的检查是IDE来完成的,所以不同的IDE有不同的方式。

值传递与引用传递

在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。

  • 不可变类型:变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变a的值,相当于新生成了a。

  • 可变类型:变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。

python中,不可变类型参数都是值传递,可变类型参数都是引用传递。

def changeValue(a:int, b:list):
    a += 10 #不可变类型,值传递,在函数内改变,不影响函数外的变量值
    b.append('Last!') #可变类型,引用传递,在函数内改变,会函数外的变量值

a = 1
b = ['First', 'Second', 'Three']
changeValue(a, b)
print(f'{a=}, {b=}')

'''
a=1, b=['First', 'Second', 'Three', 'Last!']
'''

按位置传参

位置参数是最常见的参数类型。当定义一个函数时,可以指定一个或多个位置参数。在调用函数时,需要按照参数的顺序提供对应的值。

def foo2(name, age):
    print(f'Hello {name}! You are {age} years old.')

foo2('Frica', 25)

'''
Hello Frica! You are 25 years old.
'''

在上面的例子中,name 和 age 是位置参数,按照顺序分别提供 “Frica” 和 25 作为参数值。

按名称传参

关键字参数允许你使用参数的名称来指定值,而不必按照顺序提供参数。使用关键字参数可以使代码更加清晰和易读。

def foo2(name, age):
    print(f'Hello {name}! You are {age} years old.')

foo2('Frica', 25)
foo2(age=13, name='John')

'''
Hello Frica! You are 25 years old.
Hello John! You are 13 years old.
'''

在上面的例子中,通过使用 age=25 和 name="Frica" 来指定参数的值,而不必考虑它们的顺序。

缺省参数/默认参数

如果在函数调用时没有提供参数值,函数将使用默认值。默认参数通常在函数定义中指定,并且必须要位于位置参数之后(否则会有SyntaxError异常)。

def foo2(name, age, gender='male'):
    if gender == 'male' :
        print(f'Hello Mr. {name}! You are {age} years old.')
    else:
        print(f'Hello Ms. {name}! You are {age} years old.')

foo2('Frica', 25)
foo2(age=13, name='John')
foo2('Dawson', 23, 'female')
foo2(age=23, name='Dawson', gender='female')

注意:

  • 默认参数必须在最右端(最后),这样才能被解释器正确识别,否则会产生二义性 .
  • 默认参数一定要指向不变对象!

解释器会将默认参数作为一个公共对象来对待,多次调用含有默认参数的函数,就会进行多次修改。
因此定义默认参数时一定要使用不可变对象(int、float、str、tuple)。使用可变对象语法上没错,但在逻辑上是不安全的,代码量非常大时,容易产生很难查找的bug。 

def defaultzero(list = []):  #我们的本意是提供的list参数为0时 返回只有一个0的list
    list.append(0)
    return list
print(defaultzero())  #输出:[0]
print(defaultzero())  #输出:[0, 0] 显然重复调用的时候结果不是我们所期望的

#解决方案 使用None
def defaultzero1(list = None):
    if list == None:
        list = []
        list.append(0)
    return list
print(defaultzero1()) #输出:[0]
print(defaultzero1()) #输出:[0] 重复调用的时候,也输出相同

''' advanced: 简单询问框 '''
def ask_ok(hint, retries=4, complaint='Yes or no, please!'):  # 仅有hint是必须要传入的,retries 和 complaint 均有默认值
  while True:
    u = input(hint)
    if u in ('y','ye','yes'):       # in 的用法;若用户回答在('y','ye','yes') return True 
      return True
    if u in ('n','no','nop','nope'):   # 若用户回答在('n','no','nop','nope') return False
      return False
    retries = retries -1         # 若用户输入不在之前所列,可重试,重试次数-1
    if retries <= 0 :           # 若超出重试次数,raise自定义Error
      raise IOError('refusenik user')
    print(complaint)           # 若用户输入不在之前所列,提示 complaint 信息
result1 = ask_ok("Yes or No?")        # 只给必要的参数值hint,可尝试输入'y' 'no' 等;输入其他的如 'x' 超过4次
print(result1)                # 查看return的值
# result2 = ask_ok("Yes or No?",2)      # 给出retries=2,尝试输入其他的如 'x' 超过2次    
# result3 = ask_ok("Yes or No?",'Y or N?')  # 不可只省略第二个参数,若尝试输入其他的如 'x',会报错
# result4 = ask_ok("Yes or No?",3,'Y or N?') # 给出所有的参数,可尝试输入'y' 'no' 等;输入其他的如 'x' 超过3次
# print(result4)
 

可变参数

可变参数就是传入的参数个数是可变的,可以是0个,1个,2个,……很多个。作用:就是可以一次给函数传很多的参数特征。

可变按位置传参

可变数量的位置参数允许接受任意数量的位置参数。在可能不确定函数会接受多少个参数的情况下,可以使用可变数量的参数。在函数定义中,可以使用星号(*)来指定一个可变数量的位置参数。一般使用*args,变量名可以任意。在函数内部,args是一个tuple来保存可变按位置传参。

def print_info(*args):
 print(type(args))# 输出: <class 'tuple'>
    total = sum(num for num in args)
 return total
result = print_info(1, 2, 3, 4, 5)

'''
15
'''

*args可以放在命名参数的前面,也可以放在后面(但要放在默认参数前面)

def join_bysep(*strs,sep):    # strs 可为多个参数
  return sep.join(strs)     # 字符串连接函数 sep.join(str)
print(join_bysep("red","blue","green",sep=" "))
print(join_bysep("red","blue",sep=","))
print(join_bysep("red",sep=","))
print(join_bysep(sep=","))    # 无strs传参,为一空的字符串

'''
red blue green
red,blue
red

'''

可变按名传参

可变数量的关键字参数允许接受任意数量的关键字参数。在函数定义中,可以使用双星号(**)来指定一个可变数量的关键字参数。一般我们会使用 **kwargs,当然,使用其它的变量也可以(这里的关键是 **,而不是kwargs。)kwargs在函数内部是一个dict字典类型,存储参数名:参数值键值对。

def print_info(**kwargs):
 print(type(kwargs)) # 输出: <class 'dict'>
 for key, value in kwargs.items():
 print(key, ":", value)
print_info(name="Frica", age=25, city="GuangZhou")

几种传入参数法也可以组合使用

def print_info(name, *args, age=18, **kwargs):
 print("Name:", name)
 print("Age:", age)
 print("Additional arguments:")
 for arg in args:
 print("-", arg)
 print("Keyword arguments:")
 for key, value in kwargs.items():
 print("-", key, ":", value)
print_info("Frica", "arg1", "arg2", age=25, city="GuangZhou", country="China")

在上面的例子中,函数print_info接受以下参数:

  • name是一个位置参数,必须提供值。
  • *args是一个可变数量的位置参数,可以接受任意数量的额外参数。
  • age是一个具有默认值的关键字参数。
  • **kwargs是一个可变数量的关键字参数,可以接受任意数量的关键字参数。

强制按名传递参数

python可以强制部分参数必须是按名传参,按名传参和按位置位置传参中间使用*号分隔,按位置传参必须在*号前面,如:

def personinfo(name, age, *, gender, city): #只能传递gender和city参数
    print(name, age, gender, city)
personinfo('Steve', 22, gender = 'male', city = 'shanghai')  #输出:Steve 22 male shanghai
personinfo('Steve', age=23, gender = 'male', city = 'shanghai')  #输出:Steve 23 male shanghai
personinfo('Steve', 24, 'male', city = 'shanghai')  #提示TypeError错误,要求第3个参数必须是按名传参

前面的位置参数可以按位置,也可以按名传参,*号后面的参数必须按名传参。

如果中间加一个可变按位置参数*args,则就不需要另外加一个*作为分隔了,*args前面都是位置参数,后面都是按名参数

def personinfo(name, age, *args, gender, city): #只能传递gender和city参数
    print(name, age, gender, city, f'{args=}')
personinfo('Steve', 22, 'London', 15, gender = 'male', city = 'shanghai')  #输出:Steve 22 male shanghai
personinfo('Steve', age=23, 'London', 15, gender = 'male', city = 'shanghai')  #参数有二义性,提示SyntaxError

但是,*args前面的按位置参数就不能再按名称传递了,因为这会引发解释器的二义性,解释器直接提示语法错误

返回值

函数如果需要返回值,需要使用return语句。

不带参数值的return语句返回None,即使没有return语句函数也会返回 None.

Python中的return语句可以从函数中带出不同类型的值,这与C、Java等语言不同。

def retTypeValue(n):
    if n == 0:
        return "OK"
    elif n == 1:
        return (1, 2, 3)
    elif n == 2:
        return [1, 2, 3]
    elif n == 3:
        return {"1": "one", "2":"two", "3":"three"}
    else:
        return None

注意,python中的return可以返回多个值,多个值通过逗号分隔开,对于函数调用者来看,函数是返回的一个tuple类型,return的时候,也可以用小括号括起来,两种用法是等效的。

return a,b
return (a,b)

Lambda函数

Lambda函数也被称为匿名(没有名称)函数,它直接接受参数的数量以及使用该参数执行的条件或操作,该参数以冒号分隔,并返回最终结果。为了在大型代码库上编写代码时执行一项小任务,或者在函数中执行一项小任务,便在正常过程中使用lambda函数。

定义为:

lambda argument_list:expersion

  • argument_list是参数列表,它的结构与Python中函数(function)的参数列表是一样的
  • expression是一个关于参数的表达式,表达式中出现的参数需要在argument_list中有定义,并且表达式只能是单行的。

普通函数和Lambda函数的区别

1、没有名称

Lambda函数没有名称,而普通操作有一个合适的名称。

2、Lambda函数不需要指定返回值

使用def关键字构建的普通函数需要通过return语句指定返回值,但在Lambda函数不能通过return指定返回值,它的返回值只能是后面表达式的计算结果。实质上,Lambda返回的是一个带参数的函数对象(第一类对象)。

假设我们想要检查数字是偶数还是奇数,使用lambda函数语法类似于下面的代码片段。

b = lambda x: "Even" if x%2==0 else "Odd"
print(b(9))

’’’
Odd
‘’‘

3、函数只​​​​​​​在一行中

Lambda函数只在一行中编写和创建,而在普通函数的中使用缩进

4、不用于代码重用

Lambda函数不能用于代码重用,或者不能在任何其他文件中导入这个函数。相反,普通函数用于代码重用,可以在外部文件中使用。

简单的性能比较

lambda函数会略微提高效率,但效果并不是很明显:

# 测试的Def函数
def square1(n):
    return n ** 2

# 测试的Lambda函数
square2 = lambda n: n ** 2

t1 = time.time()

# 使用Def函数
i = 0
while i < 100000000:
    square1(100)
    i += 1

t2 = time.time()
print(f'def函数用时:{t2-t1}')

t1 = time.time()

# 使用lambda函数
i = 0
while i < 100000000:
    square2(100)
    i += 1

t2 = time.time()
print(f'limbda函数用时:{t2-t1}’)

‘’'
def函数用时:22.46737313270569
limbda函数用时:21.934571027755737
’‘’

lambda函数首先减少了代码的冗余,其次,用lambda函数,不用费神地去命名一个函数的名字,可以快速的实现某项功能,最后,lambda函数使代码的可读性更强,程序看起来更加简洁。

lambda函数在高阶函数中有较多使用,这个在后面的高阶函数的学习再举例说明

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值