• 函数是一个组织好的 ,可以重复使用的代码段 ,函数可以提高代码的重复利用率
  • 函数封装的目的为了复用,减少冗余代码
  • python有许多的内置函数可供使用
  • 你也可以自己定义函数,这通常被称之为自定义函数
  • 函数是可调用对象

1. 函数的定义

  1. def 函数名(参数列表):
  2. 函数体(代码块)
  3. [return返回值]
  4. 函数名就是标识符,命名要求一样
  5. 语句块必须缩进,约定4个空格
  6. Python的函数没有Return语句,隐式会返回一个None
  7. 定义中的参数列表成为形式参数,简称是形参
  8. 函数定义,只是一个声明,不会执行,需要调用
  9. 调用时写的参数是实际参数,简称是实参

先看几个示例

  1. def hello_word():
  2. pass
  3. def print_diamond(count):
  4. pass
  5. def get_max(a, b, c):
  6. pass

关于上面这段代码,你要记住下面3个结论

  • 函数可以没有参数
  • 函数的参数可以是一个,也可以是多个
  • pass的作用相当于占位符,没有实际作用

    2. 函数的调用

示例1, 定义并调用没有参数的函数

  1. def hello_word():
  2. print('hello world')
  3. hello_word()

示例2, 定义并调用有一个参数的函数

  1. def print_diamond(count):
  2. """
  3. 输出菱形
  4. """
  5. for i in range(count):
  6. if i <= count//2:
  7. print(" "*(count//2-i) + "*"*(2*i + 1))
  8. else:
  9. print(" "*(i - count//2) + "*"*((count-i)*2-1))
  10. print_diamond(11)

示例3, 定义并调用有三个参数的函数

  1. def get_max(a, b, c):
  2. max_number = a
  3. if b > max_number:
  4. max_number = b
  5. if c > max_number:
  6. max_number = c
  7. return max_number
  8. max_num = get_max(22, 23, 21)
  9. print(max_num)

3、函数参数

  • 函数调用,将实参的值赋值给形参
  • 形参本身没有实际值,在函数调用时,传入什么实参,形参就装有什么意义
  • 形参:在函数定义时,相当于变量名, 实参: 在函数调用阶段传入的值称为实际参数,简称实参,相当于变量名
  • 在调用阶段,实参(变量值)会赋值给形参(变量名)
  • 实参与形参的绑定关系只能在函数体内使用,函数调用时生效

3.1)形参和实参的具体使用

  • 位置参数

    1. def f(x,y,z):
    2. sum = x+y+z
    3. print(sum)
    4. f(1,2,3) #调用

    按照参数定义顺序传入实参 (x=1,y=2,z=3)
    位置实参: 按位置先后进行传参 (按照从左到右的顺序依次定义的参数)

    A: 位置形参: 按从左到右的顺序直接定义的变量名 (函数定义阶段)
    def func(x,y):
    print(x,y)
    特点: 必须被传值

    B: 位置实参: 函数调用传入的值 (在函数调用阶段)
    func(2,3)
    func(y=2,x=5)

  • 关键字参数

指名道姓的进行传参,可以颠倒位置传参,名字指向谁,就是谁

注意: 位置实参需要出现在关键字实参之前

特点: 指名道姓的进行传参,可以颠倒位置传参

  • 默认参数(形参)

在定义函数阶段,就已经被赋值的形参,称为默认参数

  1. def f(x,y,z=5):
  2. sum = x+y+z
  3. print(sum)
  4. f(1,3)
  5. #在定义阶段就已经被赋值,就意味着在调用阶段,可以不用赋值
  • 可变参数

在形参前使用表示形参是可变参数,放在一个元组里
形参前面使用*
符号,,表示可以接收多个关键字参数,收集的实参名称和值组成一个字典

  1. def add(nums):
  2. sum=0
  3. for x in nums:
  4. sum += x
  5. return sum
  6. add([1,3,5])
  7. add((2,4,5))
  8. 传入一个可迭代对象,迭代元素求和
  9. 一个形参可以匹配任意个参数
  10. def add(*nums): #放在一个元组里
  11. sum=0
  12. for x in nums:
  13. sum += x
  14. print(sum)
  15. add(3,6,9)
  16. 在形参前使用*表示形参是可变参数
  17. 收集多个实参为一个tuple
  18. #关键字参数的可变参数
  19. def showconfig(**kwargs):
  20. for k,v in kwargs.items():
  21. print('{} = {}'.format(k,v))
  22. showconfig(host='127.0.0.1',port='8080',username='wayne')
  23. 形参前面使用**符号,表示可以接收多个关键字参数
  24. 收集的实参名称和值组成一个字典

总结:

  • 位置可变参数和关键字可变参数
  • 不能为同一个形参重复赋值
  • 位置可变参数在形参前使用一个星号*
  • 关键字可变参数在形参前使用2个星号**
  • 位置可变和关键字可变参数都可以收集若干个实参,位置: tuple,关键字:dict
  • 混合使用的时候,可变参数放在参数列表的最后,普通参数需要放在参数列表前面,位置可变参数需要在关键字参数之前
  • 位置形参必须在默认值形参的前面
  1. def fn(x,y,*args,**kwargs):
  2. print(x)
  3. print(y)
  4. print(args)
  5. print(kwargs)
  6. fn(3,5,7,9,10,a=1,b='python') # x=3,y=5,(7,9,10),{'a':1,'b':'python'}
  7. fn(3,5,a=1,b='python') #3,5,(),{'a': 1, 'b': 'python'}
  8. fn(7,9,y=5,x=3,a=1,b='python') #TypeError: fn() got multiple values for argument 'y'
  • keyword-only参数

如果在一个星号参数后,或者一个位置可变参数后,出现一个普通参数,该参数不是普通的参数,而是keyword-only参数

  1. def fn(*args,x):
  2. print(x)
  3. print(args)
  4. fn(3,5,x=7)
  5. fn(3,5) #错误fn() got multiple values for argument 'y'
  6. def fn(**kwargs,x):
  7. print(x)
  8. print(kwargs)
  9. fn(3,5)
  10. 报错
  11. File "<ipython-input-47-89a0d4d3df0a>", line 1
  12. def fn(**kwargs,x):
  13. ^
  14. SyntaxError: invalid syntax
  1. def fn(*,x,y):
  2. print(x,y)
  3. fn(x=5,y=6)
  4. *号之后,普通形参都变成必须给出的keyword-only参数
  1. def fn(y,*args,x=5):
  2. print('x={},y={}'.format(x,y))
  3. print(args)
  4. fn(1,2,3,x=10) #x=10,y=1 (2, 3)
  5. fn(1,2,y=3,x=10) #TypeError: fn() got multiple values for argument 'y'
  6. 说明: xkeyword-only参数

4、函数嵌套

python中以函数为作用域,在作用域中定义的相关数据(变量)只能被当前作用域或子作用域使用

  1. name="xhaihua"
  2. print(name) #xhaihua
  3. def func():
  4. print(name) #xhaihua
  5. func()

其实,函数也是定义在作用域中的数据,在执行函数时候,也同样优先在自己的作用域中寻找,没有则向上
作用域,例如

  1. def func():
  2. print('xhaihua')
  3. func()
  4. def execute():
  5. print('开始')
  6. func() #xhaihua
  7. print('结束')
  8. execute()

函数可以定义在全局作用域,其实函数也可以定义在局部作用域,这样函数被局部作用域和子作用域中调用(函数的嵌套)

  1. def func():
  2. print('666')
  3. def handless():
  4. print("1234")
  5. def inner():
  6. print('aaa')
  7. inner()
  8. func()
  9. print('结束')
  10. handless()

嵌套函数示例: 生成图片验证码(简单版)

  1. pip install pillow
  2. 代码示例如下:
  3. '''
  4. #把msyh.ttc拷贝到项目目录下
  5. import random
  6. from PIL import Image,ImageDraw,ImageFont
  7. def create_image_code(img_file_path,text=None,size=(60,30),node="RGB",bg_color=(255,255,255)):
  8. _letter_cases='abcdefghjkmnpqrstuywx'
  9. _upper_cases= _letter_cases.upper()
  10. _numbers = ''.join(map(str,range(3,10)))
  11. chars = ''.join((_letter_cases,_upper_cases,_numbers))
  12. width,heigh = size
  13. img = Image.new(node,size,bg_color)
  14. draw = ImageDraw.Draw(img)
  15. def get_chars():
  16. return random.sample(chars,6)
  17. def create_lines():
  18. line_num = random.randint(*(1,2))
  19. for i in range(line_num):
  20. begin = (random.randint(0,size[0]),random.randint(0,size[1]))
  21. end = (random.randint(0,size[0]),random.randint(0,size[1]))
  22. draw.line([begin,end],fill=(0,0,0))
  23. def create_points():
  24. chance = min(108,max(0,int(2)))
  25. for w in range(width):
  26. for h in range(heigh):
  27. tmp=random.randint(0,180)
  28. if tmp>100 - chance:
  29. draw.point((w,h),fill=(0,0,0))
  30. def create_node():
  31. if text:
  32. code_string = text
  33. else:
  34. char_list = get_chars()
  35. code_string = ''.join(char_list)
  36. font = ImageFont.truetype('msyh.ttc',size=15)
  37. draw.text([0,0],code_string,"green",font=font)
  38. create_lines()
  39. create_points()
  40. code = create_node()
  41. with open(img_file_path,mode='wb') as img_object:
  42. img.save(img_object)
  43. return code
  44. code = create_image_code("a2.png")
  45. print(code)

作用域总结:

  • 优先在自己的作用域找,自己没有就去上级作用域
  • 在作用域中寻找值时,要确保值是什么
  • 分析函数的执行,并确保函数的作用域

5、闭包

闭包简而言之就是将数据封装在一个包(区域)中,使用的时候在去里面取(本质上闭包是基于函数
嵌套的一种特殊嵌套)
场景1: 将数据封装到包里,防止污染全局

  1. def func(age):
  2. name="xhaihua"
  3. def f1():
  4. print(name,age)
  5. def f2():
  6. print(name,age)
  7. f1()
  8. f2()

场景2: 封装数据在一个包,使用的时候去取

  1. def task(age):
  2. def inner():
  3. print(age)
  4. return inner
  5. v1 = task(11) # v1 = inner
  6. v1() #v1() = inner()
  7. #
  8. def task(age):
  9. def inner():
  10. print(age)
  11. return inner
  12. inner_func_list = []
  13. for val in [11,22,33]:
  14. inner_func_list.append(task(val))
  15. inner_func_list[0]()
  16. inner_func_list[1]()
  17. inner_func_list[2]()
  1. import requests
  2. from concurrent.futures.thread import ThreadPoolExecutor
  3. #下载文件
  4. def task(url):
  5. res = requests.get(
  6. url=url,
  7. headers = {
  8. "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36"
  9. }
  10. )
  11. return res.content
  12. #保存文件
  13. def outer(file_name):
  14. def done(arg):
  15. content = arg.result()
  16. with open(file_name,mode='wb') as file_object:
  17. file_object.write(content)
  18. return done
  19. #线程池
  20. POOL = ThreadPoolExecutor(10)
  21. video_list = [
  22. ("乌云.mp4","https://video.699pic.com/videos/93/62/02/b_Uc9dpeHc9DhE1592936202_10s.mp4"),
  23. ("光线.mp4","https://video.699pic.com/videos/81/39/90/a_ImItRvbQye9G1592813990_10s.mp4")
  24. ]
  25. for item in video_list:
  26. #print(item[0],item[1])
  27. #取线程池取,让其做事情
  28. future = POOL.submit(task, url=item[1])
  29. #执行保存
  30. future.add_done_callback( outer(item[0]))

6、装饰器

  1. def func():
  2. print("我是func函数")
  3. value = (11,22,33,44)
  4. return value
  5. def outer(origin):
  6. def inner():
  7. print("before")
  8. res = origin()
  9. print("after")
  10. return res
  11. return inner
  12. func = outer(func)
  13. result = func()
  14. print(result)

python中支持特殊语法,在某个函数上方使用: @函数名
在python内部会自动执行:函数名()

  1. def outer(origin):
  2. def inner():
  3. print("before")
  4. res = origin()
  5. print("after")
  6. return res
  7. return inner
  8. @outer
  9. def func():
  10. print("我是func函数")
  11. value = (11,22,33,44)
  12. return value
  13. result = func()
  14. print(result)