默认参数的陷阱:
# 正常 def func(name,sex='男'): print('name') print(sex) func('小杨')
陷阱只针对于默认参数是可变的数据类型。如果你的默认参数指向的是可变的数据类型,那么你无论调用多少次这个默认参数,都是同一个,id也相同。
def func(a, list=[]): list.append(a) return list print(func(10)) # 第一次,默认参数没有改变,里面的值传入10 print(func(100)) # 第二次,默认参数没有改变,里面的值还是默认的时候第一次的值 print(func(20, [])) # 第三次,默认参数从新传值了,就变成了一个新的列表从新传值 print(func(200)) # 第四次,默认参数没有改变,里面的值还是之前第一次和第二次的值。 # 输出 [10] [10, 100] [20] [10, 100, 200]
局部作用域的坑:在函数中,如果你定义了一个变量,但是在定义这个变量之前对其引用了,那么解释器认为:语法问题。
你应该在使用之前先定义。
count = 1 def func(): print(count) count = 3 func() # 报错 UnboundLocalError: local variable 'count' referenced before assignment
global
在局部作用域声明一个全局变量。
# name = '小红' # 注释掉全局作用域下的name变量 def func(): global name # 在局部声明name为全局变量 name = '小杨' # 不声明时,name为局部作用域的变量 print(name) # 会先在局部先查这个变量,没有就会去全局下查 func() # 要先执行函数才会声明name为全局变量 print(name) # 在打印name时才能找到。 # 输出 小杨 小杨
修改一个全局变量
count = 1 # 全局定义一个count def func(): global count # 定义count为全局变量 count += 1 # count才能自加1 func() # 要先执行函数,count才能自加1 print(count) # 输出 2
nonlocal
不能够操作全局变量。
局部作用域:内层函数对外层函数的局部变量进行修改。
def wrapper(): count = 1 # 在外层局部定义个变量 def inner(): nonlocal count # 使内层函数能够操作外层函数的局部变量 count += 1 # 外层函数count自加1 print(count) # 此时打印的还是外层没有自加1的count inner() # 执行内层函数,时外层函数自加1 print(count) # 此时在打印的是外层函数count自加1后的值 wrapper() # 输出 1 2
二、函数名的运用
函数名指向的是函数的内存地址。函数名 + ()就可以执行此函数。
函数名就是变量。
def func(): print(666) f = func f1 = f f2 = f1 f() f1() f2() # 输出 666 666 666
函数名可以作为容器类数据类型的元素。
def func1(): print('in func1') def func2(): print('in func2') def func3(): print('in func3') l1 = [func1, func2, func3] for i in l1: i() # 输出 in func1 in func2 in func3
函数名可以做为函数的参数。
def func(): print('in func') def func1(x): x() # 相当于func() print('in func1') func1(func) # 输出 in func in func1
函数名可以作为函数的返回值。
def func(): print('in func') def func1(x): # 打印func1并返回x print('in func1') return x ret = func1(func) # 打印func2并返回func,并把返回的func赋值给ret ret() # 相当于 func() # 输出 in func1 in func
三、迭代器:
可迭代对象
字面意思
对象?:python中一切皆对象。(一个实实在在存在的值)。
可迭代?:更新迭代。重复的,类似于循环的一个过程,更新迭代每次都有新的内容,
可以进行循环更新的一个实实在在的值。
专业角度:可迭代对象?内部含有__iter__
方法的对象,可迭代对象
目前所学过的可迭代对象?:str,list,tuple,dict,set,range,文件句柄。
获取对象的所有方法并且以字符串的形式表现出来
dir()
l1 = [1, 2, 3, 4, 5] print(dir(l1)) # 输出: ['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
判断一个对象是否是可迭代对象
判断__iter__
是否在dir(对象)
l1 = [1, 2, 3, 4, 5] print('__iter__' in dir(l1)) # 输出: True # 是可迭代对象
小结
字面意思:可以进行循环更新的一个实实在在的值。
专业角度:内部含有__iter__
方法的对象,可迭代对象。
判断一个对象是不是可迭代对象:判断__iter__
是否在dir(对象)
优点:
1、存储的数据直接能显示,比较直观。
2、拥有的方法比较多。操作方便。
缺点:
1、占内存。
2、不能直接通过for循环,不能直接取值索引,key。(为什么以前可以用呢?因为,for循环在里面做了迭代器。)
迭代器
字面意思:更新迭代,器:工具,可更新迭代的工具。
专业角度:内部含有__iter__
方法并且含有__next__
方法的对象就是迭代器。
判断对象是否是迭代器
可以判断是否是迭代器:__iter__
and__next__
在不在 dir(对象)。
with open('文件1', encoding='utf-8', ) as f1: print('__iter__' in dir(f1) and '__next__' in dir(f1)) # 输出: True
迭代器的取值
s1 = 'abcdefg' obj = iter(s1) # 或者s1.__iter__() print(next(obj)) # print(obj.__next__) 一个就相当于取第一个的值 print(next(obj)) # print(obj.__next__) 两个就相当于取第二个的值 print(next(obj)) # print(obj.__next__) 三个就相当于取第三个的值 # 输出: a b c
可迭代对象如何转化成迭代器
s1 = 'abcdefg' obj = iter(s1) # 或者s1.__iter__() 把 s1 转换成迭代器 obj print('__iter__' in dir(s1) and '__next__' in dir(s1)) # s1 不是迭代器 False print('__iter__' in dir(obj) and '__next__' in dir(obj)) # obj 是迭代器 True # 输出: False True
while循环模拟for循环机制
1、将可迭代对象转换成迭代器。
2、利用next对迭代器进行取值
3、当爆错的时候,就跳出循环。
l1 = [11, 22, 33, 44, 55, 66, 77, 88, 99] obj = iter(l1) # 将可迭代对象转换成迭代器。 while 1: try: print(next(obj)) # 利用next对迭代器进行取值 except StopIteration: break # 当爆错的时候,就跳出循环。报错是因为它循环到第10次列表就没有了 # 输出: 11 22 33 44 55 66 77 88 99
小结
字面意思:更新迭代,器:工具,可更新迭代的工具。
专业角度:内部含有__iter__
方法并且含有__next__
方法的对象就是迭代器。
优点:
1、节省内存。
2、惰性机制。next一次,取一个值,绝不多取。
缺点:
1、速度慢
2、不走回头路。
可迭代对象与迭代器的对比
可迭代对象是一个操作方法比较多,比较直观,存储数据相对少(几百万个对象,8G内存是可以承受的)的一个数据集。
应用:当你侧重于对对于数据可以灵活处理,并且内存空间足够,将数据集设置为可迭代对象是明确的选择
可迭代对象每次循环都会从头开始
l1 = [11, 22, 33, 44, 55, 66, 77, 88] count = 0 for i in l1: if count > 3: break else: print(i) count += 1 print('---------------------------------') count = 0 for i in l1: if count > 4: break else: print(i) count += 1 # 输出 11 22 33 44 --------------------------------- 11 22 33 44 55
是一个非常节省内存,可以记录取值位置,可以通过循环 + next 方法取值,但是不直观,操作方法比较单一的数据集。
迭代器每次循环会记住你上次的位置往下循环
l1 = [11, 22, 33, 44, 55, 66, 77, 88] obj = iter(l1) # 先把可迭代对象转换成迭代器 for i in range(3): print(next(obj)) print('--------------------') for i in range(4): print(next(obj)) # 输出 11 22 33 -------------------- 44 55 66 77