Python中如何创建函数以及详细的使用教程 各种常用函数总结

首页 » Python » Python中如何创建函数以及详细的使用教程 各种常用函数总结

函数的定义和调用

函数能提高应用的模块性,和代码的重复利用率,学习了函数,你就可以让你的代码不在那么死板,更加结构化和过程化。那么如何创建一个函数呢?这是函数的语法格式:

def 函数名(参数列表):
    函数体

在使用函数的时候你需要遵守以下几点:

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

我们创建一个简单的函数:

def fun():
    """
    在pycharm中这是自动生成的,用来解释函数的说明
    :return: 
    """
    pass

上面的就是一个简单的空函数且没有return表达式,返回值就是None。我们运行这个程序也不会报错,什么也没有做,显然它没有什么用处而言。我们往下接着学习更有趣的东西。我们重新定义一个函数,它接受一个参数并且有return表达式:

#定义一个函数
def Function(name):
    print('hello world! %s '% name)
    return 'Welcome to www.e1yu.com'
#运行函数
Function('鳄鱼君'+'\n----')
print(Function('鳄鱼君'))

# hello world! 鳄鱼君
# ---- 
# hello world! 鳄鱼君 
# Welcome to www.e1yu.com

函数必须调用才能执行,调用方法就是 函数名字+(参数列表),定义函数的时候有几个参数,调用的时候就需要传入对应值的参数,否则就会抛出TypeError。我们可以看到上面的代码单单执行函数是不会打印return语句的内容的,使用print方法打印函数,两者都会打印出来。那么return表达式的存在就是为了给函数加上返回值的,关于返回值你需要知道:

  • 返回值数=0,就是没有return,返回None
  • 返回值数=1,return一个值,返回object
  • 返回值数>1,return多个值,返回tuple元组
  • 返回值可以是函数,return function
  • 函数的返回值是为了让我们更清楚函数的执行结果

函数的实参和形参

对于函数来说,形式参数简称形参,不是实际存在的,是虚拟变量。在定义函数函数体的时候使用形参,目的是在函数调用时接受实际参数实参与形参是一一对应的

对于函数来说,实际参数简称实参。是指在调用函数时传入的实际的数据,这会被绑定到函数的形参上,可以是常量、变量、表达式、函数。

形参和实参两者的区别:形参是虚拟的,不占用内存空间,形参变量只有在被调用时才分配内存单元;实参是一个变量,占用内存空间,数据传送单向,实参传给形参,不能形参传给实参。简单一句话理解:定义函数时的参数为形参,调用函数时的参数为实参。我们看一个简单的例子:

def fun(a,b):    #形参
    "返回多个值,结果以元组形式表示"
    return a,b,a+b
fun(1,2)#实参
#实参和形参必须一一对应

函数的位置参数

基于实参的顺序,调用函数时的参数,是按照先后顺序依次赋值给函数的:

#计算x的n次方
def fun(x,n):
    s=1
    while n>0:
        n=n-1
        s=s*x
    return s
print(fun(2,3))  #2d3次方
print(fun(3,2))  #3的2次方

函数的默认参数

定义函数时的形参有默认值,你如过看到过Python的一些模块的话,会经常看到函数这样定义:def func(x, maxsize=0, loop=None),这就是默认参数,我们来看一个具体的列子:

#计算x的2次方 或者x的n次方
def fun(x,n=2):  #n默认值=2
    s=1
    while n>0:
        n=n-1
        s=s*x
    return s
print(fun(2))   #2的2次方 n有默认值为2,可以不传入n值
print(fun(2,3))  #2d3次方 #如果传入n值,就更新

默认参数必须指向不变对象!列表不能用作默认参数!

函数的可变参数

可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个,可变参数在函数调用时自动组装为一个元组tuple。我们来看一个列子:

def fun(*num):
    sum=0
    print(num) #是一个元组
    print(type(num)) 
    for i in num:
       sum=sum+i*i
    return sum

print(fun(1,2,3))

在函数内部,参数num接收到的是一个tuple,Python允许在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去:

tu=(1,2,3)
print(fun(*tu))
或者
tu=[1,2,3]
print(fun(*tu))

如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。python先匹配位置实参和关键字实参,再将剩下的实参都收集到最后一个形参中:

def func(n,*args):
    print('接收的位置参数为:%s'% n)
    print('接收的可变参数为:', args)
func('eyujun','18','gender')

# 接收的位置参数为:eyujun
# 接收的可变参数为: ('18', 'gender')

函数的关键字实参

关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。它可以扩展函数的功能,让函数可以接受任意个参数!例子:

def fun(name,gender,**kwargs):
   return {"name":name,"gender":gender,"other":kwargs}
print(fun('鳄鱼君','男'))
print(fun('鳄鱼君','男',age='18',city='河南'))

和可变参数一样,也可以先定义一个dict,然后,把该dict转换为关键字参数传进去:

def fun(name,gender,**kwargs):
   return {"name":name,"gender":gender,"other":kwargs}
dic={'hobby':'Ball','weight':'120kg'}
print(fun('鳄鱼君','男',**dic))

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

def build_profile(name, age, **user_info):
    """创建一个字典,其中包含我们知道的有关用户的一切"""
    profile = {}
    profile['name'] = name
    profile['age'] = age
    for key, value in user_info.items():
        profile[key] = value
    return profile
user_profile = build_profile('eyujun', '18',
                             location='henan',
                             field='python')
print(user_profile)

函数的命名关键字参数

对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下:

def fun(name,gender,*,hobby,weight):
   return (name,gender,hobby,weight)
dic={'hobby':'Ball','weight':'120kg'}
print(fun('鳄鱼君','男',hobby='Ball',weight='120kg'))

关键字参数**kwargs不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。如果单独出现星号, * 后的参数必须用关键字传入。错误的代码不在进行演示

函数的参数组合

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

def fun(name,age=18,**kwargs): #关键字参数必须要放到参数组前面,否则报错
    print(name,age,kwargs)
fun3('zjj',salary=12000)
fun3('zjj',33,salary=12000,age=18) #会报错,age的值给了两个
#给默认参数赋值,可以使用位置参数,关键字参数赋值

def fun2(name,age=18,*args,**kwargs):
    print(name,age,args,kwargs)
fun4('zjj',19,salary=120000) #这样传值会保存,*args接受位置参数,第三个为关键字参数,它不会接收,返回空tuple

函数的强制位置参数

/ 用来指明函数形参必须使用指定位置参数,不能使用关键字参数的形式,可以结合命名关键字参数理解。

def f(a, b, /, c, d, *, e, f): #前面说过*单独出现,e,f为关键字参数
    print(a, b, c, d, e, f)
#调用函数
f(10, 20, 30, d=40, e=50, f=60)
#形参 a 和 b 必须使用指定位置参数,c 或 d 可以是位置形参或关键字形参
f(10, b=20, c=30, d=40, e=50, f=60)   # b 不能使用关键字参数的形式
f(10, 20, 30, 40, 50, f=60)           # e 必须使用关键字参数的形式

全局变量和局部变量

在子程序(函数)中定义的变量是局部变量,在程序的一开始定义的变量称为全局变量。全局变量的作用域是整个程序,局部变量的作用域是定义该变量的子程序。当全局变量与局部变量重名:在定义局部变量的子程序内,局部变量起作用,在其它地方全局变量起作用

你可以简单理解为,函数内部定义的变量为局部变量,在函数外部定义的变量就是全局变量;全局变量在函数内部修改值,它只会在函数内部修改,在函数外部,也就是全局中,不会被改变。所以说在很多的函数中,我们可以定义相同的变量名字,他不会影响其他函数中的变量,也不会影响全局中的变量。

def fun1():
	a=1
	return a
def fun2():
	a=2
	return a
print(fun1())
print(fun2())

对于变量作用域,变量的访问以 L(Local) –> E(Enclosing) –> G(Global) –>B(Built-in) 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。

x = int(3.3)  #. 内建作用域
g = 0       #全局作用域
def outer():
    o = 1     #闭包函数外的函数中
    def inner():
        i = 2    #局部作用域
        print(x)
    inner()

outer()
#局部打印x,会从里向外找,有就打印,没有再往外找

函数内可以访问全局变量,但不能更新(修改)其值!

a = 10
def sum ( n ) :
   n += a
   a = 11    #不能更新(修改)其值,会报错
   print ('a = ', a, end = ' , ' )
   print ( 'n = ', n )
sum(3)

a = 10
def sum ( n ) :
   global a  #加上 global 引用以更新变量值 
   n += a
   a = 11
   print ('a = ', a, end = ' , ' )
   print ( 'n = ', n )
sum ( 3 )
print ( '外 a = ', a ) #a值更新为11
def fun1(name):
    print('名字改变前:',name)
    name='ZJJ' #我在函数内部修改了name值:局部变量
    print('名字改变后:',name) #函数内部打印改变的值,是修改过的

name='zjj'
fun1(name)
print(name) #我们在函数调用后打印名字,是没有修改过的

在函数内部修改外部的全局变量,可以采用下面的方法:

school='清华大学'
def fun1(name='zjj'):
    global school #定义变量为全局变量
    school='北京大学' #这样就直接把全局变量修改了
    return school
f=fun1()
print(f) 

特殊情况

name=['zjj','wff','xxk']
def name_change():
    name[0]='zgp' #在函数里面修改name
    print('name is change:%s'% name) #返回结果是修改完之后的
name_change()
print(name)

注意:

  • 全局变量如果是字符串、数字,在函数内部是不可修改的
  • 全局变量如果是列表(List)、字典(Dict)、集合(Set)、类(Class),在函数内部是可以修改的
  • 元组(Tuple)是不可改变的,不管在哪都是的

一般情况下,我们是在函数内部把变量修改为全局变量,这就存在一种情况,我们创建函数是因为它可以多次调用,假设函数很多,而我们在调用一个函数时,你已经把局部变量修改为全局变量,如果过程出了错误,你就可能找不到错误的原因,程序非常乱,所以尽量不要乱用。

递归函数介绍

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。在高中的时候我们接触到过阶乘:n! = 1 x 2 x 3 x ... x n,我们来定义一个函数处理任意数的阶乘:

def fun1(n):
  if n==1:
    return 1
  return n * fun1(n-1)
print(fun1(5))

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

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

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

尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

def fun1(num,):
   return fun2(num,1)
def fun2(num,prodect):
  if num==1:
    return prodect
  return fun2(num-1,num*prodect)
print(fun1(5))

可以看到,return fun2(num – 1, num * product)仅返回递归函数本身,num – 1和num * product在函数调用前就会被计算,不影响函数调用。如果不理解两个函数之间的计算方式,可以使用断点调试一步一步查看具体的效果。

尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的fun1(n)函数改成尾递归方式,也会导致栈溢出

– END –

未经允许不得转载:作者:鳄鱼君, 转载或复制请以 超链接形式 并注明出处 鳄鱼君
原文地址:《Python中如何创建函数以及详细的使用教程 各种常用函数总结》 发布于2019-11-08

分享到:
赞(1) 赏杯咖啡

评论 抢沙发

9 + 1 =


文章对你有帮助可赏作者一杯咖啡

支付宝扫一扫打赏

微信扫一扫打赏

Vieu4.6主题
专业打造轻量级个人企业风格博客主题!专注于前端开发,全站响应式布局自适应模板。
切换注册

登录

忘记密码 ?

您也可以使用第三方帐号快捷登录

Q Q 登 录
微 博 登 录
切换登录

注册