函数介绍--函数组成部分 函数的作用域 值传递 引用传递 匿名函数 闭包 装饰器 迭代器 生成器 递归 map reduce filter sorted 枚举

1 函数介绍

1.1 概念

 
'''
所谓函数,就是把具有独立功能的代码块组织成为一个小模块,在需要的时候调用
函数的使用包含两个步骤
1.定义函数–封装独立的功能
2.调用函数–享受封装的成果
'''

  

1.2 函数的组成部分

def fn(*args,**kwargs):  
    ....  # 函数体
    return   # 返回值可有可无
# 函数名 :使用函数的依据 如print(函数名())
# 参数 :完成功能需要的的条件信息
# 函数体 :完成功能的代码块
# 返回值 :功能完成反馈的结果 

  

1.3 函数的命名规则

def 函数名(参数们):
         函数的代码
 # def是英文define的缩写
 # 函数名称应该能够表达函数封装代码的功能,方便后续的调用
 # 函数名称的命名应该符合标识符的命名规则

'''
函数名命名规则:
可由字母,下划线和数字组成
不能以数字开头
不能与关键字重名
'''

  

 

1.4函数的参数

函数的参数根部需求 可有可无 

形参  实参

形参:没有实际意义的参数 实参赋予形参后  形参才变得有价值
实参:有实际意义的参数 (值得是在函数调用的时候  传入的参数就是实参)

实参主要分为位置实参(*args),按先后顺序接收传参    关键字实参(**kwargs) ,指名道姓的传参

 
def fen(*args,**kwargs):
    pass

# *args  可以是元祖
# **kwargs 可以是字典

  

值传递 引用传递

'''
不可变对象作为函数参数,相当于C语言的值传递。
可变对象作为函数参数,相当于C语言的引用传递。
...

 

'''
值传递(passl-by-value)过程中,被调函数的形式参数作为被调函数的局部变量处理,
即在堆栈中开辟了内存空间以存放由主调函数放进来的实参的值, 从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。 (被调函数新开辟内存空间存放的是实参的副本值) '''

  

 

def test(x):
    print("test before")
    print(id(x),2222)
    x += 1
    print("test after")
    print(id(x),3333)
    return x


if __name__ == '__main__':
    a = 2
    print(id(a),1111)
    n = test(a)
    print(a)
    print(id(a),4444)
    
    
'''
140722942210336 1111
test before
140722942210336 2222
test after
140722942210368 3333
2
140722942210336 4444
'''

 

'''
引用传递(pass-by-reference)过程中,被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,
但是这时存放的是由主调函数放进来的实参变量的地址。 被调函数对形参的任何操作都被处理成间接寻址,即通过堆栈中存放的地址访问主调函数中的实参变量。
正因为如此,被调函数对形参做的任何操作都影响了主调函数中的 实参变量。(被调函数新开辟内存空间存放的是实参的地址) '''

  

def test(list2):
    print("test before")
    print(id(list2))
    list2[1] = 30
    print("list after")
    print(id(list2))
    return list2



if __name__ == '__main__':
    list1 = ["limei", 25, 'female']
    print(id(list1))
    list3 = test(list1)
    print(list1)
    print(id(list1))
    print(id(list3))

'''
1848503063112
test before
1848503063112
list after
1848503063112
['limei', 30, 'female']
1848503063112
1848503063112
'''

  

1.5 函数的作用域

global  :将局部变量变成全局变量

 
# global  将局部变量变成全局变量
num = 100
def fn1():
    global num
    num = 600
    return num
print(fn1())  # 600

  

nonlocal  :将局部变量变成嵌套变量

 
# nonlocal 将局部变量变成嵌套变量
def outer():
    num = 888
    def inner():
        nonlocal num
        num = 666
        print(num)  # 666
    inner()
    print(num)  # 666
outer()

  

'''
全局变量是在函数外部定义的变量,(没有定义在某一个函数内),所有函数内部都可以使用这个变量
局部变量:在函数内部定义的普通变量,只在函数内部作用,函数执行结束
变量会自动删除(也就是说局部变量是有生命周期的,函数执行完后,就会被内存回收)
'''

  

a = 1
print ('outside:',a,id(a))    #使用id(a)来查看变量在内存中地址,具有惟一性
def fun():
    global a # 声明a为全局变量
    a = 5
    print ('inside:',a,id(a))
fun()
print (a,id(a))
# 输出的结果

'''
outside: 1 140716752138496
inside: 5 140716752138624
5 140716752138624
'''

# 分析:使用了全局变量,那么函数的值在经过函数的计算之后记录新值,
# 也就是说a这个变量之前是记录1这个数的地址,在函数内部重新赋值之后,
# 它就便成5这个数的地址,并且函数执行完后不被系统内存回收。


print('===================================')
a = 1

print ('outside:',a,id(a))    #使用id(a)来查看变量在内存中地址,具有惟一性
def fun():
    # global a # 声明a为全局变量
    a = 5
    print ('inside:',a,id(a))
fun()
print (a,id(a))


#   输出的结果
'''
outside: 1 140716770554112
inside: 5 140716770554240
1 140716770554112
'''



#分析:结合上面的示例,就会发现函数执行完后,
# 局部变量的函数的生命周期也就结束,被系统回收。

  

2 常用函数

匿名函数(lambda)

lambda 参数们(可省略):逻辑表达式(即表达的结果,不可省略),参数的范围(可省略)

  

常用格式

lambda 参数:结果
 
lambda x, y: x*y;函数输入是x和y,输出是它们的积x*y
lambda:None;函数没有输入参数,输出是None
lambda *args: sum(args); 输入是任意个数的参数,输出是它们的和(隐性要求是输入参数必须能够进行加法运算)
lambda **kwargs: 1;输入是任意键值对参数,输出是1

  

# map 映射
l = [1, 2, 3, 4, 5, 6]
print(list(map(lambda x: x + 5, l)))  # 基于for循环  此处l就是参数x 的范围  
# [6, 7, 8, 9, 10, 11]

  

闭包函数

# 通俗解释 就是函数内部调用函数本身

  

def func():
    name = 'python'
    def inner():
        print(name)
    return inner

f = func()  # f = func() = inner 
f()  # f() = inner 
# 输出结果:python

  

装饰器

装饰器的作用: 在不改变原有功能代码的基础上,添加额外的功能,如用户验证等。
@wraps(view_func)的作用: 不改变使用装饰器原有函数的结构(如name, doc)  

基本模板

def outer(func):
    def inner(*args,**kwargs):
        '''执行函数之前要做的'''
        re = func(*args,**kwargs)
        '''执行函数之后要做的'''
        return re
    return inner

@outer  # 使用装饰器
fn():
    pass

  

案例

**案例**

```python
import time
from functools import wraps  
def timer(func):
    @wraps(func) #加在最内层函数正上方  
    def inner(*args,**kwargs):
        start = time.time()
        time.sleep(1)
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer   #==> func1 = timer(func1)
def func1(a,b):
    print('in func1')

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func1('aaaaaa','bbbbbb')
print(func2('aaaaaa'))
```

  

# 结果如下 
in func1
1.015622615814209
in func2 and get a:aaaaaa
1.0153844356536865
fun2 over

  

高阶函数

map

map()函数接收两个参数,一个是函数,一个是Iterable(可迭代的),map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

  

def fn(x):
    return x*X
r = map(fn,[1,2,3])
print(list(r))  # [1 4 9]

  

reduce

...
reduce()把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,
reduce把结果继续和序列的下一个元素做累积计算,
返回一个整形,其效果就是:   reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
...

  

  

from functools import reduce

def fn(x,y):
    return x*10+y
res = reduce(fn,[1,3,5])
print(res)  # 135 
'''
第一次 x = 1  ,y=3 计算结果后为13  因为没有穷尽  此时x=13,y =5  顾结果为135  
'''

  

filter

'''
Python内建的filter()函数用于过滤序列。和map()类似,filter()也接收一个函数和一个序列。
和map()不同的是,filter()把传入的函数依次作用于每个元素,
然后根据返回值是True还是False决定保留还是丢弃该元素,最后返回一个 Iterator
'''

  

  

# 只保留奇数
def is_even(x):
    return x % 2 ==1

res = list(filter(is_even,[1,2,3,4,5,6,7,8,9]))
print(res) # [1 3 5 7 9]

  

sorted

'''
排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小。
如果是数字,我们可以直接比较,但如果是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,
因此,比较的过程必须通过函数抽象出来。Python内置的sorted()函数可以实现对数据按照指定规则排序,
它可以接收一个key函数来实现自定义的排序,还可以接收第三个参数  reverse=True来实现对数据的反向排序。
'''

  

 

res = sorted(['bob', 'about', 'Zoo', 'Credit'])
print(res)
# ['Credit', 'Zoo', 'about', 'bob']
'''
默认情况下,对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面
'''

 

res1 = sorted(['bob', 'about', 'Zoo', 'Credit'],key=str.lower)
print(res1) # ['about', 'bob', 'Credit', 'Zoo']

'''
忽略大小写,按照字母序排序。要实现这个算法,不必对现有代码大加改动,
只要我们能用一个key函数把字符串映射为忽略大小写排序即可。忽略大小写来比较两个字符串,
实际上就是先把字符串都变成大写(或者都变成小写),再比较。
这样,我们给sorted传入key函数,即可实现忽略大小写的排序:
'''

  

res = sorted(['bob', 'about', 'Zoo', 'Credit'],key=str.lower,reverse=True)
print(res)  # ['Zoo', 'Credit', 'bob', 'about']

'''
要进行反向排序,不必改动key函数,可以传入第三个参数 reverse=True:
'''

递归函数

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

递归特点

1、必须有一个明确的结束条件
2、每次进入更深一层递归时,问题规模(计算量)相比上次递归都应有所减少
3、递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,
每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。
由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

  

关于递归还有两个名词,可以概括递归实现的过程

```python
递推:像上边递归实现所拆解,递归每一次都是基于上一次进行下一次的执行,这叫递推
 
回溯:则是在遇到终止条件,则从最后往回返一级一级的把值返回来,这叫回溯
```

  

递推:像上边递归实现所拆解,递归每一次都是基于上一次进行下一次的执行,这叫递推
 
回溯:则是在遇到终止条件,则从最后往回返一级一级的把值返回来,这叫回溯

最大递归深度

python当中一般认为是 998 

案例

# 1、递归求阶乘

# 1!+2!+3!+4!+5!+...+n!
def factorial(n):
    ''' n表示要求的数的阶乘 '''
    if n==1:
        return n # 阶乘为1的时候,结果为1,返回结果并退出
    n = n*factorial(n-1) # n! = n*(n-1)!
    return n  # 返回结果并退出
res = factorial(5) #调用函数,并将返回的结果赋给res
print(res) # 打印结果

  

# 2、递归推斐波那契数列
# 1,1,2,3,5,8,13,21,34,55,试判断数列第十五个数是哪个?
def fabonacci(n):
    ''' n为斐波那契数列 '''
    if n <= 2:
        ''' 数列前两个数都是1 '''
        v = 1
        return v # 返回结果,并结束函数
    v = fabonacci(n-1)+fabonacci(n-2) # 由数据的规律可知,第三个数的结果都是前两个数之和,所以进行递归叠加
    return v  # 返回结果,并结束函数
print(fabonacci(15)) # 610    调用函数并打印结果

  

# 3、二分法找有序列表指定值

data = [1,3,6,13,56,123,345,1024,3223,6688]
def dichotomy(min,max,d,n):
    '''
    min表示有序列表头部索引
    max表示有序列表尾部索引
    d表示有序列表
    n表示需要寻找的元素
    '''
    mid = (min+max)//2
    if mid==0:
        return 'None'
    elif d[mid]<n:
        print('向右侧找!')
        return dichotomy(mid,max,d,n)
    elif d[mid]>n:
        print('向左侧找!')
        return dichotomy(min,mid,d,n)
    else:
        print('找到了%s'%d[mid])
        return
res = dichotomy(0,len(data),data,222)
print(res)

  

3 迭代器

'''
代器不仅可以对序列对象(string、list、tuple)进行迭代,
还可以对不是序列,但表现出序列行为的对象进行迭代,例如字典键、文件的行。

迭代器可以被循环  有 iter() 和__next__()的方法 ,
迭代器也有一些限制,它不能向后移动,不能回到开始,也不能复制一个迭代器。

有__next__()方法的称之为迭代器对象 
迭代器对象依赖__next__()取值,所有条目迭代完后,
迭代器引发一个StopIteration异常告诉程序循环结束。
for语句可用于序列类型,也可以用于迭代器类型,它会在内部调用next()并捕获异常。
'''

  

# iter(obj)工厂函数可以返回一个迭代器,reversed()函数返回一个反序访问的迭代器,
t = (1,2,3,'ccc')

# it = iter(t)
#
# print(it.__next__()) # 1
# print(it.__next__()) # 2
# print(it.__next__()) # 3
# print(it.__next__()) # ccc
# print(it.__next__()) # 出异常 StopIteration

# ik = reversed(t)  # 进行反转
# print(ik.__next__()) # ccc
# print(ik.__next__()) # 3
# print(ik.__next__()) # 2
# print(ik.__next__()) # 1
# print(ik.__next__()) # 出异常 StopIteratio

# iter()方法的另一种使用方式是:iter(func, sentinel),它会重复调用func,直到迭代器的下个值等于sentinel。

a = 1

def foo():
    global a
    print(a)
    a += 1
    return a

it = iter(foo, 11)
for i in it:
    pass
# 上面的例子中,for循环会在i=11的时候停下来。

  

  

with open('text.txt','rb',) as f:
    res = f.__next__()  # 第一行内容
    print(res)
    res = f.__next__() # 第二行内容
    print(res)

  

4 生成器

'''
在函数定义中,如果使用关键字yield语句代替return返回一个值,
则表示定义了一个生成器函数(generator)。生成器函数使用yield语句返回一个值,
然后保存当前函数的整个执行状态,等待下一次调用。生成器函数是一个迭代器,是可迭代对象

优点:
    生成器在调用时 才会去内存开辟空间 而列表是提前开闭空间 且不论是否用得着
    相对而言 生成器更节约内存
'''

  

  

import sys
 
def fibonacci(n): # 生成器函数 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
 
while True:
    try:
        print (next(f), end=" ")
    except StopIteration:
        sys.exit()

# 执行结果  0 1 1 2 3 5 8 13 21 34 55

  

枚举 enumerate

就是给可迭代器对象及迭代器对象添加迭代索引

  

s = 'abn'
for v in enumerate(s):
    print(v)

# 结果
'''
(0, 'a')
(1, 'b')
(2, 'n')
'''
原文地址:https://www.cnblogs.com/wakee/p/10772046.html