'''1.函数的参数:实参与形参 形参:定义函数()中出现的参数 实参:调用函数()中出现的参数 形参拿到实参的值,如果整体赋值(自己改变存放值的地址),实参不会改变,(可变类型)如果修改内部内容,实参会跟着变化 位置实参 - 只能对位置形参赋值 关键字实参 - 可以对所有(不包含可变长位置形参)形参赋值 位置形参 - 必须出现在最前,且必须传值 默认形参 - 出现在位置形参后*前,可以不用传参 可变长位置形参 - 接收所有位置形参、默认形参没有接收完的位置实参 有无默认值关键字形参 - 必须出现在所有位置形参之后,有默认值可以不用传参,没有默认值必须通过关键字实参传值 可变长关键字形参 - 接收所有有名字的关键字形参没有接收完的关键字实参 def fn(a, b=10, *args, c, d=20, e, **kwargs): pass 2.函数的嵌套调用 在一个函数的内部调用另一个函数:在函数内部遇到调用其他函数,就进入其他函数内部,全部走完 回到调用其他函数 的入口3.函数对象 - 直接赋值、可以加()调用、作为其他函数的参、作为函数的返回值、作为容器对象的元素(成员)4.名称空间与作用域 - LEGB:查找顺序LEGB | 加载顺序BGEL 5.函数的嵌套定义 - 闭包 - 函数的嵌套定义:在一个函数内部定义另一个函数,内部的函数就是闭包 - 应用场景: - 延迟执行 - 装饰器6.装饰器'''
带参装饰器
# 为什么要出现带参装饰器def outer(func): # outer与inner之间要使用外部数据 # 可以解决的方案路径,给outer添加参数,但是outer的参数是固定一个,就是被装饰的函数 def inner(*args, **kwargs): res = func(*args, **kwargs) return res return inner# 所以只能使用函数的闭包,通过外层函数给内存函数传递参数的方式def wrap(*arg, **kwargs): def outer(func): # 就可以使用wrap中的*arg, **kwargs,就是要使用的外部数据 def inner(*args, **kwargs): res = func(*args, **kwargs) return res return inner return outera = 10b = 20@wrap(a, b) # @wrap(10, 20) => @outer => fn = outer(fn) => fn = innerdef fn(): pass
WRAPS修改函数文档注释
# 为什么要出现该语法from functools import wrapsdef outer(func): @wraps(func) def inner(*args, **kwargs): res = func(*args, **kwargs) return res return innerdef fn(): ''' fn的文档注释 ''' print(fn.__doc__) # fn本质是inner,使用打印fn.__doc__本质是inner函数的文档注释# 形参假象:让打印fn.__doc__显示的效果是fn自己的
今日内容
'''基础残留:三元表达式,列表字典推导式迭代器:可迭代对象、迭代器对象、for循环迭代器、枚举对象、生成器(自定义的迭代器)内置函数:匿名函数、常用的内置函数模块:模块,包,常用模块'''
三元表达式
# what:就是简写if...else...结构,且都只有一条语句# 语法:结果1 if 条件 else 结果2# 注意:结果1|2不一定要与条件有必然关系,条件只是选择结果1或结果2的判断依据# 案例:获得两个数中的大值 | 小者n1 = int(input('n1: '))n2 = int(input('n2: '))res = n1 if n1 > n2 else n2print(res)res = n2 if n1 > n2 else n1print(res)
列表与字典的推导式
# 列表推导式# 语法:[结果 for 结果 in 可for循环操作的对象]# 案例:[v for v in 'abc'] => ['a', 'b', 'c']# 案例:['奇数' if i % 2 != 0 else '偶数' for i in range(1, 11)]# 字典推导式# 语法:{k: v for k, v in 可for循环操作的对象(每一次循环的结果可以被解压为两个值)}# 案例: [('a', 1), ('b', 2)] => {'a': 1, 'b': 2}# dic = {k: v for k, v in [('a', 1), ('b', 2)]}# 案例:{i: 0 for i in 'abc'} == {}.fromkeys('abc', 0) == {'a': 0, 'b': 0, 'c': 0}
迭代器
可迭代对象
# 有__iter__()方法的对象都称之为 可迭代对象# 可迭代对象:可以被转化为不依赖索引取值的容器,这样的对象就叫做可迭代对象# -- 对象.__iter__() 来生成不依赖索引取值的容器# -- 结论:有__iter__()方法的对象都称之为 可迭代对象# 可迭代对象.__iter__() => 和该对象有关系的迭代器对象 dict_keyiterator objectbox = dic.__iter__() # 可迭代对象有哪些:str | list | tuple | set | dict | range() | enumerate() | file | 生成器对象
迭代器对象
# 有__next__()且可以通过__next__()进行取值的容器# 迭代器对象:可以通过__next__()的方式进行取值的容器,且取一个少一个# -- 结论:有__next__()且可以通过__next__()进行取值的容器# -- 注意:迭代器对象自身也拥有__iter__(), 通过该方法返回的是迭代器对象自身res = box.__next__() # 从迭代器对象(容器)取出值,取一个少一个box = box.__iter__() # 迭代器对象.__iter__()得到迭代器对象本身# 迭代器对象有哪些:enumerate() | file | 生成器对象
for迭代器
# 可以操作迭代器对象及可迭代对象,且能自动处理异常的循环,内部同迭代器对象__next__()来取值# 迭代器(for循环):就是用来从可迭代对象中进行取值的循环方法 | 语法:for 变量 in 对象:# -- 1.通过对象.__iter__()获取其对应的迭代器对象# -- for可以操作迭代器对象及可迭代对象,统一写法,所以迭代器和可迭代对象都有__iter__()# -- 2.在内部通过迭代器对象的__next__()进行取值,将值赋值给 语法中的变量,取一个少一个# -- 3.当迭代器对象取完了,在内部自动捕获异常,并结束循环取值ls = [1, 2, 3, 4, 5]for v in ls: print(v)for v in ls.__iter__(): print(v)
生成器
# 自定义的迭代器对象,写法和函数非常相似,但是内部用yield来对外部返回值,且可以有多个yield# 语法:def my_generator(): # => [1, 2, 3] yield 1 yield 2 yield 3# 生成器名() 不是函数的调用,而是得到生成器对象,生成器对象就是迭代器对象,所有有__next__()方法obj = my_generator()# 一个个取值# 去生成器中执行代码,拿到遇到的第一个yield后面的值,并停止运行print(obj.__next__())# 再接着上一个yield,再进行往下执行代码,再拿到下一个个yield后面的值,并停止运行print(obj.__next__())# 重复上面的过程,如果没有遇到yield,就报错print(obj.__next__())# 循环取值while True: try: print(obj.__next__()) except Exception: break # 案例:# 将传入的值扩大两倍返回def fn1(*args): i = 0 while i < len(args): yield args[i] * 2 i += 1for v in fn1(10, 20, 30, 40, 50): print(v) # 依次获取阶乘 1! 2! 3! ...def fn2(): total = 1 count = 1 while True: total *= count yield total count += 1obj = fn2()print(obj.__next__()) # 1!print(obj.__next__()) # 2!print(obj.__next__()) # 3!# ...# print(obj.__next__()) # n!
了了解:生成器的send
# send:# 1.send会为当前停止的yield传入参数,内部可以通过yield来接收传入的参数# 2.send自身也会调用__next__()去获取下一个yield的结果def fn4(peoples): count = 0 print('%s在面试' % peoples[count]) while count < len(peoples): name = yield peoples[count] count += 1 print(name + "叫来%s来面试" % peoples[count])peoples = ['张三', '李四', '王五']obj4 = fn4(peoples)name = obj4.send(None) # 第一次没有yield接收值,所以只能调__next__(),或是send(None)print(name + '面试完毕')while True: try: name = obj4.send(name) print(name + '面试完毕') except Exception: print('所有人面试完毕') break
枚举对象
# 枚举对象:为迭代器对象产生迭代索引ls = [3, 1, 2, 5, 4]list(enumerate(ls)) # => [(0, 3), (1, 1), (2, 2), (3, 5), (4, 4)]dic = {'a': 100, 'b': 200}print(list(enumerate(dic))) # => [(0, 'a'), (1, 'b')]
递归
# 递归:函数直接或间接调用自己# 回溯:找寻答案的过程# 递推:通过最终的值反向一步步推出最初需要的结果# 前提:# 1.递归条件是有规律的# 2.递归必须有出口# 拿递归求得年纪def get_age(num): if num == 1: return 58 age = get_age(num - 1) - 2 return ageage = get_age(10)print(age)# 传入一个num,求得该num的阶乘# 5! = 5 * 4 * 3 * 2 * 1 = 5 * 4!# 4! = 4 * 3 * 2 * 1 = 4 * 3!# 3! = 3 * 2 * 1 = 3 * 2!# 2! = 2 * 1 = 2 * 1!# 1! = 1def get_total(num): if num == 1 or num == 0: return 1 total = num * get_total(num - 1) # 3 * 2! => 2 * 1!1 => 1 => 2 * 1 return totalprint(get_total(3))
内置函数
匿名函数
# 匿名函数: 没有名字的函数# 特点:# 1.用lambda声明匿名函数# 2.没有函数名,lambda与:之间一定是参数列表,参数列表省略(),且支持所有参数语法# 3.匿名函数没有函数体,只有返回值,所有省略了return,且返回值只能有一个# -- (不能将多个返回值自动格式化为元组)# lambda 参数1, ..., 参数n: 一个返回值# 应用场景:# 1.用一个变量接收,该变量就充当与函数的名字 - 不常见# func = lambda x, y: (x + y, x - y)# print(func(10, 20))# 2.结合内置函数来使用max([1, 2, 6, 5, 3], key=lambda x: x)dic = { 'Bob': (1, 88888), 'Ben': (2, 300000), 'Tom': (3, 99999)}min(dic, key=lambda k: dic[k][1]) # 按薪资求最小值
## 一、装饰器
```python
'''
装饰器:就是闭包(闭包的一个应用场景)
-- 把要被装饰的函数作为外层函数的参数通过闭包操作后返回一个替代版函数
优点:
-- 丰富了原有函数的功能
-- 提高了程序的可拓展性
'''
```
#### 开放封闭原则
```python
'''
1.不能修改被装饰对象(函数)的源代码
2.不能更改被修饰对象(函数)的调用方式
'''
```
#### 装饰器的简单实现
```python
def outer(func):
def inner():
print("新增功能1")
func()
print("新增功能2")
return inner
def func():
print("原有功能")
func = outer(func)
```
#### 装饰器语法糖
```python
def outer(func):
def inner():
print("新增功能1")
func()
print("新增功能2")
return inner
@outer
def func():
print("原有功能")
```
#### 装饰有参有返的函数
```python
def outer(func):
def inner(*args, **kwargs):
print("新增功能1")
result = func(*args, **kwargs)
print("新增功能2")
return result
return inner
@outer
def func(*args, **kwargs):
print("原有功能")
return "原有结果"
```
#### 有参装饰器
```python
def wrap(arg):
def outer(func):
def inner(*args, **kwargs):
print("新增功能1")
result = func(*args, **kwargs)
print("新增功能2")
return result
return inner
@wrap("装饰器参数")
def func(*args, **kwargs):
print("原有功能")
return "原有结果"
```
### wraps修改函数文档注释
```python
from functools import wraps
def outer(func):
@wraps(func)
def inner(*args, **kwargs):
'''装饰器文档注释'''
func(*args, **kwargs)
return inner
@outer
def func(*args, **kwargs):
'''原有文档注释'''
print("原有功能")
```
## 二、迭代器
```python
# 迭代器:循环反馈的容器(集合类型)
# -- 不同于索引取值,但也可以循环的从容器对象中从前往后逐个返回内部的值
# 优点:不依赖索引,完成取值
# 缺点:不能计算长度,不能指定位取值(只能从前往后逐一取值)
```
#### 可迭代对象
```python
# 可迭代对象:内置有__iter__()方法的对象,称之为可迭代对象
# 可迭代对象:str | list | tuple | set | dict | range() | enumerate() | file | 生成器对象
```
#### 迭代器对象
```python
# 迭代器对象:内置有__next__()方法的对象,称之为迭代器对象,可以通过调用__next__()方法完成取值
# 迭代器对象:enumerate() | file | 生成器对象
# 注:迭代器对象调用__iter__()方法返回的还是迭代器对象
```
#### 迭代器
```python
# 迭代器:for循环
# 工作原理:
# -- 1.获取可迭代对象的迭代器对象
# -- 2.循环__next__()取值
# -- 3.异常处理(StopIteration),停止迭代
```
#### 生成器
```python
# 生成器:一次生成一个值得容器(函数类型)
# -- 内部包含yield关键字的函数
# 注:函数的调用不会执行函数,而是返回生成器对象
```
#### 生成器对象
```python
# 生成器对象:就是一个迭代器对象
# -- 可以通过__next__()方法取值,得到yield关键字的返回值
# -- 可以调用send()方法给yield关键字传值,内部接收yield关键字可以得到传入的值
```
#### 枚举对象
```python
# 枚举对象:通过enumerate()方法,可以为可迭代对象生成迭代索引,其本身也是一个迭代器对象
```
## 三、知识点补充
#### 三元表达式
```python
# 结果1 if 条件 else 结果2
```
#### 列表推导式
```python
[i for i in range(10)]
```
#### 字典推导式
```python
{k, v for k, v in enumerate('abc')}
```
## 四、递归
```python
# 函数的直接或间接自调用
```
## 五、内置函数
#### 匿名函数
```python
# lambda 参数1, ..., 参数n: 返回值
```
#### 内置函数
```python
# https://docs.python.org/zh-cn/3.7/library/functions.html
```
## 六、模块与包
#### 模块
```python
'''
模块:一系列功能的集合体
常见的四种模块:
1.使用python编写的.py文件
2.把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
3.使用C编写并链接到python解释器的内置模块
4.已被编译为共享库或DLL的C或C++扩展
'''
```
#### 模块的搜索路径
```python
'''
搜索顺序:内存 => 内置模块 => sys.path
1.导入模块会优先在内存中查找
2.内存中没有被加载的话,再去查找内置模块
3.还没有查找到,就根据sys.path中的路径顺序逐一查找
'''
```
#### 模块导入的执行流程
```python
'''
导入模块的指令:
-- 相对于 函数名() 调用函数体,函数调用会进入函数体,从上至下逐句解释执行函数体代码
-- 导入模块,会进入模块文件,从上至下逐句解释执行模块文件代码
-- 如果在模块中又遇到导入其他模块,会接着进入导入的模块,从上至下逐句解释执行文件中代码,依次类推
'''
```
#### 循环导入
```python
'''
模块之间出现了环状导入,如:m1.py 中导入了m2,m2.py 中又导入了m1
循环导入的问题:
-- 导入模块是要使用模块中的变量
-- 正常逻辑都是在文件最上方先完成对模块的导入,再在下方定义自身模块变量,以及使用导入的模块中的变量
-- 由于导入模块的特殊机制,第一次导入模块会编译执行导入的模块,也就是会进入模块逐句执行模块内容,再次导入只是使用内存中的名字
-- 就会出现下面的情况,m2在使用m1中的变量x,但变量x却并未产生,这就出现了循环导入问题
m1.py文件
import m2
x = 10
print(m2.y)
m2.py文件
import m1
y = 10
print(m2.x)
解决循环导入的问题:延后导入
1、将循环导入对应包要使用的变量提前定义,再导入响应的包
2、将导包的路径放倒函数体中,保证存放导包逻辑的函数调用在要使用的变量定义之后
重点:
问题:from导包极容易出现循环导入问题
解决:取消from导入方式,采用import导入方式
'''
```
#### 包
```python
'''
一系列功能模块的集合体
-- 包就是管理功能相近的一系列模块的文件夹
-- 该文件夹包含一个特殊文件__init__.py
-- 文件夹名就是包名,产生的包名就是指向__init__.py的全局名称空间
导包完成的三项事:
1.编译执行包中的__init__.py文件,会在包中__pycache__创建对应的pyc文件
2.产生__init__.py文件的全局名称空间,用来存放__init__出现的名字
3.产生包名指向__init__.py文件的全局名称空间 | 指定变量名指向包中指定名字
'''
```
#### 包中模块的使用:import
```python
'''
module文件夹
-- __init__.py
-- m1.py
test.py文件
import module
# 在该文件中使用包
'''
# 1.__init__.py文件中产生的普通名字可以直接使用
'''
__init__.py
x = 10
test.py
print(module.x)
'''
# 2.管理的模块中出现的名字,要通过 包名.模块名 间接使用
'''
m1.py
num = 10
__init__.py
import module.m1
test.py
print(module.m1.num)
'''
```
#### 包的嵌套
```python
# 在包中再定义包
# 连包的导入
import 父包.子包
# 重点:导包的.语法,在所有点左侧都必须是包
# 正确案例:
import 父包.子包
import 父包.子包.模块
# 错误案例
import 父包.子包.模块.名字
```
#### 包中模块的使用:from...import
```python
'''
使用规则与import差不多,但是导包的.语法需严格执行,就是所有点左侧都必须是包
'''
```
#### 导包的两种方式
```python
# 绝对导入:通过sys.path方式来实现
# 相对导入:通过包内.语法来实现
```
#### 绝对导入
```python
# 将对应的文件夹添加至sys.path中,就可以直接导入对应文件夹下的模块
```
#### 相对导入
```python
# 相对导入是存在于包内的语法
# .代表当前文件夹
# ..代表上一级文件夹
# 存在.语法的文件,不能作为执行文件
```
来源:https://www.cnblogs.com/qiangyuzhou/p/10787221.html