Python基础知识总结笔记(四)函数

一个人想着一个人 提交于 2020-02-21 19:33:26

Python基础知识总结笔记(四)函数
python中的函数
函数中的参数
变量作用域
偏函数PFA
递归函数
高阶函数
BIFs中的高阶函数
匿名函数lambda
闭包Closure
装饰器Decorator
函数式编程Functional Programming
1. python中的函数
■ 函数的意义:
■1.对输入进行变换映射后输出 ,可以进行反复调用。以函数名对代码块进行封装
■2.过程化 VS 结构化
■ 函数的创建及结构:
■定义函数名:def foo1(num):
■参数:num
■函数体
■返回
□有无返回:
有返回值:return返回的是对象,(如返回对象数>1返回元组)。且返回的为最后的一个return值。
无返回值 :无返回值或者return后为空。
print可以有很多,但是return只有一个。
□return与yield的区别:
print:不返回任何的函数
return 结束函数,返回值,当函数被调用时,遇到结尾或return时就准备结束了。只显示最后一个return的值。
yield:丢出一个数,对象变成了生成器–记录当前运行到哪里了。

#函数的意义

n=5
for i in range(n):
    print(i)
    
    
# 以函数名对以上代码块进行封装
def foo(num):       #函数名foo; 参数num
    for i in range(num):
        print(i)


#然后进行反复调用
# foo()

#对输入进行变换映射后输出

    
#函数的定义
def foo1(num):#函数名与参数
#函数体
    cumsum=0#函数内部的变量
    for i in range(num):
        cumsum+=i
#返回值
    return cumsum
foo1(15)


#函数的返回值-无返回值 
def foo2():#函数名与参数
#函数体
    cumsum=0
    for i in range(15):
        cumsum+=i
#无返回值
#或者return后为空
foo2()

#函数的返回值,有返回值
def foo3():#函数名与参数
#函数体
    cumsum=0
    for i in range(15):
        cumsum+=i
        
#return返回的是对象,(如返回对象数>1返回元组)
    return cumsum,cumsum*2
#函数结束

foo3()


#return与print在Notebook上的差异
#print可以有很多,但是return只有一个

# def foo2():
#     for i in range(5):
#         print(i)

# foo2()

# #函数的参数
# def foo3(num):
#     for i in range(num):
#         print(i)
#     return 'Done'
# foo3(1)

# print(foo3(2))

### 函数的返回

#return与yield关键字

#return 结束函数
def foo1():#不返回任何的函数
    print('i\'m foo1,but i return nothing')

def foo2():#使用return返回值
    return 'i\'m foo2, this is what i return'

def foo3():#函数被调用时,遇到结尾或return时就准备结束了。
    return 'i\'m foo3, this is the first return'
    return 'i\'m foo3, this is the secod return'


#yield 丢出一个数--对象变成了生成器--记录当前运行到哪里了
def foo4():
    for i in range(5):
        return i#执行第一次时foo4函数就结束了

    
#yield
def foo4():
    for i in range(5):
        yield i
        yield 'f' #再加入一个yield看看会不会被执行
        i+=1
        


g1=foo4()#看一下调用foo4()返回的是什么
type(g1)#看一下g1是个什么样子的对象
next(g1)#继续上一次的位置,进入下一层循环
#执行完最后一次循环,结束 yield语句并抛出StopIteration 异常



2. 函数中的参数
■ 参数:
■语法:
func(positional_args,keyword_args,*tuple_nonkw_args,**dict_kw_args)
positional_args位置参数
keyword_args关键字参数
tuple_nonkw_args非关键字不定长参数
dict_kw_args关键字不定长参数

■ 按参数传递方式:
■位置参数(定位参数,非关键字参数) :位置顺序十分重要,不可缺少个数。对位置敏感,传递时不用指定参数名
■关键字参数:等于号确定。intro=" "
不传关键字参数,函数依旧可以被调用,采用默认值。
传关键字参数,则覆盖默认值。
■位置参数包裹及使用*:一次传入不定长个位置参数。传入n个位置参数。传进来列表[]
■关键字参数包裹及使用 **:一次性传入不定长关键字参数传入n个关键字参数。传进来字典{}

位置参数包裹可以写n个——列表;关键字参数包裹可以写n个——字典

■包裹解包顺序:首先拆位置参数包裹,按顺序给必选,默认,可变。再抓给关键字参数包裹。
■传递不定长参数时,使用包裹:位置参数
■传递参数时,使用包裹:关键字型参数
传递不定长参数时使用包裹——位置参数必须打碎(只传递内容)再传递,传递参数同理。即:列表类型、tuple类型必须(*list);字典类型必须( * *字典)。若传(list)代表的是传对象对不上函数。

l1=[1,5,6,7,2,5,3.5,9,6,3,4]
mysum2(*l1)#mysum2(1,5,6,7,2,5,3.5,9,6,3,4)可以,但是mysum2(l1)不可以,因为相当于是传递了l1为一个list类型的对象。*l1表示可迭代对象,里面有多少对象就当多少对象接受。
1
2
■ 按参数的类型:
■必选(位置参数):必须写、个数要对应
■关键字/默认:*args可变长位置参数,**kwargs可变长关键字参数
其中:
*args列表
**kwargs字典
■ 函数如何处理传入参数
■值传递参数与指针传递参数:
值传递参数:值传递时,变量传递给函数后,函数复制一份,不会影响原有变量。
指针传递参数,会修改参数本身:指针(或引用)传递时,变量传递给函数的是及引用,该引用可以改变原变量

#按参数传递方式

#位置参数
def foo3(companyName,websiteUrl):#位置参数的定义
    print('公司名:',companyName,'公司网址:',websiteUrl)
foo3('七月在线','http://www.julyedu.com')
foo3('http://www.julyedu.com',websiteUrl='七月在线') 

#位置参数,对位置敏感,传递时不用指定参数名

#关键字参数
def foo4(companyName,websiteUrl,intro='专注AI教育'):#关键字参数的定义intro='专注AI教育'
    print('公司名:',companyName,'公司网址:',websiteUrl,'介绍:',intro)

foo4('七月在线','http://www.julyedu.com')#不传关键字参数,函数依旧可以被调用,采用默认值
foo4('七月在线','http://www.julyedu.com',intro='国内领先的AI教育平台')#传关键字参数,覆盖默认值

#如果想一次传入不定长个位置参数,怎么办呢?
#位置参数包裹及使用*

def mysum1(a,b,c):
    return a+b+c

mysum1(5,7,15)
#mysum1(5,7)  #报错,因为传入的参数少于函数定义时的个数
#mysum1(5,7,15,22)  #报错,因为传入的参数多于函数定义时的个数

#这时我们就需要使用包裹*args(tuple类型)进行接收
#位置参数包裹可以写n个——列表;关键字参数包裹可以写n个——字典
def mysum2(*args):
   # print(type(args))
    return sum(args)

mysum2(5,7,15,22,33)#正常调用
mysum2(5,7)#正常调用


#那同样如果想一次性传入不定长关键字参数,也可以使用一个包裹进行接收
#关键字参数包裹及使用**

def filmInfo(**kwargs):
    print(type(kwargs))
    for key,values in kwargs.items():
        print(key,':',values)

filmInfo(film='羞羞的铁拳',box=3.5)
filmInfo(film='羞羞的铁拳',box=3.5,rate=7.9)
# d1={'羞羞的铁拳':3.5,'box':3.5}


#包裹解包顺序
#首先拆位置参数包裹,按顺序给必选,默认,可变。
#再抓给关键字参数包裹
def scoreReport(name,age,*args,course='python',**kwargs):
    #位置;位置;位置参数包裹;关键字(默认,可以不修改);关键字参数包裹
    #位置参数包裹可以写n个——列表;关键字参数包裹可以写n个——字典
    print('个人信息:',name,age)
    for item in args:
        print(item)
    print('课程信息:',course)
    print('每节课成绩:')
    for key,value in kwargs.items():
        print (key,value)
        
scoreReport('xiaoming',22,'高中部','三年二班',Lesson1=80,Lesson2=85)
scoreReport('xiaoming',22,'三年二班',course='machine learning',Lesson1=80,Lesson2=85)


# #传递不定长参数时使用包裹-位置参数 必须打碎(只传递内容)再传递,即列表类型必须(*list);字典类型必须(**字典)。若(list)代表的是传对象对不上函数。
l1=[1,5,6,7,2,5,3.5,9,6,3,4]
mysum2(*l1)#=mysum2(1,5,6,7,2,5,3.5,9,6,3,4)

# #zip案例
# l1=[(1,3),(2,4)]
# list(zip(*l1))

# ##传递参数时使用包裹-关键字型参数
# d1={'羞羞的铁拳':3.5,'雷神3':3.1,'战狼2':60}
# filmInfo(**d1)


#值传递,参数本身不会被修改
a=7
b='julyedu'
print('before reference:',a,b)

def foo1(a,b):
    a+=1
    b+='.com'
    print('In foo1 a,b has changed to',a,b)

foo1(a,b)
print('after reference:',a,b)
#结论:值传递时,变量传递给函数后,函数复制一份,不会影响原有变量


print('>'*40)


#指针传递参数,会修改参数本身
l1=[1,2,3.4]
d1={'name':'jack'}
def foo2(a,b):
    a.pop()
    b['age']=22
print('before reference:',l1,d1)

foo2(l1,d1)


print('after reference:',l1,d1)
#结论:指针(或引用)传递时,变量传递给函数的是及引用,该引用可以改变原变量


3. 变量作用域
位置与顺序
■ 标识符的作用域
globals()#全局变量名称空间
locals()#局部变量名称空间
■ 全局变量
■定义:全局变量会一直存在,除非是脚本运行结束或 del 删除它。
■经过函数时:可以被函数内部调用
■ 局部变量
■函数内部创建与访问,当被调用结束时,生命周期结束。
■ 变量的搜索顺序
■覆盖问题:重名,从局部到全局:先从局部找,找不到再去全局找。
■局部作用域->全局作用域:从局部到全局

4. 偏函数PFA
■ 偏函数Partial function application
■使用场景:如果一个函数的参数很多,而在每次调用的时候有一些又经常不需要被指定时,就可以使用偏函数(近似理解为默认值)有些参数无需修改,固定部分参数
只设置了一部分的参数的函数,固定一部分参数,使得被调用时,某些参数被固定住了。
■语法:partical(func,*args,**keywords)
■使用:from functools import partial
■原理:创建一个新函数,固定住原函数的部分参数(可以为位置参数,也可以是关键字参数),偏函数的类型与普通函数不一样,为:functools.partial。

#Partial function application(PFA)
# 只设置了一部分的参数的函数
# 固定一部分参数,使得被调用时,某些参数被固定住了。


#例如我们要定义一个函数将传进来的16进制的字符串,转换为10进制的
def hex2int(num):
    return  int(str(num),base=16)#base为关键字参数,这个在调用int这个函数时,固定给16。因为这个函数就是用来转换16进制到10进制

hex2int('F')#这时调用,相当于实际上会把10作为*args的一部分自动加到左边,也就是:int('F',base=16),这样就少写了一个函数


#这时也可以使用偏函数,固定int函数的关键字参数base,可以这样定义:
import functools
hex2int2=functools.partial(int,base=16)
#hex2int2('A')


#偏函数能固定位置参数?
#可以
max100=functools.partial(max,100)#定义一个叫max100的偏函数,将这个偏函数的第一个值固定为100
max100(101)#这时调用它,传入的值,都会与100进行比较反并返回。

type(max100)#偏函数的类型与普通函数不一样


5. 递归函数
■ 定义:
■函数在内部调用自身
■ 例子:
■求一个列表中数值的累加

#递归的方法求一个列表中数值的累加
def foo1(num):
    if len(num)==1:
        return num[0]
    else:
#         print(num[0],'>'*6)
#         print(num[1:],'   >'*6)
        return num[0]+foo1(num[1:])
    
foo1([1,2,31,5,6,55])

#使用递归分解质因数
l1=[]
def fenji(num):
    num=int(num)

    for i in range(2,num):
            if num%i==0:
                l1.append(i)
                nextv=int(num/i)
                fenji(nextv)
                break#这里的break不加就会硬循环60次                
    return l1

fenji(60)

#阶乘
def factorial(n):
    if n==1:
        return 1
    else:
        return n*factorial(n-1)
factorial (5)


6. 高阶函数
■ 函数的引用与调用
■引用:访问,变量别名(多个别名引用)
一个对象可以被多个变量引用。
■调用:() 最后返回结果
■ 函数对象可以被引用,可以作为参数被传入或作为结果被返回。
■ 高阶函数:
■==一个函数接收另一个函数作为参数。==将函数作为变量。可接受一个指向函数变量的变量名
■ 回调函数:
■函数作为调用函数的结果返回。返回函数

#函数对象的引用 与 调用
num=-5
a=b=c=abs

#引用
print(abs,a)

#调用
a(num)
b(-8.8)
c(7)

#函数名本质即变量,同一个函数对象可以被多个变量引用
def foo():#新建一个函数对象,并用foo这个变量名对其进行引用 
    pass

del foo#对函数对象的引用可以删除

#高阶函数
def mycalucate(num,func):
    return func#返回函数

l1=[5,8,3,6,9,15,22]

mycalucate(l1,max)
mycalucate(l1,min)
mycalucate(l1,sum)
mycalucate(l1,reversed)


#回调函数
def callbackfunc(*num):
    return max
callbackfunc(53,5,33)


7. BIFs中的高阶函数
■ filter
■核心点:对每个元素做过滤
语法:filter(function,list可迭代对象)
函数 f 的作用是对每个元素进行判断,返回 True或 False。
filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。
■ map
■核心点:对每个元素做映射
语法:map(function, list)
让list的每一个元素依次调用function函数,并获取返回值存入一个新的list中。
■ reduce
■核心点:两两传给func
■Python 3.x中,reduce()函数已经被从全局名字空间里移除了,它和partical一样被放置在 fucntools模块中。使用前需要调用.
语法:from functools import reduce
reduce(function, list)
函数function必须包含两个参数,optional可选,如果存在则表示初值为optional
reduce()对list的每个元素反复调用函数f,并返回最终结果值。

l1=[2,3,5,7,3,4,5,9,'julyedu.com',[2,3,3],5,2]

filter
#语法:filter(function,list)
# 函数 f 的作用是对每个元素进行判断,返回 True或 False
# filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。


def myfilter(x):
    if x>60:
        return True
    else:
        return False

myfilter(61)

l1=[1,61]

filter(myfilter,l1)

list(filter(lambda x:True if type(x)==str else False,l1))

#map
# 语法:map(function, list)
#让list的每一个元素依次调用function函数,并获取返回值存入一个新的list中。

map(lambda x:(x,l1.count(x)),l1)


# reduce
# 语法:reduce(function, list)
#函数function必须包含两个参数,optional可选,如果存在则表示初值为optional 
#reduce()对list的每个元素反复调用函数f,并返回最终结果值。
from functools import reduce
reduce(lambda a,b:a+b,[1,2,3,4,5,6,7,8])


8. 匿名函数lambda
■ 匿名函数lambda: 简化函数
■使用场景:
□返回简单结束 ,不需要专门定义函数
■特点:
□简洁,同一行定义体+声明。不用写return
■定义:
□定义后,赋值给一个变量,做正常函数使用
□lambda关键字定义在高阶函数的参数位上
■语法:
□lambda格式:lambda 接受n个参数:函数体,即: lambda(args1,args2, argsN):expression

#匿名函数--简单的函数功能 
#lambda语法:lambda 接受n个参数:函数体
print(type(lambda a,b:a**b ))
#定义
#函数体
#不用写return

#使用1:作为正常函数使用,不推荐
foo=lambda x,y:x+y #不用写return

#lambda
print(foo(5,6))

#使用2:lambda关键字定义在高阶函数的参数位上
d1={'china':15,'India':9,'USA':2,'Japan':1.5} #排序key
sorted(d1.items(),key=lambda x:(x[0],x[1]))#按d1.items()第0个元素升序,国家名
# sorted(d1.items(),key=lambda x:(x[1]))#按d1.items()第1个元素升序,人口数

9. 闭包Closure
■ 闭包的概念
■涉及嵌套函数时才有闭包问题
■内层函数引用了外层函数的变量(参数),然后返回内层函数的情况,称为闭包(Closure)。
■这些外层空间中被引用的变量叫做这个函数的环境变量。
■环境变量和这个非全局函数一起构成了闭包。
■ 闭包的特点:
■函数会记住外层函数的变量
■ 闭包的实现:

10. 装饰器Decorator
■ 定义:
■以函数作参数并返回一个替换函数的可执行函数
■ 简单理解:
■装饰器的作用就是==为已存在的对象添加额外功能 ==
■为一个函数增加一个装饰(用另一个函数装饰)
■本质上就一个返回函数的高阶函数
■ 应用:
■给函数动态增加功能(函数)
■ 定义与使用:
@是python装饰器的简便写法,也叫语法糖。装饰器语法糖在要被包裹的函数前声明。@后面的函数名,是包裹下边函数的函数名extrafoo。
■ 装饰器在Python使用如此方便都要归因于
Python的函数能像普通的对象一样能作为参数传递给其他函数
可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内。

#现有如下 三个函数
def foo1():
    print ('this is foo1 function')

def foo2():
    print ('this is foo2 function')

def foo3():
    print ('this is foo3 function')
    
#现在想为每一个函数都添加一个打印当前时间的功能
#一种解决办法是一个一个修改,在底部添加代码,但问题是,太麻烦了~
import datetime

def foo1():
    print ('this is foo1 function')
    print(datetime.datetime.now())

def foo2():
    print ('this is foo2 function')
    print(datetime.datetime.now())
    
def foo3():
    print ('this is foo3 function')
    print(datetime.datetime.now())  


#另一种办法是,写一个函数,负责打印时间,然后修改那三个函数
import datetime

def printtime():
    print(datetime.datetime.now())  
    
def foo1():
    print ('this is foo1 function')
    printtime()

def foo2():
    print ('this is foo2 function')
    printtime()
    
def foo3():
    print ('this is foo3 function')
    printtime()

#虽然不那么麻烦了,但还是每个函数都要修改,依旧很麻烦。

#法一:我们可以这样解决
def foo1():
    print ('this is foo1 function')

def foo2():
    print ('this is foo2 function')

def foo3():
    print ('this is foo3 function')

def extrafoo(func):
    print(datetime.datetime.now())
    return func

decorated=extrafoo(foo2)
decorated()

#这样每次调用还是有点麻烦,要先写一个函数接收,再调用。OK,再弄简单一些

#法二:我们可以这样解决: 已有功能foo1,现在想要加计时功能,再写一个函数,利用装饰器。
import datetime
def extrafoo(func):#func代表函数对象
    def inner():
        print('from inner to execute:',func.__name__)
        print('the',func.__name__,'result:',func())#func()代表原始函数
        print('extra:',datetime.datetime.now())#加计时功能,在原始函数后面加计时功能。
    return inner


@extrafoo#装饰器特性,被装饰的函数定义之后立即运行。@extrafoo可以理解成decorated=extrafoo(foo1)以及decorated()
def foo1():
    return 'this is foo1 function--'


#@是python装饰器的简便写法,也叫语法糖
#装饰器语法糖在要被包裹的函数前声明。@后面的函数名,是包裹下边函数的函数名extrafoo
#该语法糖省略了
#decorated=foo(test)
#decorated()


# 装饰器在Python使用如此方便都要归因于
# Python的函数能像普通的对象一样能作为参数传递给其他函数
# 可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内。


foo1()


11. 函数式编程Functional Programming
■ 函数式编程思想:
■函数是第一等公民First Class
■函数式编程是一种编程范式,是如何编写程度的方法论。 把运算过程尽量变成一系列函数的调用。属于结构化编程
■ 特点:
■允许函数作为参数,传入另一个函数
■返回一个函数
■ 理解:
■结合本章知识点,案例进行理解

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!