07 python函数

函数是结构化编程的核心。

函数可以提高代码的模块化和重复利用率。

1、函数的定义和调用

使用def语句定义函数。

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

示例:

import random
def generate_random():       #定义函数,无参数
    for i in range(10):
        ran = random.randint(1, 20)
        print(ran)

print(generate_random)       #打印函数名:<function generate_random at 0x000001F03C2254C0>
generate_random() #调用函数,函数名后必须有小括号,代表引用函数体。

2、函数的参数

  • 在def语句中,位于函数名后面的变量称为形参,而调用函数时提供的值称为实参

2.1、位置参数

  • 位置参数:实参和形参数量一致,且一一对应。
  • 参数的位置至关重要。
def 函数名(参数1, 参数2, ...):
    函数体

示例:

def add(a, b):          #a和b是形参。形参可以有多个
    sum1 = a + b
    print('a + b =', sum1)

add(1, 3)               #1和3是实参。实参要和形参数量一致,且一一对应
'''
定义一个登录函数,参数是username、password。
判断参数传过来的值是否正确,如果正确就打印登录成功,否则打印登录失败。
'''

def login(username, password):
    name = 'admin'
    pwd = '123456'
    if name == username and pwd == password:
        print('登录成功')
    else:
        print('登陆失败')

login('admin', '123456')
练习:

2.2、关键字参数

  • 关键字参数:使用名称指定的参数。
  • 关键字参数最大的优点在于,可以指定默认值
  • 位置参数和关键字参数可以混合使用,但必须先指定所有的位置参数(位置参数必须在前面),否则解释器将不知道它们是哪个参数(即不知道参数对应的位置)。
  • 通常不应混合使用位置参数和关键字参数
def func(a, b, c=6):  #混合使用位置参数和关键字参数
    print('a = {},  b = {},  c = {}'.format(a, b, c))

func(1, 2)            #结果是:a = 1,  b = 2,  c = 6。位置参数必须传递实参,关键字参数可以使用默认值
func(b=2, a=1)        #结果是:a = 1,  b = 2,  c = 6。关键字参数的位置不重要
func(1, 2, 3)         #结果是:a = 1,  b = 2,  c = 3
func(1, 2, c=3)       #结果是:a = 1,  b = 2,  c = 3。

2.3、收集参数(装包)

  • 有时候要允许用户提供任意数量的参数。
  • 收集参数:定义函数时使用运算符*实现。

1、单星号(*)

  • 单星号收集多余的位置参数,并存放进一个元组中。如果没有多余的位置参数,元组将是一个空元组。
  • 单星号不会收集关键字参数。
def func(a, *b, c):
    print('a = {},  b = {},  c = {}'.format(a, b, c))

func(1, c=2)           #结果是:a = 1,  b = (),  c = 2。要使用关键字参数指定后面的参数
func(1, 2, c=3)        #结果是:a = 1,  b = (2,),  c = 3。
func(1, 2, 3, 4, c=6)  #结果是:a = 1,  b = (2, 3, 4),  c = 6。
# func(1, b=2, c=3)    #报错

2、双星号(**)

  • 双星号仅收集多余的关键字参数,并存放进一个字典中。没有多余的将是一个空字典。
  • 定义函数时,双星号参数变量必须放在最后,即在无星号和单星号参数变量之后。
def func(a, b, **c):
    print('a = {},  b = {},  c = {}'.format(a, b, c))

func(1, b=2)          #结果是:a = 1,  b = 2,  c = {}
func(1, 2, c=3, d=4)  #结果是:a = 1,  b = 2,  c = {'c': 3, 'd': 4}

3、单双星号结合使用

def func(a, b, c=6, *d, **e):
    print('a = {},  b = {},  c = {},  d = {},  e = {}'.format(a, b, c, d, e))

func(1, 2)             #结果是:a = 1,  b = 2,  c = 6,  d = (),  e = {}
func(1, 2, 3)          #结果是:a = 1,  b = 2,  c = 3,  d = (),  e = {}
func(1, 2, c=3, d=4)   #结果是:a = 1,  b = 2,  c = 3,  d = (),  e = {'d': 4}。以但有星号后就与名称无关了,只是用于收集多余的位置参数或关键字参数
func(1, 2, 3, 4, d=4)  #结果是:a = 1,  b = 2,  c = 3,  d = (4,),  e = {'d': 4}。要使*d接收到值,c只能使用位置参数的形式

2.4、分配参数(拆包)

  • 分配参数:调用函数(而不是定义函数)时使用运算符*实现。
  • 如果在定义和调用函数时都使用*或**,将只传递序列或字典。一般不建议这样使用。
  • 只有在定义函数(允许可变数量的参数)或调用函数时(拆分字典或序列)使用,星号才能发挥作用。

1、单星号(*)

  • 实参只能是序列。
def func(a, b, *c):
    print('a = {},  b = {},  c = {}'.format(a, b, c))


hh = [1, 2]
func(*hh)      #结果是:a = 1,  b = 2,  c = ()。传递一个列表,相当于func(1,2)

hh = [1, 2, 3]
func(*hh)      #结果是:a = 1,  b = 2,  c = (3,)。相当于func(1,2,3)

hh = (1,2)
func(*hh)      #结果是:a = 1,  b = 2,  c = ()。传递一个元组,相当于func(1,2)

hh = 'abcd'
func(*hh)      #结果是:a = a,  b = b,  c = ('c', 'd')。传递一个字符串,相当于func('a','b','c','d')

2、双星号(**)

  • 实参只能是字典。
def func(a, b, **c):
    print('a = {},  b = {},  c = {}'.format(a, b, c))

hh = {'a': 1, 'b': 2}
func(**hh)            #结果是:a = 1,  b = 2,  c = {}。相当于func(a=1,b=2)

hh = {'a': 1, 'b': 2, 'c': 3}
func(**hh)            #结果是:a = 1,  b = 2,  c = {'c': 3}。相当于func(a=1,b=2,c=3)

hh = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
func(**hh)            #结果是:a = 1,  b = 2,  c = {'c': 3, 'd': 4}。

3、定义和调用都使用星号

def func(a, *b, **c):
    print('a = {},  b = {},  c = {}'.format(a, b, c))


hh = {'b': 2, 'c': 3, 'd': 4}
gg = [1, 2, 3]

func(*gg)              #结果是:a = 1,  b = (2, 3),  c = {}
# func(**hh)           #报错
func(3, **hh)          #结果是:a = 3,  b = (),  c = {'b': 2, 'c': 3, 'd': 4}
func(*gg, **hh)        #结果是:a = 1,  b = (2, 3),  c = {'b': 2, 'c': 3, 'd': 4}
func(3, 4, *gg, **hh)  #结果是:a = 3,  b = (4, 1, 2, 3),  c = {'b': 2, 'c': 3, 'd': 4}
func(3, 4, gg, **hh)   #结果是:a = 3,  b = (4, [1, 2, 3]),  c = {'b': 2, 'c': 3, 'd': 4}
func(3, 4, *gg, hh)    #结果是:a = 3,  b = (4, 1, 2, 3, {'b': 2, 'c': 3, 'd': 4}),  c = {}

3、函数的返回值

  • 按是否有返回值,函数可分为两类:
    • 有返回值的是真正意义上的函数,称为函数。
    • 没有返回值的函数是不是函数的函数,称为过程式函数。

3.1、有返回值的函数

  • 函数执行特定的操作并通过return返回一个值(可以是任意值,即返回什么由你决定)。
  • return语句非常重要。
    • 一是return语句用于从函数返回值,
    • 二是return语句可以提前结束函数。

1、一个返回值的函数

def add(a, b):
    sum = a + b
    return sum    #返回一个值

hh = add(3, 4)
print(hh)         #结果是:7

2、多个返回值的函数

  • 要返回多个返回值时,它们将被放到一个元组中,并返回这个元组。
def add(a, b):
    sum = a + b
    return sum, a, b, 'jhahfdah'   #返回多个值

hh = add(3, 4)
print(hh)                     #结果是:(7, 3, 4, 'jhahfdah')。

3、return语句结束函数

def add(a, b):
    if a==0:
        return '第一个变量是零'
    sum = a + b
    return sum

hh = add(0, 4)
print(hh)      #结果是:第一个变量是零

3.2、无返回值的函数:过程式函数

  • 在Python中,什么都不返回的函数也是函数,即使它严格来说并非函数。
    • 一是不包含return语句。
    • 二是包含return语句,但没有在return后面指定值。
  • 所有的函数都返回值。如果你没有告诉它们该返回什么,将返回None。
###示例1
def add(a, b):
    sum = a + b

hh = add(0, 4)
print(hh)     #结果是:None

###示例2
def add(a, b):
    sum = a + b
    return

hh = add(0, 4)
print(hh)     #结果是:None

4、变量作用域

4.1、作用域

  • 每一个被声明的变量都存放在一个“看不见”的字典中,内置函数vars可以返回这个不可见的字典。
  • 这种“看不见的字典”称为命名空间作用域
  • LEGB

    • Python有很多名字空间,而 LEGB 则是名字空间的一种查找规则。
    • LEGB 代表名字查找顺序: locals --> enclosing function --> globals --> __builtins__
      • locals 是函数内的名字空间,包括局部变量和形参
      • enclosing 外部嵌套函数的名字空间(闭包中常见)
      • globals 全局变量,函数定义所在模块的名字空间
      • builtins 内置模块的名字空间
>>> x = 1
>>> scope = vars()
>>> scope['x']
1

>>> x = 'maiheng'
>>> scope['x']
'maiheng'
>>> vars()    #返回这个不可见的字典
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'x': 'maiheng', 'scope': {...}}
  • 除全局作用域外,每个函数调用都将创建一个局部作用域。
def func1():
    x = 3
    y = 4
    print('func1的作用域:', vars())
def func2():
    x = 5
    y = 6
    print('func2的作用域:', vars())

x = 1
y = 2
print('全局作用域:', vars())

func1()
func2()

<<<
全局作用域: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001A7030B0910>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/test/python/day/test.py', '__cached__': None, 'func1': <function func1 at 0x000001A7032954C0>, 'func2': <function func2 at 0x000001A7032955E0>, 'x': 1, 'y': 2}
func1的作用域: {'x': 3, 'y': 4}
func2的作用域: {'x': 5, 'y': 6}

4.2、函数使用全局变量和局部变量

  • 全局变量:不是函数中的变量,就是全局变量。
  • 局部变量:在函数内使用的变量称为局部变量。函数的参数类似局部变量。作用范围仅在当前函数中
  • 在函数中使用或修改不可变类型的全局变量(数、字符串、元组)时,必须先用global声明。
  • 在函数中使用或修改可变类型的全局变量(列表、字典、集合和类式)时,不必用global声明
  • 局部变量和全局变量同名时,在函数中局部变量生效。

1、修改不可变类型的变量(数、字符串、元组)

def func():
    global x, y, z    #要修改全局变量,先有global声明
    x += 4
    y += 'ha'
    z += (4, 5, 6)
    print('func的作用域:', vars())
    print('x = {},  y = {},  z={}'.format(x, y, z))

x = 1
y = 'heng'
z = (1, 2, 3)
print('全局作用域:', vars())
print('x = {},  y = {},  z={}'.format(x, y, z))

func()

print('全局作用域:', vars())
print('x = {},  y = {},  z={}'.format(x, y, z))

<<<
全局作用域: {..., 'x': 1, 'y': 'heng', 'z': (1, 2, 3)}  #全局作用域略写了
x = 1,  y = heng,  z=(1, 2, 3)

func的作用域: {}
x = 5,  y = hengha,  z=(1, 2, 3, 4, 5, 6)

全局作用域: {..., 'x': 5, 'y': 'hengha', 'z': (1, 2, 3, 4, 5, 6)}
x = 5,  y = hengha,  z=(1, 2, 3, 4, 5, 6)

2、修改可变类型的变量(列表、字典、集合和类式)

def func():
    a[2] = 10
    b['y'] = 10
    print('func的作用域:', vars())
    print('a = {},  b = {}'.format(a, b))


a = [1, 2, 3]
b = {'x': 1, 'y': 2, 'z': 3}
print('全局作用域:', vars())
print('a = {},  b = {}'.format(a, b))

func()

print('全局作用域:', vars())
print('a = {},  b = {}'.format(a, b))

<<<
全局作用域: {..., 'a': [1, 2, 3], 'b': {'x': 1, 'y': 2, 'z': 3}}
a = [1, 2, 3],  b = {'x': 1, 'y': 2, 'z': 3}

func的作用域: {}
a = [1, 2, 10],  b = {'x': 1, 'y': 10, 'z': 3}

全局作用域: {..., 'a': [1, 2, 10], 'b': {'x': 1, 'y': 10, 'z': 3}}
a = [1, 2, 10],  b = {'x': 1, 'y': 10, 'z': 3}

3、局部变量遮住全局变量

def func():
    x = 4    #局部变量和全局变量同名时,在函数中局部变量生效。函数中没有声明变量y,全局变量y生效
    z = (4, 5, 6)
    print('func的作用域:', vars())
    print('x = {},  y = {},  z={}'.format(x, y, z))

x = 1
y = 'heng'
z = [1, 2, 3]
print('全局作用域:', vars())
print('x = {},  y = {},  z={}'.format(x, y, z))

func()

<<<
全局作用域: {..., 'x': 1, 'y': 'heng', 'z': [1, 2, 3]}
x = 1,  y = heng,  z=(1, 2, 3)

func的作用域: {'x': 4, 'z': (4, 5, 6)}
x = 4,  y = heng,  z=(4, 5, 6)

4、解决遮住问题

  • 必要时可以在函数中使用“globals()['全局变量名']”来访问全局变量。
  • globals()内置函数,查看全局变量,以字典的形式输出(包含一些系统内置的变量)
  • locals()内置函数,查看本地变量,以字典的形式输出。
def combine(hh2):
    hh1 = 'hh3 '    #局部变量hh1,形参hh2
    print(globals()['hh1'] + hh1 + globals()['hh2'] + hh2)

hh1 = 'hh1-'
hh2 = 'hh2-'
combine('hh4')

5、内部函数(套嵌函数)

  • 内部函数:在函数中声明的函数。
  • 内部函数可以访问外部函数的变量。
  • 内部函数修改外部函数可变类型的变量,不用nonlocal提前声明。
  • 内部函数修改外部函数不可变类型的变量,要用nonloca提前声明。
  • 内部函数修改全局变量可变类型的变量,不用global提前声明。
  • 内部函数修改全局变量不可变类型的变量,要用global提前声明。
def func():
    n = 100                      #声明一个不可变类型的变量
    list1 = [9, 1, 7, 3, 8]      #声明一个可变类型的变量
    def in_func():               #声明内部函数
        nonlocal n               #修改外部不可变类型的变量要用nonlocal提前声明,
        global a                 #修改全局不可变类型的变量要用global提前声明
        for index, i in enumerate(list1):
            list1[index] = i + n #修改外部可变
        for index, i in enumerate(b):
            b[index] = i + n     #修改全局可变
        n += 11
        a += 11
    in_func()                    #调用内部函数
    print('list1是:{},  n是:{},  b是:{},  a是:{}'.format(list1, n, b, a))

a = 10                           #声明一个全局不可变类型的变量
b = [1, 7, 2, 9, 4]              #声明一个全局可变类型的变量
func()                           #结果是:list1是:[109, 101, 107, 103, 108],  n是:111,  b是:[101, 107, 102, 109, 104],  a是:21

6、闭包

  • 闭包:是个嵌套函数(在外部函数中声明一个内部函数,内部函数中引用外部函数的变量,且外部函数的返回值是内部函数名
  • 闭包是特殊的内部函数
def func(a):
    def in_func(b):
        print('a是:{},  b是:{}'.format(a, b))
    print(locals())
    return in_func

x = func(10)        #结果是:{'in_func': <function func.<locals>.in_func at 0x00000243C5B755E0>, 'a': 10}。将内部函数的内存地址引用赋值给x
print(x)            #结果是:<function func.<locals>.in_func at 0x00000243C5B755E0>
x(20)               #结果是:a是:10,  b是:20
  • 闭包的作用之一,保存参数的状态。
def func(a, b):
    def in_func(c):
        sum = a + b + c
        print('{} + {} + {} = {}'.format(a, b, c, sum))
    print(locals())
    return in_func

x1 = func(10, 20)    #将外部函数的返回值--内部函数地址赋值给x1。 结果是:{'in_func': <function func.<locals>.in_func at 0x00000232BB5755E0>, 'a': 10, 'b': 20}。
x2 = func(100, 200)  #每次引用内部函数都会开辟新的内存空间。     结果是:{'in_func': <function func.<locals>.in_func at 0x00000232BB575550>, 'a': 100, 'b': 200}
x2(300)              #结果是:100 + 200 + 300 = 600
x1(30)               #结果是:10 + 20 + 30 = 60

 示例:闭包之计数器

def generate_count():
    container = [0]
    def add_one():
        container[0] = container[0] + 1
        print('当前是第{}访问'.format(container[0]))
    return add_one

counter = generate_count()
counter()              #结果是:当前是第1访问
counter()              #结果是:当前是第2访问
counter()              #结果是:当前是第3访问

示例:闭包之同级调用、内部函数的返回值

def func():
    a = 100
    def in_func1():
        b = 200
        sum = a + b
        print('a + b =',sum)
    def in_func2():
        in_func1()
        return '我是第二个内部函数'  #内部函数的返回值
    return in_func2                 #外部函数的返回值

x = func()                          #将内部函数in_func2的内存引用赋值给x
y = x()                             #将内部函数in_func2的返回值赋值给y。  结果是:a + b = 300
print(y)                            #                                   结果是:我是第二个内部函数  

7、装饰器

  • 装饰器:拥有闭包的特点,且只接受函数为参数。
    • 装饰器是闭包,且将被装饰的函数当作实参。
    • 在内部函数中调用以参数传进来的被装饰函数。
  • 装饰器不改变原函数,也不改变原函数的调用方式,就可以给原函数添加新功能。
  • 装饰器是特殊的闭包。
  • 装饰器是python2.4引入的新语法,装饰器可用于包装任何可调用的对象,并且可用于函数和方法(类中的函数)。

7.1、装饰器的定义和使用

###定义装饰器
def 装饰器名(func):    #第一层函数用于接收被装饰的函数
    ...
    def 内部函数名():  #第二层函数用于接收被装饰函数的参数
        ...
        func()
    return 内部函数名

###使用装饰器
@装饰器名             #在程序执行到“@装饰器”和“def 被装饰函数名():”时,python解释器会自动在后台做:
def 被装饰函数名():   #1、将“被装饰函数名”当参数传递给装饰器,并调用(执行)装饰器。
    ...              #2、将装饰器的返回值(内部函数引用的内存地址)赋值给“被装饰函数名”

###调用被装饰函数
被装饰函数名()         #此时“被装饰函数名”引用的内存地址是装饰器内部函数的,而不是它自己的了

示例:

def decorate(func):         #声明装饰器
    def in_decorate():
        print("八戒在吃自己的人参果...")
        print('八戒向猴哥讨要人参果,猴哥掂这八戒的大耳朵说“呆子,滚...”')
        func()
        print(func)         #打印被装饰函数自己引用的内存地址:<function bajie at 0x0000029317E55550>
    return in_decorate

@decorate                   #使用装饰器
def bajie():                #声明被装饰函数
    print('八戒伤心的哭了...')

bajie()                     #调用被装饰函数
print(bajie)                #打印被装饰后的被装饰函数引用的内存地址:<function decorate.<locals>.in_decorate at 0x0000029317E55670>

<<<
八戒在吃自己的人参果...
八戒向猴哥讨要人参果,猴哥掂这八戒的大耳朵说“呆子,滚...”
八戒伤心的哭了...
<function bajie at 0x0000029317E55550>
<function decorate.<locals>.in_decorate at 0x0000029317E55670> 
def decorate(func):
    def in_decorate():
        print("八戒在吃自己的人参果...")
        print('八戒向猴哥讨要人参果,猴哥掂这八戒的大耳朵说“呆子,滚...”')
        func()
    return in_decorate

@decorate
def bajie():
    print('八戒伤心的哭了...')

bajie()

<<<
八戒在吃自己的人参果...
八戒向猴哥讨要人参果,猴哥掂这八戒的大耳朵说“呆子,滚...”
八戒伤心的哭了...
View Code

7.2、被装饰函数的参数

1、参数传递的两步

  • 调用被装饰函数时,此时“被装饰函数名”引用的内存地址是装饰器内部函数的,即此时的"被装饰函数名"就是装饰器内部函数,因此参数会直接传递给装饰器内部函数。
  • 装饰器内部函数得到此参数后,再传递给在其内部的原被装饰函数
def decorate(func):
    def in_decorate(x):    #内部函数又将参数传递给原被装饰函数
        print("八戒在吃自己的人参果...")
        print('八戒向猴哥讨要人参果,猴哥掂这八戒的大耳朵说“呆子,滚...”')
        func(x)

    return in_decorate

@decorate
def bajie1(s):
    print('八戒伤心的哭{}天了...'.format(s))

@decorate
def bajie2(lst):
    print('八戒伤心的哭了...')
    for i in lst:
        print('八戒喊来了%s,要他替自己做主。' % i)

n = 9
bajie1(n)                  #传递一个整型作为参数。此时将参数传递给装饰器内部函数

list1 = ['师傅', '观音', '如来']
bajie2(list1)              #传递一个列表作为参数

<<<
八戒在吃自己的人参果...
八戒向猴哥讨要人参果,猴哥掂这八戒的大耳朵说“呆子,滚...”
八戒伤心的哭9天了...

八戒在吃自己的人参果...
八戒向猴哥讨要人参果,猴哥掂这八戒的大耳朵说“呆子,滚...”
八戒伤心的哭了...
八戒喊来了师傅,要他替自己做主。
八戒喊来了观音,要他替自己做主。
八戒喊来了如来,要他替自己做主。

2、万能装饰器

  • 只要是装饰器,一般都是这种万能装饰器。
def decorate(func):
    def in_decorate(*args, **kwargs):    #装包,必须有*args和**kwargs
        print("八戒在吃自己的人参果...")
        print('八戒向猴哥讨要人参果,猴哥掂这八戒的大耳朵说“呆子,滚...”')
        func(*args, **kwargs)            #拆包,必须有*args和**kwargs
    return in_decorate

@decorate
def bajie1():
    print('八戒伤心的哭了...')

@decorate
def bajie2(s):
    print('八戒伤心的哭{}天了...'.format(s))

@decorate
def bajie3(lst, t=99):
    print('八戒伤心的哭%d了...' % t)
    for i in lst:
        print('八戒喊来了%s,要他替自己做主。' % i)

bajie1()                 #调用时,使用无参数

n = 9
bajie2(n)                #调用时,使用单个参数

list1 = ['师傅', '观音', '如来']
bajie3(list1, t=33)       #调用时,使用关键字参数

<<<
八戒在吃自己的人参果...
八戒向猴哥讨要人参果,猴哥掂这八戒的大耳朵说“呆子,滚...”
八戒伤心的哭了...

八戒在吃自己的人参果...
八戒向猴哥讨要人参果,猴哥掂这八戒的大耳朵说“呆子,滚...”
八戒伤心的哭9天了...

八戒在吃自己的人参果...
八戒向猴哥讨要人参果,猴哥掂这八戒的大耳朵说“呆子,滚...”
八戒伤心的哭33了...
八戒喊来了师傅,要他替自己做主。
八戒喊来了观音,要他替自己做主。
八戒喊来了如来,要他替自己做主。

7.3、多层装饰器

  • 当一个被装饰函数有多个装饰器时,先使用离得最近的装饰器,然后依次使用较近的装饰器。
def zhuang1(func):   #声明第一个装饰器
    print('--------> start1')
    def in_1():
        func()                                #2、这个func()就是zhuang()
        print('--------> 铺地板')
    print('--------> stop1')
    return in_1

def zhuang2(func):   #声明第二个装饰器
    print('--------> start2')
    def in_2():
        func()                                #4、这个func()就是ni_1
        print('--------> 买家具')

    print('--------> stop2')
    return in_2

@zhuang2             #离得较远的最后使用。     #3、将zhuang(此时的zhuang是in_1)作为参数传递给zhuang2,并将in_2赋值给zhuang
@zhuang1             #先使用离得最近的装饰器。  #1、将zhuang作为参数传递给zhuang1,并将in_1赋值给zhuang
def zhuang():
    print('--------> 毛胚房')

zhuang()                                      #5、调用zhuang,此时的zhuang是in_2

<<<
--------> start1
--------> stop1

--------> start2
--------> stop2

--------> 毛胚房
--------> 铺地板
--------> 买家具

7.4、装饰器的参数

  • 装饰器如果要带参数,装饰器外面就要再嵌套一层函数,用于接收装饰器的参数,即一共有三层函数。

1、不带参数的装饰器

def banzhuan(func):                        #第一层函数用于接收被装饰的函数
    def in_banzhuan(*args, **kwargs):      #第一层函数用于接收被装饰函数的参数
        func(*args, **kwargs)
        print('现在已经在铺设地板啦。。。')
    return in_banzhuan

@banzhuan
def maopei(time):
    print('{}买了这个毛胚房'.format(time))

maopei('2021-02-17')

<<<
2021-02-17买了这个毛胚房
现在已经在铺设地板啦。。。

2、带参数的装饰器

def canshu(a):                              #第一层函数用于接收装饰器的参数
    def banzhuan(func):                     #第二层函数用于接收被装饰的函数
        def in_banzhuan(*args, **kwargs):   #第三层函数用于接收被装饰函数的参数
            func(*args, **kwargs)
            print('已经铺设%d地板啦。。。' % a)
        return in_banzhuan                  #在第二层函数中返回第三层的函数名
    return banzhuan                         #在第一层函数中返回第二层的函数名

@canshu(100)                                #使用装饰器时,用的是第一层的函数名
def maopei(time):
    print('{}买了这个毛胚房'.format(time))

@canshu(10000)
def road():
    print('这是一条新修的小路')


maopei('2021-02-17')
road()

<<<
2021-02-17买了这个毛胚房
已经铺设100地板啦。。。

这是一条新修的小路
已经铺设10000地板啦。。。

8、匿名函数

  • 匿名函数简化函数的声明。
  • 有些函数在代码中只用一次,且函数体比较简单,使用匿名函数可以减少代码量。

8.1、声明匿名函数

###使用关键字lambda声明匿名函数

函数名 = lambda 参数1,参数2...:返回值        #返回值是表达式

示例1:

add = lambda x, y: x + y  #声明匿名函数

s = add(3, 5)             #调用匿名函数
print(s)                  #结果是:8

示例2:三元运算

calc = lambda x, y: x * y if x > y else x / y

print(calc(2, 5))         #结果是:0.4
print(calc(5, 2))         #结果是:10

8.2、匿名函数的使用场景

  • 匿名函数主要和内置函数联合使用

1、map(function, sequence, ...) 

  • 内置函数map()有两个参数,一个是函数func,一个是序列,map将序列中的每个元素依次传入函数func中,并将结果返回到新的序列中。

示例1:

# 一般函数
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for index, val in enumerate(list1):
    list1[index] = val * val

print(list1)         #结果是:[1, 4, 9, 16, 25, 36, 49, 64, 81]

# 匿名函数
list2 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
list2 = (map(lambda x: x * x, list2))

print(list(list2))   #结果是:[1, 4, 9, 16, 25, 36, 49, 64, 81]

示例2:

list1 = [1, 3, 2, 6, 3, 8, 9, 5, 0]
result = map(lambda x: x if x % 2 == 0 else x + 1, list1)

print(list(result))   #结果是:[2, 4, 2, 6, 4, 8, 10, 6, 0]

2、max(*args, key=None)

  • 内置函数max(),返回最大值。

示例:

###离散值
max1 = max(3, 6, 1, 10, 44, 33)
print(max1)      #结果是:44

###序列
list1 = [3, 6, 1, 10, 44, 33]
max2 = max(list1)
print(max2)      #结果是:44

###字典,比较复杂的数据需要指定以那个字段作比较
dict1 = [{'name': 'mai1', 'age': 55}, {'name': 'mai2', 'age': 33}, {'name': 'mai3', 'age': 77}, {'name': 'mai3', 'age': 11}, {'name': 'mai5', 'age': 22}]
max3 = max(dict1,key=lambda x:x['age'])
print(max3)      #结果是:{'name': 'mai3', 'age': 77}

3、filter(function, sequence) 

  • 内置函数filter()有两个参数,一个是函数func,一个是序列,filter将序列中的每个元素依次传入函数func中,并根据返回结果决定是否该元素放进新的列表中。
###列表
list1 = [22, 55, 33, 1, 4, 22, 7, 99]
list2 = filter(lambda x: x > 10, list1)
print(list(list2))     #结果是:[22, 55, 33, 22, 99]

###字典
dict1 = [{'name': 'mai1', 'age': 55}, {'name': 'mai2', 'age': 33}, {'name': 'mai3', 'age': 77}, {'name': 'mai3', 'age': 11}, {'name': 'mai5', 'age': 22}]
dict2 = filter(lambda x: x['age'] > 22, dict1)
print(list(dict2))     #结果是:[{'name': 'mai1', 'age': 55}, {'name': 'mai2', 'age': 33}, {'name': 'mai3', 'age': 77}]

4、sorted(iterable[, cmp][, key][, reverse])

  • 返回一个排序后的列表,其中的元素来自iterable(可迭代对象)。可选参数与列表的方法sort相同
###列表
list1 = [22, 55, 33, 1, 4, 22, 7, 99]
list2 = sorted(list1)
print(list(list2))   #结果是:[1, 4, 7, 22, 22, 33, 55, 99]

###字典
dict1 = [{'name': 'mai1', 'age': 55}, {'name': 'mai2', 'age': 33}, {'name': 'mai3', 'age': 77}, {'name': 'mai3', 'age': 11}, {'name': 'mai5', 'age': 22}]
dict2 = sorted(dict1, key=lambda x: x['age'])
print(dict2)         #结果是:[{'name': 'mai3', 'age': 11}, {'name': 'mai5', 'age': 22}, {'name': 'mai2', 'age': 33}, {'name': 'mai1', 'age': 55}, {'name': 'mai3', 'age': 77}]

5、reduce函数

  • reduce(func, seq[, initial])等价于func(func(func(seq[0], seq[1]), seq[2]), ...)
from functools import reduce

tuple1 = (1, 4, 2, 5, 9, 3)
tuple2 = reduce(lambda x, y: x + y, tuple1)
print(tuple2)         #结果是:24

9、递归函数

  • 在函数内部,可以调用其他函数。如果一个函数在内部调用自己,这个函数就是递归函数。
  • 基线条件(针对最小的问题):满足这种条件时函数将直接返回一个值。(必须有一个结束条件
  • 递归条件:包含一个或多个调用,这些调用旨在解决问题的一部分。

示例1:求n以内的正整数之和

def add(n):
    if n == 0:
        return 0
    else:
        return n + add(n - 1)

sum = add(100)
print(sum)         #结果是:5050

示例2、求n以内的正整数的阶乘

def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n - 1)

ss =factorial(10)
print(ss)          #结果是:3628800

示例3:求x的n次幂

def power(x, n):
    if n == 0:
        return 1
    else:
        return x * power(x, n - 1)

ss = power(2, 10)
print(ss)          #结果是:1024

 

posted @ 2021-02-14 22:02  麦恒  阅读(57)  评论(0编辑  收藏  举报