Python 基础集合6:函数基本用法

一、前言

本小节主要梳理函数的基本知识,包含函数的定义和作用、参数的类别、return语句、变量作用域等。另外拓展函数的三种展现形式:匿名函数、闭包、装饰器。

环境说明:Python 3.6、windows11 64位

二、函数介绍

2.1 函数是什么

函数是组织好的、可以重复使用的、用来实现单一功能的代码。由于函数的这些特性,使得主体代码可以变得更加的简洁,一些功能可以反复调用,减少重复写代码,代码之间的耦合度也降低,降低维护难度,提高效率。
基本语法:

def 函数名(参数1,参数2...参数n): 
     函数体

注意点:

  • 函数名和变量名的命名逻辑差不多,不能数字开头,多个词关联只能用下划线,函数名也可以用中文命名!命令时尽量和所实现的功能相近,特别在函数比较多的情况下,方便理解。
  • 参数的类别比较多,有位置参数,默认参数,不定长参数,关键参数(下文逐一说明)。
  • 函数体是所有实现功能的代码。

写一个简单函数(如下),该函数就是把Hello world!封装起来,每次我们调用func()的时候就可以直接打印Hello world!了,是不是很简单。

# 定义函数
def func():
    print('Hello world!')
# 调用
func()

当然,如果仅仅是打印Hello world!大可不必这么大费周章,但是如果是其他很复杂的一些逻辑就大有裨益。
像很多Python库,基本都是已经封装好的,然后暴露相关可以完成某些工作的接口,这些接口的名称,往往对应着某个底层的函数,贡献者把相关的代码封装起来,然后留一个函数名给使用者进行调用。最基本的就是Python的内置函数,如str()、int()、float()等。

2.2 函数的参数

参数的类别比较多,有位置参数,默认参数,不定长参数,关键参数。

  • 位置参数:就是跟参数所在的位置相关,涉及到参数的顺序,其实有点像列表或元组,每一个参数有一个位置标签,传入参数时按照位置标签一一匹配。如下代码,我创建了一个叫Say_Hello的函数,函数有两个参数:greetobjects,greet是问候语变量,objects是问候对象变量。我后面调用的时候,按顺序传入('Hello','world')就可以打印出Hello world!。如果我改用Say_Hello('world','Hello')是否可以呢?大家可以试试。代码不会报错,但是结果便反过来了,变成了world Hello!
    • 注:除了按顺序赋值,还可以通过指定参数名,如下最后2行
def Say_Hello(greet, objects):  # 定义函数
    print('%s %s!'%(greet, objects))
Say_Hello('Hello','world')      # 该句为调用函数,打印结果为:Hello world!
Say_Hello(greet='Hello',objects='world')
Say_Hello(objects='world',greet='Hello')
  • 默认参数:给参数赋一个值,作为默认值,如果不传参数则采用默认值。注意,默认参数要放在位置参数后面。如下代码,给greet加上一个默认值,则参数要放在objects后面,传参时可以不用传,也可以重新传一个覆盖已有的值。
def Say_Hello(objects,greet='Hello'):
    print('%s %s!'%(greet, objects))
Say_Hello('world')       # 打印结果为:Hello world!
Say_Hello('world','Hi')  # 打印结果为:Hi world!
  • 关键参数:就是**kw。可以不用传值,也可以通过参数=值的形式传递一个或多个参数。具体看看以下例子。
def Say_Hello(objects,greet='Hello',**kw):
    print('%s %s! %s'%(greet, objects,kw))
Say_Hello('world','Hi',my_name='Xindata')               # 打印结果为:Hi world! {'my_name': 'Xindata'}
Say_Hello('world','Hi',my_name='Xindata',other='test')  # 打印结果为:Hi world! {'my_name': 'Xindata', 'other': 'test'}
  • 不定长参数:不限制参数个数,可以传一个或多个,以元组形式返回。返回的时候也是以元组的格式返回。如果要将元组元素分别取出来,可以使用join()
def Say_Hello(greet, *objects):
    print('%s %s!'%(greet, objects))
    # print('%s %s!'%(greet,'、'.join(objects)))
Say_Hello('Hello','world')            # 打印结果为:Hello ('world',)!
Say_Hello('Hello','world','Xin学数据')  # 打印结果为:Hello ('world', 'Xin学数据')!

2.3 函数体

函数体是所有实现功能的代码。函数体中可以通过return返回一个值,在函数调用的时候,可用于赋值给变量;当不需要返回值时,只是实现某项功能时,也可以不用加return语句。
前面讲列表排序的时候涉及到两个函数:list.sort()sorted(list)。前者没有返回值,仅对列表的顺序进行改变,所以使用sort()函数之后,原来的列表的元素顺序便改变了,但是没有返回任何值(函数没有返回值,如果用于赋值,打印出来的变量是None)。后者有返回值,调用该函数之后,不会对原来的列表的元素的顺序进行更改,而是返回一个新的列表,这个新的列表就是sorted()函数的返回值。
image.png

ls = [3,7,0,5]
ls1 = sorted(ls)
print(ls)        # [3, 7, 0, 5]
print(ls1)       # [0, 3, 5, 7]
ls2 = ls.sort()
print(ls)        # [0, 3, 5, 7]
print(ls2)       # None

2.3.1 return

函数体遇到return之后就停止执行并将结果返回。当一个函数没有return时,返回None

def Hello(words):
    return words
    print('该句不打印!')
val = Hello('Hello world!')
print(val) 	     # 打印结果为:Hello world!

2.3.2 变量作用域

函数外定义的变量属于全局变量,在函数内部可以进行调用。

my_name = 'Xindata'
def Hello():
    print('My name is %s.'%my_name)
Hello()         # 结果为:My name is Xindata.
print(my_name)  # 结果为:Xindata

函数内部定义的变量属于局部变量,在函数外部调用该变量会报错。

def Hello():
    my_name = 'Xindata'
    print('My name is %s.'%my_name)
Hello()
print(my_name)  # 报错NameError: name 'my_name' is not defined

常见的错误:在全局定义一个变量,然后再函数内部使用该变量,然后又赋值给一个名字相同的变量。(如下num)

num = 1
def test():
    num += 1
    print(num)
test() 

那想要将函数内部的变量给到全局使用怎么办?用global。举个例子:

def Hello():
    global my_name
    my_name = 'Xindata'
    print('My name is %s.'%my_name)
Hello()         # 结果为:My name is Xindata.
print(my_name)  # 结果为:Xindata

2.4 另外三种展现形式

除了上面讲的比较常规的展现形式,还有一些其他的展现形式,这里主要讲三种:匿名函数、闭包、装饰器。

2.4.1 匿名函数

匿名函数的关键字是lambda。一般用于实现一些比较简单的处理。
比如将上一个代码用lambda实现如下:

# 原代码
def Hello(words):
    return words
val = Hello('Hello world!')
print(val) 	     # 打印结果为:Hello world!
# lambda 实现
Hello = lambda words:words
val = Hello('Hello world!')
print(val)       # 打印结果为:Hello world!

lambda函数的格式是在lambda后面跟参数,同普通函数的参数,然后冒号,加上处理逻辑即可。

lambda [参数]:[处理逻辑]

lambda函数用于处理数据会比较多一些,像用pandas或numpy处理数据的时候,经常要传入一个简单的函数处理数据,通过lambda处理会比较简洁、方便。

import pandas as pd
dic = {'A':[1,3,4,2],'B':[8,3,9,5]}
df = pd.DataFrame(dic)
print(df)
df['AA'] = df.A.apply(lambda x:x**2)  # 将A列的值进行平方,然后在df中新增AA列来存放
print(df)

image.png

2.4.2 闭包

闭包就是函数里再嵌入一个函数,内层函数将结果返回给外层函数,调用外层返回内层函数(是返回,而不是调用,不需要加括号)。
作用:将函数作为返回值,传递函数。需要查看函数结果时再进行调用打印。

def Introduction(greet,*words):
    def My_Introduction():
        my_words = ' '.join(words)                    # 将传递给words 的值用空格连接起来
        return '%s,%s!'%(greet,my_words) 
    return My_Introduction   # 注意,没有括号,调用Introduction()时返回My_Introduction 函数
aa = Introduction('Hello','My name','is','Xindata')   # 外层return 返回值(My_Introduction函数)赋值给变量aa ,此时aa 是一个函数类型的变量
bb = aa() # 调用aa() 函数,将内层return 返回值(字符串)赋值给变量bb
print(type(aa))  # 结果为:<class 'function'>
print(type(bb))  # 结果为:<class 'str'>
print(aa) # 结果为:<function Introduction.<locals>.My_Introduction at 0x000001F6D5FC0A60>
print(bb) # 结果为:'Hello,My name is Xindata!'

2.4.3 函数装饰器

装饰器的作用是给函数增加新的功能,但又不修改原来的函数,而是使得调用原函数的时候附加一些功能。
装饰器一般是一个闭包函数,只是函数接收的参数是一个函数。如下代码,Self_Introduction()就是一个装饰器,直接调用即可。

# 定义一个装饰器
def Self_Introduction(func):   # 传入被装饰的函数
    def wrapper():
        print('print wrapper') 
        return func()          # 返回被装饰函数的调用结果,如果传递的func函数没有return,则结果为None,此时return 可以去掉。但是由于装饰函数一般需要具备一定的普适性,所以习惯加上return 。
    return wrapper  # 返回wrapper 函数

def Introduction():
    global name
    name = 'Xindata'
    print('My name is %s!'%name) # 在装饰器函数中调用的时候才执行,如果装饰器没有调用,则不执行
    return 'name:%s'%name       # 调用Introduction() 时的返回值

## 调用函数
aa = Self_Introduction(Introduction) # 将wrapper 函数赋值给aa
print(aa)   # 结果为:<function Self_Introduction.<locals>.wrapper at 0x0000019A7956F9D0>
bb = aa()   # 调用aa() 函数,结果为:print wrapper【wrapper()函数print()结果】 和 My name is Xindata!【调用func()的结果】。当被修饰的函数(这里是Introduction()有返回值时,该返回值会赋值给bb,传递过程是通过wrapper 函数调用func(),func()执行之后返回Introduction()的return 值给到wrapper的return值,然后在调用aa函数时传递给bb)
print(bb)   # 结果为:name:Xindata
print(name) # 结果为:Xindata。如果wrapper中没有调用func()则报错。

装饰器除了像以上展示代码,通过调用装饰器,传入函数的方法,还可以用@语法糖,然后调用被装饰的函数来实现。具体如下代码:

def Self_Introduction(func):  
    def wrapper():
        print('print wrapper') 
        return func()          
    return wrapper  

@Self_Introduction   # 在Introduction前,用@Self_Introduction声明用Self_Introduction 装饰Introduction
def Introduction():
    global name
    name = 'Xindata'
    print('My name is %s!'%name) 
    return 'name:%s'%name     

## 调用函数
aa = Introduction # 直接将Introduction 赋值给aa,相当于早期方法调用了Self_Introduction(Introduction),将wrapper 函数赋值给aa
print(aa)   
bb = aa()   
print(bb)   
print(name) 

更详细的用法可参考Python 进阶:函数装饰器

小结

python函数是一个很有用的工具,可以解决很多重复性的工作。它就好比是我们经常干的一件事,把一些经常要用的东西做成模板,之后每一次使用时,创建一个副本即可。
本文的知识架构如下:
image.png

<下节预告:类和实例>

  • End -
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xin学数据

为你点亮一盏灯,愿你前进无阻。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值