函数名-函数参数坑-迭代器

一、关键字:global nonlocal

默认参数的陷阱:

# 正常
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 方法取值,但是不直观,操作方法比较单一的数据集。

应用:当你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,将数据集设置为迭代器是一个不错的选择。(可参考为什么python把文件句柄设置成迭代器)。

迭代器每次循环会记住你上次的位置往下循环

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
学习之旅
原文地址:https://www.cnblogs.com/XiaoYang-sir/p/14666349.html