函数的定义与调用
def my_func(str):length = 0for i in str:length = length + 1passprint("长度是%s" % length)s = 'hello world'my_func(s)
定义:def 关键词开头,空格之后接函数名称和圆括号(),最后还有一个”:”。
注释:每一个函数都应该对功能和参数进行相应的说明,应该写在函数下面第一行。以增强代码的可读性。
调用:就是 函数名() 要记得加上括号。
函数的返回值
def my_len():s = 'hello world'length = 0for i in s:length = length + 1return lengthstr_len = my_len()print(str_len)运行结果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’])
<a name="0mKyV"></a># 函数的参数<a name="n7Zco"></a>## 带参数的函数```pythondef my_len(s):length = 0for i in s:length += 1return lengthret = my_len('hello world!')print(ret)
实际的要交给函数的内容,简称实参。
在定义函数的时候它只是一个形式,表示这里有一个参数,简称形参。
1.按位置传值:位置参数
def maxnumber(x,y):the_max = x if x > y else yreturn the_maxret = maxnumber(10,20)print(ret)#运行结果20
2.按关键字传值:关键字参数
def maxnumber(x,y):the_max = x if x > y else yreturn the_maxret = maxnumber(y = 10,x = 20)print(ret)#运行结果20
3.位置、关键字混合使用:混合传参
def maxnumber(x,y):the_max = x if x > y else yreturn the_maxret = maxnumber(10,y = 20)print(ret)#运行结果20
注意:
- 位置参数必须在关键字参数的前面
- 对于一个形参只能赋值一次
4.默认参数
```python def stu_info(name,age = 18): print(name,age) stu_info(‘aaron’) stu_info(‘song’,50)
运行结果
aaron 18 aaron 50
当默认参数是一个可变数据类型```pythondef demo(a,l = []):l.append(a)print(l)demo('abc')demo('123')#运行结果['abc']['abc', '123']
5.动态参数
def demo(*args,**kwargs):print(args,type(args))print(kwargs,type(kwargs))demo('aaron',1,3,[1,3,2,2],{'a':123,'b':321},country='china',b=1)#运行结果('aaron', 1, 3, [1, 3, 2, 2], {'a': 123, 'b': 321}) <class 'tuple'>{'country': 'china', 'b': 1} <class 'dict'>
动态参数,也叫不定长传参,就是你需要传给函数的参数很多,不定个数,那这种情况下,你就用args,
*kwargs接收,args是元祖形式,接收除去键值对以外的所有参数,kwargs接收的只是键值对的参数,并
保存在字典中
python中函数的参数有位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这个顺序也是定
义函数时的必须顺序。
命名空间与作用域
代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间;
在函数的运行中开辟的临时的空间叫做局部命名空间。
命名空间一共分为三种:
- 全局命名空间
- 局部命名空间
- 内置命名空间
取值顺序:
- 在局部调用:局部命名空间->全局命名空间->内置命名空间
- 在全局调用:全局命名空间->内置命名空间
作用域
- 全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效
- 局部作用域:局部名称空间,只能在局部范围内生效
globals和locals方法
print(globals())print(locals())def func():a = 12b = 20print(globals())print(locals())func()
运行结果
{'__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}{'__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}{'__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>}{'a': 12, 'b': 10}
global 关键字
- 声明一个全局变量。
2. 在局部作用域想要对全局作用域的全局变量进行修改时,需要用到 global(限于字符串,数字)。 ```python count = 1 def search(): global count count = 2
search() print(count)
运行结果```python2
对可变数据类型(list,dict,set)可以直接引用不用通过global
li = [1,2,3]dic = {'name':'aaron'}def change():li.append(4)dic['age'] = 18print(dic)print(li)change()print(dic)print(li)#运行结果[1,2,3,4]{'name':'aaron','age':18}
nonlocal
- 不能修改全局变量。
2. 在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且
引用的哪层,从那层及以下此变量全部发生改变。
运行结果def add_b():b = 1def do_global():b = 10print(b)def dd_nolocal():nonlocal b # 应用了上一层的变量bb = b + 20print(b) # 发生了改变dd_nolocal() # 调用函数,导致do_global的命名空间b也改变了print(b)do_global()print(b)add_b() # 最上面一层没有变化
1030301
函数的嵌套和作用域链
在一个函数里可以调用其他函数 ```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)
也可以直接再函数里定义另一个函数,并直接调用,但注意在函数内定义的函数是**局部**的,只能在函数内调用。```pythondef f1():print("in f1")def f2():print("in f2")f2()f1()
函数名的本质
函数名的本质是内存地址
- 可以被引用 ```python def func(): print(‘in func’)
f = func print(f) f()
运行结果
2. 可以当作容器类型的元素```pythondef f1():print('f1')def f2():print('f2')def f3():print('f3')l = [f1,f2,f3]d = {'f1':f1,'f2':f2,'f3':f3}# 调用l[0]()d['f2']()# 运行结果f1f2
- 可以作为其他函数的参数或返回值 ```python def f1(): print(‘f1’)
def func(argv): argv() return argv
f = func(f1) f()
运行结果
f1
<a name="ddbQY"></a># 闭包内部函数包含对外部作用域而非全剧作用域变量的引用,该内部函数称为闭包函数。如下,内部函数inner里引用了外部函数func的变量name,所以inner是闭包函数。```pythondef func():name = 'aaron'def inner():print(name)return innerf = func()f()
判断闭包函数的方法closure
def func():name = 'aaron'def inner():print(name)print(inner.__closure__)return innerf = func()f()# 最后运行的结果里面有cell就是闭包# 运行结果(<cell at 0x000001E2A562B738: str object at 0x000001E2A56504F0>,)aaron===========================================================name = 'aaron'def func():def inner():print(name)print(inner.__closure__)return innerf = func()f()# 输出结果为None,说明不是闭包# 运行结果None
案例一:使用闭包修改函数功能
def func1():
print(‘in func1’)
需求是使用闭包,在不更改这两行代码的前提,执行func1(),输出的结果为:
hello
in func1
hello
def func1():print('in func1')def func2(f):s = "hello"def inner():print(s)f()print(s)return innerfunc1 = func2(func1)func1()
装饰器*(面试高频)
装饰器的形成过程
使用闭包修改函数最后还要把原函数作为参数再被覆盖,这样的操作每修改一次就要执行一次,很麻烦,python为此准备了语法糖,省略了这一步骤。
即原本的”func1 = func2(func1)”替换为再func1函数上加上”@func2”
def func2(f):s = "hello"def inner():print(s)f()print(s)return inner@func2def func1():print('in func1')func1()
从上面这个例子可以看出,一个无参函数的装饰器就两层,外层的参数用来接收被装饰函数func1,内层无参并引用外层的参数形成闭包。
装饰器的应用
装饰一个带有参数的函数
装饰带有参数的函数一样是两层,外层参数用来接收被装饰函数,内层的参数是被装饰函数的参数。
import timedef func2(f):def inner(a):start = time.time()f(a)last = time.time() - startprint(last)return inner@func2def func1(a):time.sleep(1)print(a)func1("hello world")
装饰一个带有各种参数的函数
import timedef timer(func):def inner(*args,**kwargs):start = time.time()func(args,kwargs)print(time.time() - start)return inner@timerdef func1(*args,**kwargs):print(args,kwargs)func1('hello world','abc',123,432)
查看函数的相关信息(加装饰器后失效)
def index():'''这是一条注释信息'''print('from index')print(index.__doc__) # 查看函数注释print(index.__name__) # 查看函数名称# 运行结果这是一条注释信息index
这时如果我们加上一个装饰器
def deco(func):def wrapper(*args,**kwargs):return func(*args,**kwargs)return wrapper@decodef index():'''这是一条注释信息'''print('from index')print(index.__doc__) # 查看函数注释print(index.__name__) # 查看函数名称# 运行结果Nonewrapper
可以发现这里函数名称被wrapper覆盖了,他重写了函数名和注释内容。我们需要利用Python提供的函数functools.wraps来解决这个问题。
from functools import wrapsdef deco(func):@wraps(func) # 在内层函数加一个装饰器@wraps(f),参数f是外层函数参数def wrapper(*args,**kwargs):return func(*args,**kwargs)return wrapper@decodef index():'''这是一条注释信息'''print('from index')print(index.__doc__) # 查看函数注释print(index.__name__) # 查看函数名称# 运行结果这是一条注释信息index
控制装饰器是否生效
加上一个outer函数,可以携带一个flag的值,然后控制装饰器是否生效
def outer(flag):def time(func):def inner(*args, **kwargs):if flag:print('函数开始执行')change = func1(*args, **kwargs)if flag:print('函数执行完毕')return changereturn innerreturn timeflag = 装饰器是否生效() # 函数返回值位True则生效,返回False则不生效@outer(flag)def func1():print("test")func1()
多个装饰器装饰一个函数
def wrapper1(func):def inner():print("第一个装饰器")func()print("第一个装饰器")return innerdef wrapper2(func):def inner():print("第二个装饰器")func()print("第二个装饰器")return inner@wrapper1@wrapper2def f():print("in f")f()# 运行结果第一个装饰器第二个装饰器in f第二个装饰器第一个装饰器
开放封闭原则
软件应该是可扩展的但不可修改的:
- 对于扩展是开放的
- 对于修改是封闭的
装饰器完美地遵守了开放封闭原则
案例二:综合练习(用户登录注册系统)
# 注册def register():list1 = []str1 = '''用户名不得以数字开头必须是英文字母或者数字组成密码不得小于6位'''print(str1)name = input("请输入用户名:")# 读取文件内容,查看用户名是否已存在with open("USER.txt", "r", encoding="utf-8") as read_f:for i in read_f.readlines():dict1 = {"user": i.split("|")[0], "password": i.strip().split("|")[1]}list1.append(dict1)for i in list1:if name == i["user"]:print("用户名已存在")return False# 判断用户名开头是不是数字if name[0].isdigit():print("用户名必须以字母开头")return False# 判断用户名是不是由字母、数字组成elif name.isalnum():print("用户名规则合法")password = input("请输入密码:")# 判断密码长度是否不少于6位if len(password) < 6:print("密码不得小于6位")return Falseprint("注册成功!")# 将输入合法的用户名和密码写入文件with open("USER.txt", "a+", encoding="utf-8") as write_f:user_data = "\n" + name + "|" + passwordwrite_f.write(user_data)return True# 登录def login():name = input("请输入用户名:")pwd = input("请输入密码:")list1 = []# 读取文件,如果用户名和密码正确登陆成功,否则失败with open("USER.txt", "r", encoding="utf-8") as read_f:for line2 in read_f.readlines():dict1 = {"user": line2.split("|")[0], "password": line2.strip().split("|")[1]}list1.append(dict1)for i in list1:if name == i["user"] and pwd == i["password"]:print("登录成功!")return Truereturn False# 读去文件a.txt的功能def read_a(flag):def change(func):def inner(*args, **kwargs):if flag:with open("a.txt", "r", encoding='utf-8') as r:for line in r.readlines():print(line)else:print("登陆失败,无法查看文件")return innerreturn changewhile True:print("==================菜单===================")print("1.登录\n2.注册\n3.退出")choice = input("请输入要使用的服务:")if choice == "1":flag1 = login()@read_a(flag1)def read_txt():print("查看文件内容")read_txt()elif choice == "2":register()elif choice == "3":breakelse:print("请重新输入!")continue
运行结果
==================菜单===================1.登录2.注册3.退出请输入要使用的服务:2用户名不得以数字开头必须是英文字母或者数字组成密码不得小于6位请输入用户名:marry用户名规则合法请输入密码:333333注册成功!==================菜单===================1.登录2.注册3.退出请输入要使用的服务:1请输入用户名:marry请输入密码:333333登录成功!apple 10 3tesla 100000 1mac 3000 2lenovo 30000 3chicken 10 3==================菜单===================1.登录2.注册3.退出请输入要使用的服务:3
