函数的定义与调用

  1. def my_func(str):
  2. length = 0
  3. for i in str:
  4. length = length + 1
  5. pass
  6. print("长度是%s" % length)
  7. s = 'hello world'
  8. my_func(s)

定义:def 关键词开头,空格之后接函数名称和圆括号(),最后还有一个”:”。
注释:每一个函数都应该对功能和参数进行相应的说明,应该写在函数下面第一行。以增强代码的可读性。
调用:就是 函数名() 要记得加上括号。

函数的返回值

  1. def my_len():
  2. s = 'hello world'
  3. length = 0
  4. for i in s:
  5. length = length + 1
  6. return length
  7. str_len = my_len()
  8. print(str_len)
  9. 运行结果
  10. 11

return关键字的作用

  • return 是一个关键字,这个词翻译过来就是“返回”,所以我们管写在return后面的值叫“返回值”。
  • 不写return的情况下,会默认返回一个None
  • 一旦遇到return,结束整个函数。
  • 返回多个值会被组织成元组被返回,也可以用多个值来接收 ```python def ret_demo(): return 1,2,’a’,[‘hello’,’world’] ret = ret_demo() print(ret)

运行结果 (1, 2, ‘a’, [‘hello’, ‘world’])

  1. <a name="0mKyV"></a>
  2. # 函数的参数
  3. <a name="n7Zco"></a>
  4. ## 带参数的函数
  5. ```python
  6. def my_len(s):
  7. length = 0
  8. for i in s:
  9. length += 1
  10. return length
  11. ret = my_len('hello world!')
  12. print(ret)

实际的要交给函数的内容,简称实参。
在定义函数的时候它只是一个形式,表示这里有一个参数,简称形参。

1.按位置传值:位置参数

  1. def maxnumber(x,y):
  2. the_max = x if x > y else y
  3. return the_max
  4. ret = maxnumber(10,20)
  5. print(ret)
  6. #运行结果
  7. 20

2.按关键字传值:关键字参数

  1. def maxnumber(x,y):
  2. the_max = x if x > y else y
  3. return the_max
  4. ret = maxnumber(y = 10,x = 20)
  5. print(ret)
  6. #运行结果
  7. 20

3.位置、关键字混合使用:混合传参

  1. def maxnumber(x,y):
  2. the_max = x if x > y else y
  3. return the_max
  4. ret = maxnumber(10,y = 20)
  5. print(ret)
  6. #运行结果
  7. 20

注意:

  • 位置参数必须在关键字参数的前面
  • 对于一个形参只能赋值一次

    4.默认参数

    ```python def stu_info(name,age = 18): print(name,age) stu_info(‘aaron’) stu_info(‘song’,50)

运行结果

aaron 18 aaron 50

  1. 当默认参数是一个可变数据类型
  2. ```python
  3. def demo(a,l = []):
  4. l.append(a)
  5. print(l)
  6. demo('abc')
  7. demo('123')
  8. #运行结果
  9. ['abc']
  10. ['abc', '123']

5.动态参数

  1. def demo(*args,**kwargs):
  2. print(args,type(args))
  3. print(kwargs,type(kwargs))
  4. demo('aaron',1,3,[1,3,2,2],{'a':123,'b':321},country='china',b=1)
  5. #运行结果
  6. ('aaron', 1, 3, [1, 3, 2, 2], {'a': 123, 'b': 321}) <class 'tuple'>
  7. {'country': 'china', 'b': 1} <class 'dict'>

动态参数,也叫不定长传参,就是你需要传给函数的参数很多,不定个数,那这种情况下,你就用args,
*
kwargs接收,args是元祖形式,接收除去键值对以外的所有参数,kwargs接收的只是键值对的参数,并
保存在字典中

python中函数的参数有位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这个顺序也是定
义函数时的必须顺序。

命名空间与作用域

代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间;
在函数的运行中开辟的临时的空间叫做局部命名空间。

命名空间一共分为三种:

  • 全局命名空间
  • 局部命名空间
  • 内置命名空间

取值顺序:

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

作用域

  • 全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效
  • 局部作用域:局部名称空间,只能在局部范围内生效

globals和locals方法

  1. print(globals())
  2. print(locals())
  3. def func():
  4. a = 12
  5. b = 20
  6. print(globals())
  7. print(locals())
  8. func()

运行结果

  1. {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000020F7B4A0448>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/mi/PycharmProjects/untitled/函数.py', '__cached__': None}
  2. {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000020F7B4A0448>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/mi/PycharmProjects/untitled/函数.py', '__cached__': None}
  3. {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000020F7B4A0448>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/mi/PycharmProjects/untitled/函数.py', '__cached__': None, 'func': <function func at 0x0000020F7B553798>}
  4. {'a': 12, 'b': 10}

global 关键字

  1. 声明一个全局变量。
    2. 在局部作用域想要对全局作用域的全局变量进行修改时,需要用到 global(限于字符串,数字)。 ```python count = 1 def search(): global count count = 2

search() print(count)

  1. 运行结果
  2. ```python
  3. 2

对可变数据类型(list,dict,set)可以直接引用不用通过global

  1. li = [1,2,3]
  2. dic = {'name':'aaron'}
  3. def change():
  4. li.append(4)
  5. dic['age'] = 18
  6. print(dic)
  7. print(li)
  8. change()
  9. print(dic)
  10. print(li)
  11. #运行结果
  12. [1,2,3,4]
  13. {'name':'aaron','age':18}

nonlocal

  1. 不能修改全局变量。
    2. 在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且
    引用的哪层,从那层及以下此变量全部发生改变。
    1. def add_b():
    2. b = 1
    3. def do_global():
    4. b = 10
    5. print(b)
    6. def dd_nolocal():
    7. nonlocal b # 应用了上一层的变量b
    8. b = b + 20
    9. print(b) # 发生了改变
    10. dd_nolocal() # 调用函数,导致do_global的命名空间b也改变了
    11. print(b)
    12. do_global()
    13. print(b)
    14. add_b() # 最上面一层没有变化
    运行结果
    1. 10
    2. 30
    3. 30
    4. 1

    函数的嵌套和作用域链

    在一个函数里可以调用其他函数 ```python def mymax(x,y): m = x if x > y else y return m

def maxx(a,b,c,d): res1 = mymax(a,b) res2 = mymax(res1,c) res3 = mymax(res2,d) return res3

ret = maxx(23,453,12,-13) print(ret)

  1. 也可以直接再函数里定义另一个函数,并直接调用,但注意在函数内定义的函数是**局部**的,只能在函数内调用。
  2. ```python
  3. def f1():
  4. print("in f1")
  5. def f2():
  6. print("in f2")
  7. f2()
  8. f1()

函数名的本质

函数名的本质是内存地址

  1. 可以被引用 ```python def func(): print(‘in func’)

f = func print(f) f()

运行结果

in func

  1. 2. 可以当作容器类型的元素
  2. ```python
  3. def f1():
  4. print('f1')
  5. def f2():
  6. print('f2')
  7. def f3():
  8. print('f3')
  9. l = [f1,f2,f3]
  10. d = {'f1':f1,'f2':f2,'f3':f3}
  11. # 调用
  12. l[0]()
  13. d['f2']()
  14. # 运行结果
  15. f1
  16. f2
  1. 可以作为其他函数的参数或返回值 ```python def f1(): print(‘f1’)

def func(argv): argv() return argv

f = func(f1) f()

运行结果

f1

  1. <a name="ddbQY"></a>
  2. # 闭包
  3. 内部函数包含对外部作用域而非全剧作用域变量的引用,该内部函数称为闭包函数。如下,内部函数inner里引用了外部函数func的变量name,所以inner是闭包函数。
  4. ```python
  5. def func():
  6. name = 'aaron'
  7. def inner():
  8. print(name)
  9. return inner
  10. f = func()
  11. f()

判断闭包函数的方法closure

  1. def func():
  2. name = 'aaron'
  3. def inner():
  4. print(name)
  5. print(inner.__closure__)
  6. return inner
  7. f = func()
  8. f()
  9. # 最后运行的结果里面有cell就是闭包
  10. # 运行结果
  11. (<cell at 0x000001E2A562B738: str object at 0x000001E2A56504F0>,)
  12. aaron
  13. ===========================================================
  14. name = 'aaron'
  15. def func():
  16. def inner():
  17. print(name)
  18. print(inner.__closure__)
  19. return inner
  20. f = func()
  21. f()
  22. # 输出结果为None,说明不是闭包
  23. # 运行结果
  24. None

案例一:使用闭包修改函数功能

def func1():
print(‘in func1’)
需求是使用闭包,在不更改这两行代码的前提,执行func1(),输出的结果为:
hello
in func1
hello

  1. def func1():
  2. print('in func1')
  3. def func2(f):
  4. s = "hello"
  5. def inner():
  6. print(s)
  7. f()
  8. print(s)
  9. return inner
  10. func1 = func2(func1)
  11. func1()

应用:当我们想修改一个函数的功能时可以这样做

装饰器*(面试高频)

装饰器的形成过程

使用闭包修改函数最后还要把原函数作为参数再被覆盖,这样的操作每修改一次就要执行一次,很麻烦,python为此准备了语法糖,省略了这一步骤。
即原本的”func1 = func2(func1)”替换为再func1函数上加上”@func2”

  1. def func2(f):
  2. s = "hello"
  3. def inner():
  4. print(s)
  5. f()
  6. print(s)
  7. return inner
  8. @func2
  9. def func1():
  10. print('in func1')
  11. func1()

从上面这个例子可以看出,一个无参函数的装饰器就两层,外层的参数用来接收被装饰函数func1,内层无参并引用外层的参数形成闭包。

装饰器的应用

装饰一个带有参数的函数

装饰带有参数的函数一样是两层,外层参数用来接收被装饰函数,内层的参数是被装饰函数的参数

  1. import time
  2. def func2(f):
  3. def inner(a):
  4. start = time.time()
  5. f(a)
  6. last = time.time() - start
  7. print(last)
  8. return inner
  9. @func2
  10. def func1(a):
  11. time.sleep(1)
  12. print(a)
  13. func1("hello world")

装饰一个带有各种参数的函数

  1. import time
  2. def timer(func):
  3. def inner(*args,**kwargs):
  4. start = time.time()
  5. func(args,kwargs)
  6. print(time.time() - start)
  7. return inner
  8. @timer
  9. def func1(*args,**kwargs):
  10. print(args,kwargs)
  11. func1('hello world','abc',123,432)

查看函数的相关信息(加装饰器后失效)

  1. def index():
  2. '''这是一条注释信息'''
  3. print('from index')
  4. print(index.__doc__) # 查看函数注释
  5. print(index.__name__) # 查看函数名称
  6. # 运行结果
  7. 这是一条注释信息
  8. index

这时如果我们加上一个装饰器

  1. def deco(func):
  2. def wrapper(*args,**kwargs):
  3. return func(*args,**kwargs)
  4. return wrapper
  5. @deco
  6. def index():
  7. '''这是一条注释信息'''
  8. print('from index')
  9. print(index.__doc__) # 查看函数注释
  10. print(index.__name__) # 查看函数名称
  11. # 运行结果
  12. None
  13. wrapper

可以发现这里函数名称被wrapper覆盖了,他重写了函数名和注释内容。我们需要利用Python提供的函数functools.wraps来解决这个问题。

  1. from functools import wraps
  2. def deco(func):
  3. @wraps(func) # 在内层函数加一个装饰器@wraps(f),参数f是外层函数参数
  4. def wrapper(*args,**kwargs):
  5. return func(*args,**kwargs)
  6. return wrapper
  7. @deco
  8. def index():
  9. '''这是一条注释信息'''
  10. print('from index')
  11. print(index.__doc__) # 查看函数注释
  12. print(index.__name__) # 查看函数名称
  13. # 运行结果
  14. 这是一条注释信息
  15. index

控制装饰器是否生效

加上一个outer函数,可以携带一个flag的值,然后控制装饰器是否生效

  1. def outer(flag):
  2. def time(func):
  3. def inner(*args, **kwargs):
  4. if flag:
  5. print('函数开始执行')
  6. change = func1(*args, **kwargs)
  7. if flag:
  8. print('函数执行完毕')
  9. return change
  10. return inner
  11. return time
  12. flag = 装饰器是否生效() # 函数返回值位True则生效,返回False则不生效
  13. @outer(flag)
  14. def func1():
  15. print("test")
  16. func1()

多个装饰器装饰一个函数

  1. def wrapper1(func):
  2. def inner():
  3. print("第一个装饰器")
  4. func()
  5. print("第一个装饰器")
  6. return inner
  7. def wrapper2(func):
  8. def inner():
  9. print("第二个装饰器")
  10. func()
  11. print("第二个装饰器")
  12. return inner
  13. @wrapper1
  14. @wrapper2
  15. def f():
  16. print("in f")
  17. f()
  18. # 运行结果
  19. 第一个装饰器
  20. 第二个装饰器
  21. in f
  22. 第二个装饰器
  23. 第一个装饰器

开放封闭原则

软件应该是可扩展的但不可修改的:

  • 对于扩展是开放的
  • 对于修改是封闭的

装饰器完美地遵守了开放封闭原则

案例二:综合练习(用户登录注册系统)

  1. # 注册
  2. def register():
  3. list1 = []
  4. str1 = '''
  5. 用户名不得以数字开头
  6. 必须是英文字母或者数字组成
  7. 密码不得小于6位
  8. '''
  9. print(str1)
  10. name = input("请输入用户名:")
  11. # 读取文件内容,查看用户名是否已存在
  12. with open("USER.txt", "r", encoding="utf-8") as read_f:
  13. for i in read_f.readlines():
  14. dict1 = {"user": i.split("|")[0], "password": i.strip().split("|")[1]}
  15. list1.append(dict1)
  16. for i in list1:
  17. if name == i["user"]:
  18. print("用户名已存在")
  19. return False
  20. # 判断用户名开头是不是数字
  21. if name[0].isdigit():
  22. print("用户名必须以字母开头")
  23. return False
  24. # 判断用户名是不是由字母、数字组成
  25. elif name.isalnum():
  26. print("用户名规则合法")
  27. password = input("请输入密码:")
  28. # 判断密码长度是否不少于6位
  29. if len(password) < 6:
  30. print("密码不得小于6位")
  31. return False
  32. print("注册成功!")
  33. # 将输入合法的用户名和密码写入文件
  34. with open("USER.txt", "a+", encoding="utf-8") as write_f:
  35. user_data = "\n" + name + "|" + password
  36. write_f.write(user_data)
  37. return True
  38. # 登录
  39. def login():
  40. name = input("请输入用户名:")
  41. pwd = input("请输入密码:")
  42. list1 = []
  43. # 读取文件,如果用户名和密码正确登陆成功,否则失败
  44. with open("USER.txt", "r", encoding="utf-8") as read_f:
  45. for line2 in read_f.readlines():
  46. dict1 = {"user": line2.split("|")[0], "password": line2.strip().split("|")[1]}
  47. list1.append(dict1)
  48. for i in list1:
  49. if name == i["user"] and pwd == i["password"]:
  50. print("登录成功!")
  51. return True
  52. return False
  53. # 读去文件a.txt的功能
  54. def read_a(flag):
  55. def change(func):
  56. def inner(*args, **kwargs):
  57. if flag:
  58. with open("a.txt", "r", encoding='utf-8') as r:
  59. for line in r.readlines():
  60. print(line)
  61. else:
  62. print("登陆失败,无法查看文件")
  63. return inner
  64. return change
  65. while True:
  66. print("==================菜单===================")
  67. print("1.登录\n2.注册\n3.退出")
  68. choice = input("请输入要使用的服务:")
  69. if choice == "1":
  70. flag1 = login()
  71. @read_a(flag1)
  72. def read_txt():
  73. print("查看文件内容")
  74. read_txt()
  75. elif choice == "2":
  76. register()
  77. elif choice == "3":
  78. break
  79. else:
  80. print("请重新输入!")
  81. continue

运行结果

  1. ==================菜单===================
  2. 1.登录
  3. 2.注册
  4. 3.退出
  5. 请输入要使用的服务:2
  6. 用户名不得以数字开头
  7. 必须是英文字母或者数字组成
  8. 密码不得小于6
  9. 请输入用户名:marry
  10. 用户名规则合法
  11. 请输入密码:333333
  12. 注册成功!
  13. ==================菜单===================
  14. 1.登录
  15. 2.注册
  16. 3.退出
  17. 请输入要使用的服务:1
  18. 请输入用户名:marry
  19. 请输入密码:333333
  20. 登录成功!
  21. apple 10 3
  22. tesla 100000 1
  23. mac 3000 2
  24. lenovo 30000 3
  25. chicken 10 3
  26. ==================菜单===================
  27. 1.登录
  28. 2.注册
  29. 3.退出
  30. 请输入要使用的服务:3