普通函数 |
定义:函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可
特性:
1、代码重用
2、保持一致性
3、可扩展性
2、函数的创建
格式:
def 函数名(参数列表):
函数体
函数的命名规则:
1、函数名必须以下划线或字母开头,可以包含任意字母、数字或下划线的组合。不能使用任何的 标点符号;
2、函数名是区分大小写的。
3、函数名不能是保留字
形参与实参:
形参:形式参数,不是实际存在,是虚拟变量。在定义函数和函数体的时候使用形参,目的是在函数调用时接收实参(实参个数,类型应与实参一一对应)
实参:实际参数,调用函数时传给函数的参数,可以是常量,变量,表达式,函数,传给形参
区别:形参是虚拟的,不占用内存空间,.形参变量只有在被调用时才分配内存单元,实参是一个变量,占用内存空间,数据传送单向,实参传给形参,不能形参传给实参
3、函数的参数
# 必须参数:需要以正确的顺序传入函数。调用时的数量必须和声明时一样
def f1(name, age):
print('name:{},age:{}'.format(name, age))
f1('张三', 20)
# 关键字参数:关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值
def f2(name, age):
print('name:{},age:{}'.format(name, age))
f2(age=20, name='李四')
# 默认值参数:默认值参数的值如果没有传入,则使用默认值,否则使用传入的值
def f2(name, age, sex='male'):
print('name:{},age:{},sex:{}'.format(name, age, sex))
f2('张三', 20)
f2('李四', 21, 'male')
f2('李四', 21, sex='female')
# 可变参数 *args
'''
*args:将接收到的变量存到元组里,就是装包操作
拆包和装包:
函数的装包
def 函数(*args) -->此时会出现装包操作
pass
函数(1,2,3,4)
拆包:
调用的时候:
函数(*列表) | 函数(*元组) | 函数(*集合)
'''
def run(a, *args):
print(a) # 1
# args是一个元素,内容为(2,3)
print(args) # (2,3)
# *args是将元组中的元素依次取出来,相当于拆包
print(*args) # 2,3
# 将未拆包的数据传递给run1
run1(args) # ((2,3),)
# 将拆包的数据传递给run1
run1(*args) # (2,3)
def run1(*args):
print(args)
run(1, 2, 3)
# 可变参数**kwargs
# *args:将接受到的变量存到字典里,就是装包操作
def userinfo(**kwargs): # 传来的key=value类型的实参会映射到kwargs的键值对里
# kwargs是一个字典
print(kwargs) # {'username':'admin','password':'123'}
# **kwargs不能像之前的*args那样打印出来看
# 此处可以理解成对**kwargs进行了拆包,username='admin' password='123'
run1(**kwargs)
def run1(username, password):
print(username, password) # admin 123
userinfo(username='admin', password='123')
# userinfo(**{'username': 'admin', 'password': '123'})
# 形参和实参的参数位置说明
形参:
1、默认值参数必须放在非默认值参数和**kwargs参数的前面
2、可变参数*args必须放在可变参数**kwargs的前面
实参:
1、关键字参数必须放在非关键字参数的前面
4、函数的返回值
1、函数在执行的过程中如果遇到return就结束,并返回结果
2、函数没有return语句时,默认返回None
3、return多个对象,解释器会把这多个对象组装成一个元组作为一个一个整体结果输出
5、可变与不可变数据类型
# 可变数据类型:修改内容后地址值不发生变化,包含:列表、字典、集合
# 不可变数据类型:修改后地址值发生变化,包含:数字、布尔、字符串、元组
# 数字,修改变量值后,地址值发生变化
a = 100
print(id(a)) # 1846611504
a = 200
print(id(a)) # 1846611504
# 字符串,修改变量值后,地址值发生变化
a = 'abc'
print(id(a)) # 2540610897488
a = 'xyz'
print(id(a)) # 2540615908296
# 布尔,修改变量值后,地址值发生变化
a = True
print(id(a)) # 1846354096
a = False
print(id(a)) # 1846354128
# 元组,修改变量值后,地址值发生变化
a = (1, 2, 3)
print(id(a)) # 2034282589280
a = (4, 5, 6)
print(id(a)) # 2034282615720
# 列表,修改变量值后,地址值不发生变化
a = [1, 2, 3]
print(id(a)) # 2181241446088
a.append(4)
print(id(a)) # 2181241446088
# 集合,修改变量值后,地址值不发生变化
a = {1, 2, 3}
print(id(a)) # 2136282285224
a.add(4)
print(id(a)) # 2136282285224
# 字典,修改变量值后,地址值不发生变化
a = {'username': 'admin', 'password': '123'}
print(id(a)) # 2386169288968
a['username'] = 'root'
print(id(a)) # 2386169288968
6、作用域
# 1、作用域的介绍:python中的作用域分4种情况
L:local,局部作用域,即函数中定义的变量;
E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
G:globa,全局变量,就是模块级别定义的变量;
B:built-in,系统固定模块里面的变量,比如int, bytearray等。 搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。
x = int(2.9) # int built-in
g_count = 0 # global
def outer():
o_count = 1 # enclosing
def inner():
i_count = 2 # local
print(o_count)
# print(i_count) 找不到
inner()
outer()
# print(o_count) #找不到
当然,local和enclosing是相对的,enclosing变量相对上层来说也是local
# 2、作用域的产生
在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if、try、for等)是不会引入新的作用域的,如下代码:
if 2>1:
x = 1
print(x) # 1
这个是没有问题的,if并没有引入一个新的作用域,x仍处在当前作用域中,后面代码可以使用。
def test():
x = 2
print(x) # NameError: name 'x2' is not defined
def、class、lambda是可以引入新作用域的。
# 3、变量的修改
场景一:函数内部可以查看全局变量的值
a = 10
def f():
print(a) # 10
f()
场景二:函数内部不可直接修改全局变量的值,想要修改使用global关键字
a = 10
def f():
# a+=10 # local variable 'a' referenced before assignment 直接修改全局变量的值会报错
global a
a += 10
print(a) # 20
f()
场景三:全局变量和局部变量相同时,采用就近原则
a = 10
def f2():
a = 5
print(a) # 5,就近原则
f2()
场景四:当要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量时要使用nolocal关键字
def outer():
a = 10
def inner():
nonlocal a
a += 10
print(a)
inner()
outer()
总结:
1、变量的查找顺序:LEGB,局部作用局--》外层的局部作用域-》当前模块的全局变量--》python内置作用域
2、只有模块、类、函数才引入新的作用域
3、对于一个变量、内部作用域先声明就会覆盖外部的全局变量,不声明直接使用就会使用外部的全局变量
4、内部作用域想要修改外部作用域的变量值时,全局变量要使用global关键字,嵌套作用域要使用nonlocal关键字。nonlocal是python3新增的关键字,有了这个关键字,就能完美的实现闭包了。
递归函数 |
定义:如果在函数内部调用自己,这个函数就是递归函数
递归函数的有点:定义简单,逻辑清晰,理论上所有的递归函数都可以用循环实现,但是循环的逻辑不如递归清晰
递归的特性:
1、必须有一个明确的结束条件
2、每次进入更深一层递归时,问题规模相对上次递归都应该有所减少
3、递归效率不高,递归次数过多会导致栈溢出(在计算机中,函数调用是通过栈这种数据结果实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减少一层栈帧。由于栈的大小不是无限的,所以递归的次数过多会导致栈溢出)
案例1:实现递归函数
def f(n):
if n == 1:
return 1
return n * f(n-1)
print(f(5))
案例2:斐波那契数列
def f(n):
if n == 1:
return 0
if n == 2:
return 1
return f(n - 1) + f(n - 2)
print(f(8))
匿名函数 |
用lambda函数能创建匿名函数,这种函数得名于省略了用def定于函数的标准步骤
lambda的函数只有一种语法,如下
lambda 参数列表:运算表达式
add = lambda x,y:x+y
print(add(1,2)) # 3
匿名函数可以执行任意表达式,可以是print函数,但是一般认为表达式应该有一个计算结果供返回使用。
# 匿名函数作为实参传递
def func(a, f):
return f(a)
print(func(5, lambda x: x + 5))
高级函数 |
高阶函数
max:返回最大值
min:返回最小值
sorted():排序,返回一个新的列表
filter():过滤,返回一个filter对象
map():加工,返回一个map对象
reduce():压缩,返回一个数
from functools import reduce
students = [
{'name': 'zhangsan', 'age': 18, 'score': 92},
{'name': 'lisi', 'age': 20, 'score': 90},
{'name': 'wangwu', 'age': 19, 'score': 95},
{'name': 'jerry', 'age': 21, 'score': 98},
{'name': 'chris', 'age': 17, 'score': 100},
]
# max(*args,key=None) -》 value 找最大的成绩的记录
print(max(students, key=lambda x: x['score']))
# min(*args,key=None) -》 value 找最小的成绩的记录
print(min(students, key=lambda x: x['score']))
# sorted(iterable,key) -> list 根据成绩排序
list1 = sorted(students, key=lambda x: x['score'], reverse=True)
print(list1)
# filter(func,iterable) -> filter object ,过滤出成绩大于94的记录
print(list(filter(lambda x: x['score'] > 94, students)))
# map(func,iterable) -> map object,对每个元素进行加工
print(list(map(lambda x: x['name'].title(), students)))
# reduce(func,iterable) -> value 累计
print(reduce(lambda x, y: x + y, range(11)))
# zip(list1,list2) -> list 将多个可迭代对象的索引相同的位置的值放到一个元组里,最后统一放到一个列表中
print(list(zip([1, 2, 3], [4, 5, 6, 7]))) # [(1, 4), (2, 5), (3, 6)]
装饰器 |
1、闭包
闭包必须满足的条件:
1、必须要有函数的嵌套
2、内部函数使用了外部函数的变量
3、外部函数返回值为外部函数名
说明:闭包本身没有意义,在装饰器中使用
代码:
def outer():
x = 10
def inner():
print(x)
return inner
outer()()
2、装饰器
# 装饰器的功能
原则:遵循开放封闭原则,在不改变原函数功能的情况下,扩展了函数的功能
适用场景:
a:引入日志
b:统计函数执行时间
c:执行函数前预备处理
d:权限校验
e:缓存
场景一:不带参数的装饰器
def login_outer(func):
print('-------进入购物商城----------')
def login_inner():
func()
print('购买牙膏')
print('购买牙刷')
print('------离开购物商城-----------')
return login_inner
@login_outer <==> shopping_car = login_outer(shopping_car)
def shopping_car():
print('开始购物')
shopping_car()
执行步骤:
1、先将函数login_outer()和函数shopping_car()加载到内存中
2、执行函数shopping_car()时,会先执行@login_outer,此时会调用loing_outer()函数,
3、执行 print('-------进入购物商城----------')语句
4、将login_inner()函数加载到内存中
5、执行return login_inner,将login_inner()函数的地址返回给shopping_car()函数,所以调用函数shopping_car() 相当于调用了函数 login_inner()
# 场景二:带参数的装饰器函数
def outer(func):
def inner(*args,**kwargs):
func(*args,**kwargs)
return inner
@outer
def add(*args,**kwargs):
print(sum(args))
add(1,2,3,4)
# 场景三:带参数和返回值的装饰器函数
def outer(func):
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
@outer
def add(*args,**kwargs):
return sum(args)
print(add(1,2,3,4))
# 场景四:装饰器带有参数
# 带参数的装饰器
def login(fo):
def outer(func):
def inner():
if fo in ('jingdong','taobao'):
func()
else:
print('登录失败')
return inner
return outer
@login('jingdong')
def jingdong():
print('欢迎进入京东商城')
@login('taobao')
def taobao():
print('欢迎进入淘宝商城')
jingdong()
taobao()
3、装饰器之登录练习
is_login = False
def login(auth_type=None):
def login_outer(func):
def login_inner():
global is_login
while not is_login:
user_name = input('请输入用户名:')
pass_word = input('请输入密码:')
with open(''.join([auth_type, '.txt']), 'r', encoding='utf-8') as f:
for user in f:
# 将字符串转化为字典
user = eval(user)
if user['username'] == user_name and user['password'] == pass_word:
is_login = True
print('登录成功!')
break
else:
print('用户名或密码错误,请重新选择!')
if is_login:
func()
return login_inner
return login_outer
@login(auth_type='jingdong')
def home():
print('欢迎来到home页面')
@login(auth_type='weixin')
def finance():
print('欢迎来到finance页面')
@login(auth_type='jingdong')
def book():
print('欢迎来到book页面')
if __name__ == '__main__':
menu = {
'1': home,
'2': finance,
'3': book
}
while True:
print('欢迎来到京东商城'.center(30, '-'))
print('1.home\n2.finance\n3.book\n4.退出\n')
choice = input('请选择>>')
if choice in menu:
menu[choice]()
elif choice == '4':
break
else:
print('选择有误,请重新选择!')
列表生成式 |
所谓列表推导式,就是指轻量级循环创建列表
2、基本的使用方式
a = [x for x in range(9)]
print(a) # [0,1,2,3,4,5,6,7,8]
3、在循环的过程中使用if
a = [x for x in range(9) if x % 2 == 0]
print(a) # [0, 2, 4, 6, 8]
4、在循环过程中使用if-else
words = ['123', 'hello', 'word', 'how']
# 将以h开头的单词首字母大写,否则全部大写
a = [word.title() if word.startswith('h') else word.upper() for word in words if word.isalpha()]
print(a)
4、双重for循环
a = [(x,y) for x in range(9) for y in range(9)]
print(a)
5、练习题:请写出一段 Python 代码实现分组一个 list 里面的元素,比如 [1,2,3,...100]变成 [[1,2,3],[4,5,6]....]
list1 = [i for i in range(1,101)]
list2 = [list1[i:i+3] for i in range(0,101,3)]
print(list2)
生成器 |
生成器算得上是Python语言中最吸引人的特性之一,生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅。代码3远没有代码1简洁,生成器(yield)既可以保持代码1的简洁性,又可以保持代码3的效果。它不需要再像上面的类一样写 __iter__() 和 __next__() 方法了,只需要一个 yiled 关键字。 生成器有如下特征是它一定也是迭代器(反之不成立),因此任何生成器也是以一种懒加载的模式生成器。用生成器来实现斐波那契数列的例子是:
def fib(max):
n, before, after = 0, 1, 1
while n < max:
yield before
before, after = after, before + after
n += 1
b = fib(8)
print(b) # <generator object fib at 0x000001B30ADFDF68>
for num in b:
print(num)
fib 就是一个普通的python函数,它特殊的地方在于函数体中没有 return关键字,函数的返回值是一个生成器对象。当执行 f=fib(5) 返回的是一个生成器对象,此时函数体中的代码并不会执行,只有显示或隐示地调用next的时候才会真正执行里面的代码。
yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个generator,在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
也可以手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法),这样我们就可以更清楚地看到 fab 的执行流程:
print(next(b)) # 1
print(next(b)) # 1
print(next(b)) # 2
print(next(b)) # StopIteration
需要明确的就是生成器也是iterator迭代器,因为它遵循了迭代器协议.
第二中创建方式如下:
g = (i for i in ragne(4))
2、生成器函数中带有return语句
在一个生成器中,如果没有return,则默认执行到函数完毕;如果遇到return,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代.
def f():
yield 5
print("ooo")
return
yield 6
print("ppp")
f=f()
# print(f.__next__())
# print(f.__next__())
for i in f:
print(i)
'''
return即迭代结束
for不报错的原因是内部处理了迭代结束的这种情况
'''
3、读取文件
def read_file():
with open('jingdong.txt','r',encoding='utf-8') as f:
while True:
data = f.read(1)
if data:
yield data
else:
return
f = read_file()
for s in f:
print(s)
如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。
注:生成器对象就是一种特殊的迭代器对象,满足迭代器协议,可以调用next;对生成器对象for 循环时,调用iter方法返回了生成器对象,然后再不断next迭代,而iter和next都是在yield内部实现的
3、练习1:使用文件读取,找出文件中最长的行的
print(max((len(x) for x in open('jingdong.txt','r',encoding='utf-8'))))
4、send()函数文章来源:https://www.toymoban.com/news/detail-435530.html
def f():
print("ok1")
s1 = yield 7
print('ok2')
s2 = yield 8
print('ok3')
b = f()
print(b.send(None)) # ok1,7,相当于next(b)
print(next(b)) # ok2,8
print(next(b)) # 报错 StopIteration
5、协程应用:文章来源地址https://www.toymoban.com/news/detail-435530.html
所谓协同程序也就是是可以挂起,恢复,有多个进入点。其实说白了,也就是说多个函数可以同时进行,可以相互之间发送消息等。
def consumer(name):
print('{}准备开始吃包子了'.format(name))
while True:
num = yield
print('{}吃了第{}个包子'.format(name, num))
def product(name):
c1 = consumer('A')
c2 = consumer('B')
next(c1)
next(c2)
print('{}准备开始做包子了'.format(name))
for i in range(1, 11, 2):
print('已经做好了2个包子')
c1.send(i)
c2.send(i + 1)
product('zs')
可迭代对象iterable |
如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。list、dict、set、tupple、str都是可迭代对象
from collections import Iterable,Iterator
x = [1,2,3]
x1 = iter(x)
print(isinstance(x,Iterable)) # True
print(isinstance(x,Iterator)) # False
print(isinstance(x1,Iterable)) # True
print(isinstance(x1,Iterator)) # True
迭代器iterator |
1、任何实现了 __next__() (python2中实现 next() )方法的对象都是迭代器
2、可迭代对象中只能使用iter()方法,迭代器中可以使用next()、iter()方法
for i in [12,3,4]:
print(i)
说明:for循环实现逻辑
1、先调用iter()方式生成一个迭代器
2、每次调用next()方法获取下一个值
3、自带捕获异常功能
到了这里,关于深浅拷贝、函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!