函数部分总结

一、自定义函数

  一、函数概念

    1、什么是函数:函数就是封装一个功能。(程序中如果没有函数的引用,会产生重复代码多,可读性差的特点。)

    2、函数的定义:def 关键词开头,空格之后接函数名称和圆括号()

l1 = [1,2,3,4]
def my_len():
    # def 关键字 定义一个函数
    # my_len 函数名书写规则与变量一样
    # def与函数名中间一个空格
    # 函数名():加上冒号
    # 函数体
    count = 0
    for j in l1:
        count += 1
    print(count)
my_len()
l1 = [1,2,3,4]
def my_len():
    count = 0
    for j in l1:
        count += 1
    return count
print(my_len())
# 函数的返回值:
# 写函数,不要在函数中写print()
# return
# 1、在函数中,遇到return结束函数。
# 2、将返回值给函数的调用者。
#   无 return = return None
#   return 1个值 该值是什么,就直接返回给函数的调用者,函数名()
#   return 多个值,将多个值放到一个元组中,返回给函数的调用者。

  二、函数的传参   

    函数分为形参和实参:

    形参:形式参数,不代表实际的变量。

    实参:实际参数,带入形参中的变量。

    形参传参:1、位置传参。按顺序,一一对应。

            2、默认参数。传参则覆盖,不传则默认,默认参数永远在位置参数的后面。

    形参传参:1、位置传参。按顺序,一一对应。

           2、关键字传参,不按顺序,一一对应。

           3、混合传参,关键字参数永远在位置参数的后面。

    形参传递顺序:位置参数,*args,默认参数,**kwargs

def test(a, *args, **kwargs):
    print(a)    # 1
    print(args) # (2, 3)
    print(kwargs)   # {'e': 5, 'd': '4'}
test(1, 2, 3, d='4', e=5)
 
# 1还是参数a的值,args表示剩余的值,kwargs在args之后表示成对键值对。
# * 魔法运用
def func(*args):
    print(args)
l1 = [1,2,30]
l2 = [1,2,33,21,45,66]
tu = (1,2,3)
func(1,2,30,1,2,33,21,45,66)    # (1, 2, 30, 1, 2, 33, 21, 45, 66)
func(*'qweqrfdsaf') # ('q', 'w', 'e', 'q', 'r', 'f', 'd', 's', 'a', 'f')
func(*{'name':'alex',"age":12}) # ('name', 'age')
func(*l1,*l2)   # (1, 2, 30, 1, 2, 33, 21, 45, 66)
def func(*args):
    print(args)
func(1,2,3,10,20,80)    # (1, 2, 3, 10, 20, 80)
def func(**kwargs):
    print(kwargs)
dic1 = {'name1':'alex','age1':46}
dic2 = {'name':'老男孩','age':56}
func(**dic1,**dic2) # {'age1': 46, 'name1': 'alex', 'name': '老男孩', 'age': 56}
# 在函数的调用执行时,
#   *可迭代对象,代表打散(list,tuple,str,dict(键))将元素一一添加到args。
#  **字典,代表打散,将所有键值对放到一个kwargs字典里。

# 在函数定义时, *args,**kwargs代表的是聚合。
def func(*args,**kwargs):
    print(args)
    print(kwargs)
dic1 = {'name1':'alex','age1':46}
dic2 = {'name':'老男孩','age':56}
func(*[1,2,3,4],*'asdfsad',**dic1,**dic2)
# (1, 2, 3, 4, 'a', 's', 'd', 'f', 's', 'a', 'd')
# {'name': '老男孩', 'name1': 'alex', 'age': 56, 'age1': 46}

  三、函数有用信息

def func1():
    """
    此函数是完成登陆的功能,参数分别是...作用。
    :return: 返回值是登陆成功与否(True,False)
    """
    print(666)
    return True
func1() # 666
print(func1.__name__)   # func1
print(func1.__doc__)    # 显示注释内容

二、命名空间和作用域

  一、命名空间

    命名空间的本质:存放名字与值的绑定关系   

    命名空间一共分为三种:

      全局命名空间:代码在运行伊始,创建的存储“变量名与值的关系”的空间

      局部命名空间:在函数的运行中开辟的临时的空间

      内置命名空间:存放了python解释器

 

    三种命名空间之间的加载与取值顺序:

    加载顺序:内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)

    取值:在局部调用:局部命名空间->全局命名空间->内置命名空间

    在全局调用:全局命名空间->内置命名空间

  二、作用域

    作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域。

    全局作用域(globals):包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效

    局部作用域(locals):局部名称空间,只能在局部范围内生效

    global关键字

#global
# 1,在局部空间内,声明一个全局变量
def func1():
    global name
    name = '老男孩'
    print(name) # 老男孩
func1()
print(name) # 老男孩
# 2,在局部空间内改变一个全局变量
a = 4
def func1():
    global a    # 5
    a = 5
func1()
print(a)

    nolocal关键字

#nonlocal
# 1,不能修改全局变量。
#在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,
# 并且引用的哪层,从那层及以下此变量全部发生改变。
a = 4
def func1():
    b = 6
    def func2():
        b = 666
        print(b) # 666
    func2()
    print(b) # 6
func1()

b = 4
def func1():
    b = 6
    def func2():
        nonlocal b
        b = 666
        print(b) # 666
    func2()
    print(b) # 666
print(b)    # 4
func1()

  三、函数名的应用

# 函数名是函数的名字,本质:变量,特殊的变量。
# 1,单独打印函数名  <function func1 at 0x0000000000872378>
def func1():
    print(666)
print(func1)  # <function func1 at 0x0000000000872378>
a = 6
print(a)    # 6
# 2、函数名的赋值
def func2():
    print(666)

f = func2
f() # 666
# 3、函数名可以作为容器类数据的元素
def f1():
    print(1211)
def f2():
    print(1222)
def f3():
    print(1233)
def f4():
    print(1233)
l1 = [f1, f2, f3, f4]
for i in l1:
    i()
# 4、函数名可以作为参数
a = 1
def f1(x):
    print(x)    # 1
f1(a)
def f1():
    print(666)
def f2(x):  # x = f1
    x()  # f1()
f2(f1)
# 5、函数名可以作为函数的返回值
def wraaper():
    def inner():
        print(666)
    return inner
ret = wraaper()  # inner
ret()  # inner()

三、闭包和装饰器

  一、闭包

    闭包:就是内层函数对外层函数(非全局)变量的引用。

    闭包:当函数开始执行时,如果遇到闭包,他又一个机制,他会永远开辟一个内存空间,将闭包中的额变量等值放入其中,不会随着函数的执行完毕而消失。

    判断是不是闭包:内层函数名.__closure__ 结果是:cell...就是闭包

name = '老男孩'
def wraaper2(n):
    #  n = '老男孩'
    def inner():
        print(n)    # 老男孩
    inner()
    print(inner.__closure__)    # (<cell at 0x000001E3F9123B28: str object at 0x000001E3F9010F30>,)
wraaper2(name)

  二、装饰器

    开放封闭原则:

    1、对扩展是开放的

    2、对修改是封闭的

    装饰器主要功能和装饰器固定结构:在不改变函数调用方式的基础上在函数的前、后添加功能。

    装饰器固定格式

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

    带参数的装饰器

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

def func():
    print(111)

func()

    多个装饰器装饰同一个函数

def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')  # 第二步
        func()
        print('wrapper1 ,after func')   # 第四步
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')  # 第一步
        func()
        print('wrapper2 ,after func')   # 第五步
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')   # 第三步
f()

四、迭代器和生成器

  一、迭代器

    可迭代协议:可以被迭代要满足的要求,可以将某个数据集内的数据“一个挨着一个的取出来”,就叫做迭代,内部实现了__iter__方法

    迭代协议:必须拥有__iter__方法和__next__方法

    迭代器的好处:

      1、节省内存空间。

      2、满足惰性机制。

      3、不能反复取值,不可逆。

     可迭代计算过程:

      1、将可迭代对象转化成迭代器

      2、内部使用__next__方法取值

      3、运用了异常处理去处理报错。    

l2 = [1, 2, 3, 4, 5, 6, 7, 8]
l2_obj = l2.__iter__()
while True:
    try:
        i = l2_obj.__next__()
        print(i)
    except Exception:
        break

  二、生成器

    生成器:生成器本质上是迭代器。

    初始生成器:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行

    生成器函数定义:

      1、一个包含yield关键字的函数就是一个生成器函数

      2、next 和send 功能一样,都是执行一次,send获取下一个值可以给上一个yield赋值。

      使用send的注意事项:第一次使用生成器的时候,使用next获取下一个值,最后一个yield不能接受外部的值

def generator():
    print(123)  # 123
    content = yield 1
    print(content)  # hello
    print(456)  #456
    yield 2
g = generator()
g.__next__()
g.send('hello')

  三、列表推导式和生成器表达式

    1、列表推导式:用列表推导式能够构建的任何列表,用别的都可以构建,一目了然,占内存。

l2 = [i*i for i in range(1,11)]
print(l2)   # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

    2、生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表,不易看出,节省内存。

l_obj = ('python%s期' % i for i in range(1,12))
print(l_obj)    # <generator object <genexpr> at 0x000001AAF0AEBDB0>
print(l_obj.__next__()) # python1期
print(l_obj.__next__()) # python2期
print(l_obj.__next__()) # python3期

    总结:

      1.把列表解析的[]换成()得到的就是生成器表达式

      2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存

      3.Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可 以直接这样计算一系列值的和:

五、内置函数

    3.x版本python共提供68种内置函数

 

详细内容见http://www.cnblogs.com/qiujie/p/8982773.html,内置函数思维导图:https://www.processon.com/view/link/5ae949b8e4b09b1bf63730c4

六、匿名函数

    匿名函数:为了解决那些功能很简单的需求而设计的一句话函数。

def calc(n):
    return n*n
print(calc(10))

cal = lambda n:n*n
print(cal(10))

上面是我们对calc这个匿名函数的分析,下面给出了一个关于匿名函数格式的说明

函数名 = lambda 参数 :返回值

#参数可以有多个,用逗号隔开
#匿名函数不管逻辑多复杂,只能写一行,且逻辑执行结束后的内容就是返回值
#返回值和正常的函数一样可以是任意数据类型

我们可以看出,匿名函数并不是真的不能有名字。

  匿名函数的调用和正常的调用也没有什么分别。 就是 函数名(参数) 就可以了~~~

  匿名函数与内置函数举例:

l=[3,2,100,999,213,1111,31121,333]
print(max(l))

dic={'k1':10,'k2':100,'k3':30}
print(max(dic))
print(dic[max(dic,key=lambda k:dic[k])])

# 31121
# k3
# 100

res = map(lambda x:x**2,[1,5,7,4,8])
for i in res:
    print(i)

# 1
# 25
# 49
# 16
# 64

res = filter(lambda x:x>10,[5,8,11,9,15])
for i in res:
    print(i)
    
# 11
# 15

七、递归函数

    递归:一个函数在内部调用自己的函数称为递归,递归的次数在python是有限制的,默认递归次数是997次。

# 输出斐波那契第n个数
def fib(n):
    if n==1 or n==2:
        return 1
    return fib(n-1)+fib(n-2)

函数部分思维导图:https://www.processon.com/view/link/5ae94a2be4b09b1bf637320e

posted @ 2018-05-01 23:02  小杰~~  阅读(284)  评论(0编辑  收藏  举报