前言时刻:
等会再写吧,刚刚写完一篇博客,我先写信息安全读书的笔记吧!
写完了,开始写day11的总结吧
总结一波:今天学习了迭代对象、迭代器、函数名的用法、以及默认参数的坑、全局变量的修改和定义
1、迭代对象
迭代对象和迭代器,两个很容易搞混,我之前一直以为两个是一样的。直到昨天听老师讲了一遍,仿佛打开了一扇门😂,瞬间明白了。
Python 中的迭代对象内部都有一个 __iter__
方法,这是一个重要的标志。python 中一切皆对象,可认为迭代对象是一个可迭代重复输出的对象。常见的有:str、list、tuple、dict、set、range
这里我们使用 dir 打印对象的所有方法,看 __iter__
是否在里面,
>>> name = "python"
>>> dir(name)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
>>> "__iter__" in dir(name)
True
可以看出字符串中是有 __iter__
方法的,同理可以测试列表、元组、字典等。
1.1 迭代对象的优缺点
迭代对象优点:可以直观显示内部数据,有很多方法,访问速度快。迭代对象缺点:占用存储空间大,不可以直接使用 for 循环,属于典型的用空间复杂度换时间复杂度。
但是你可能会说为啥字符串、列表、元组等都是可以使用 for 循环的,这是因为当你使用 for 进行循环迭代对象的时候,Python 内部会默认将其转成迭代器进行迭代循环。不懂迭代器吗,那么请看下面:
2、迭代器
从字面上理解就是可迭代的工具,其最大的优点在于节省内存空间,每次仅迭代读取其中一部分内容,但是相对费时间,典型的用时间复杂度换空间复杂度。并且只能一步一步向下迭代,不可逆向迭代。
怎么判断是不是迭代器呢?答:看是否包含有 __iter__
和 __next__
方法。目前已知的迭代器是读取文件时的句柄f,可以看下面的例子。
>>> with open('test.txt', mode='r', encoding='utf8') as f:
... print(dir(f))
... print('__iter__' in dir(f) and '__next__' in dir(f))
...
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'reconfigure', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'write_through', 'writelines']
True
2.1 迭代对象转成迭代器
通过使用 iter()
将迭代对象转成迭代器
name = "python"
item = iter(name) # 将迭代对象 str 类型的字符串转成一个迭代器
# 2、打印迭代器的内容 使用 next 向后读取
print(item.__next__()) # 或者是 等价 next(item)
print(next(item))
# p
# y
3、全局变量和局部变量
3.1 global
操作全局变量可用 global
:
def test(): global age age = 17 print(age, name) # 17 python
test() print(age) # 17
2.
也可在局部中修改全局变量
```python
# 2、局部中修改全局变量
name = "python"
def test():
global name
name = "I love python"
print(name) # I love python
test()
这里需要注意的是,如果不在局部中声明 name 是全局变量,那么就无法修改全局变量,而就变成定义一个叫 name 的局部变量。
3.2 nonlocal
操作局部变量可用 nonlocal
,作用:1.不能修改全局变量,2.用于内层函数对外层函数的数据修改。
例子:
# nonlocal 的用法
def test():
name = "I"
def love():
nonlocal name # 如果此处不声明 nonlocal 的话会报错,找不到 name 变量
name += " love Python"
print(name) # I love Python
love()
test()
4、函数名
这个作为函数的一点补充,函数的名称可以作为:
- 容器内的成员,将函数名放到容器(list、dict等)内。有点类似于 c++ 中的对象数组
- 函数名可以作为函数的参数,
- 函数名也可作为函数的返回值
总的来说,函数名仅仅是一个函数的入口地址,运行时并不做实例化,只有调用时在进行解释并实例化。
def test1():
"""直接调用型"""
print("func test1")
def test2(func):
"""
:params func: 函数名
"""
print("func test2")
func()
def test3():
"""
:return: 函数名
"""
print("func test3")
return test1
func_list = [test1, test2, test3]
print(func_list)
# [<function test1 at 0x7fd89ecd4ee0>, <function test2 at 0x7fd89ecd4dc0>, <function test2 at 0x7fd89ecd4dc0>]
# 1、函数名存在容器中 调用
func_list[0]() # func test1
# 2、函数名作为函数的参数
func_list[1](test1)
"""
func test2
func test1
"""
# 3、函数名作为返回值 调用
func_list[2]()()
"""
func test3
func test1
"""
小试牛刀面试题
注意面试的时候可不会让你拿电脑跑代码的,所以要用心思考。
(1)定义和引用顺序问题:
# 1、默认参数的坑
def func():
print(count)
count = 3
func()
"""你的结果是?
此处报错:UnboundLocalError: local variable 'count' referenced before assignment
原因:你在定义变量的前面引用变量了。
"""
(2)默认参数的坑的题:
# 默认参数的坑 案例2.1
def func(name, lis=[]):
lis.append(name)
print(lis)
func('I')
func('love')
func('python')
"""你的结果是?
['I']
['I', 'love']
['I', 'love', 'python']
"""
结果没想到吧,这是因为函数默认的参数 lis
定义了一个空列表,有存储地址。当你连续调用函数 func
时,传入的参数都添加到 lis 中了,所以就是上面打印的数据了。
弄懂了吗?来来试试第二个题
# 默认参数的坑 案例2.2
def func(name, lis=[]):
lis.append(name)
print(lis)
func('I')
func('love', ['c++'])
func('python')
"""你的结果是?
['I']
['c++', 'love']
['I', 'python']
"""
你会发现和第一题的答案差异还是很大的,为啥呢?首先第二个调用 func ,传入了一个列表设为 lis2 ,这个 lis2 会替换掉函数参数中的 lis 的值。注意是替换而不是覆盖,本身 lis 对应的存储地址还是有的,所以当第三个调用是 func 时,将 “python” 追加到了 lis 中,就变成了 [‘I’, ‘python’]。
总结:
写总结真的很脑子,锻炼自己的总结能力。