Python(四)

本文深入讲解Python函数定义、调用及各类参数使用,包括位置参数、关键字参数、默认参数、可变参数和命名关键字参数,探讨函数的灵活性与强大功能。

BIF内置函数 (built-in functions) 

【查询py中内置函数:在shell中输入“dir__builtins__敲回车即可,其中纯小写的为BIF

Help(某BIF查询具体怎么用】.

也可以直接从Python的官方网站查看文档。

调用函数的时候,如果传入的参数数量不对,会报TypeError的错误。如果传入的参数数量是对的,但参数类型不能被函数所接受,也会报TypeError的错误。

 

数据类型转换

   

函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:

定义函数

“““定义函数时,需要确定函数名和参数个数;

如果有必要,可以先对参数的数据类型做检查;

函数体内部可以用return随时返回函数结果;

函数执行完毕也没有return语句时,自动return None

函数可以同时返回多个值,但其实就是一个tuple。”””

 

在Python中,定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。

函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑。

如果没有return语句,函数执行完毕后也会返回结果,只是结果为Nonereturn None可以简写为return。

当将自定义的函数mmm()保存为myfunction.py文件时,可在当前目录下启动Python解释器,用from myfunction import mmm来导入mmm()函数,注意myfunction是文件名(不含.py扩展名)

 

空函数

如果想定义一个什么事也不做的空函数,可以用pass语句:

          

pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来。缺少了pass,代码运行就会有语法错误。

参数检查

传入了不恰当的参数时,内置函数abs会检查出参数错误,而我们定义的my_abs没有参数检查,会导致if语句出错,出错信息和abs不一样。所以,这个函数定义不够完善。

让我们修改一下my_abs的定义,对参数类型做检查,只允许整数和浮点数类型的参数。数据类型检查可以用内置函数isinstance()实现。

isinstance(object, classinfo):判断一个对象是否是一个已知的类型,类似 type()。

object -- 实例对象;classinfo -- 可以是直接或间接类名、基本类型或者由它们组成的元组。

如果对象的类型与参数二的类型(classinfo)相同则返回 True,否则返回 False。

函数的参数

ython的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码。

位置参数&关键字参数(根据实参的传递方式对参数进行的分类)

位置参数:实参位置和形参保持一致,按形参声明的先后顺序一一赋值

关键字参数:调用函数的时候以 形参=实参的方式来传参, 这个时候实参的顺序无所谓。

      

修改后的power(x, n)函数有两个参数:xn,这两个参数都是位置参数,调用函数时,传入的两个值按照位置顺序依次赋给参数xn。

 

默认参数(注:默认参数必须指向不变对象!)

声明函数的时候,可以给参数赋默认值。如果一个形参有默认值了,那么调用函数的时候这个参数就可以不用传参。
注意:有默认值的形参要放在没有默认值的形参的后面。

如何设置默认参数:当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。

n为默认参数,默认值为2。

使用默认参数最大的好处是能降低调用函数的难度(事先设定好个别参数的默认值,与默认值相同的可以不用传入,与默认参数不符的才需要提供额外的信息)

默认参数有个最大的坑,演示如下:

'''定义一个函数,当传入一个list后,自动添加一个'END’后返回'''
def defaultparameter (L=[]):
    L.append('END')
    return L

多次单独调用后出现‘END’被累计的情况。

因为:Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。

'''修改如下'''
def defaultparameter(L=None):
    if L is None:
        L =[]
    L.append('END')
    return L

为什么要设计strNone这样的不变对象呢?

因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。

可变参数(*

可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。

问题:定义一个函数,实现a²+b²+c²……的计算。

方法一:因为参数个数不确定,可以把a、b、c……作为一个list或tuple传进来,每次调用函数的时候,得先组装一个list或tuple才能实现计算。    

方法二:使用可变参数,即把函数的参数改为可变参数。

                

可变参数只在参数前面加了一个*。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数。

那么,如果要用calc()函数计算一个list或者tuple怎么办?(如计算nums = [1,2,3] 这个list的元素平方和)

在list或tuple前加一个*号,即可把list或tuple的元素变成可变参数传进去。在函数内

关键字参数(**

#关键字参数kw,允许传入0个or任意个含参数名的参数,在函数内部自动组装为一个dict
def person(name,age,**kw):
    print (‘name’,name,‘age’,age,‘other’,kw)

关键字参数的作用:扩展函数的功能。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。

此外,和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去。

注意:kw获得的dictextra的一份拷贝,对kw的改动不会影响到函数外的extra

命名关键字参数(…,*,

如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收cityjob作为关键字参数。

#*后面的参数即被视为命名关键字参数
def person(name,age,*,city,job):
    print(name,age,city,job)

和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了;命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错。(该例中缺失参数名city和job)

  

命名关键字参数也可以有缺省值,从而简化调用

参数组合

在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。

参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数

递归函数

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。

问题:设置函数计算阶乘(factorial)n!

因为n!=1 *2*3*4*…n-1*n = (n-1)!*n , 所以阶乘函数factorial(n)=factorial(n-1)*n,当n=1时,需另外注释。

def factorial(n):
    if n == 1:
        return 1
    return n * factorial(n-1)#return中有乘法表达式

递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。

使用递归函数需要注意防止栈溢出。

在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。(如factorial(1000)报错RuntimeError: maximum recursion depth exceeded in comparison)

解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。

尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。

这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的fact(n)函数改成尾递归方式,也会导致栈溢出。

def factorial(n):
    return factorial_iter(n,1)

def factorial_iter(num,product):
    if num == 1:
        return product
    return factorial_iter(num - 1,num * product)

# return fact_iter(num - 1, num * product)仅返回递归函数本身,
#num - 1,和num * product 在函数调用前就会被计算,不影响函数调用

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值