前言时刻:

等会再写吧,刚刚写完一篇博客,我先写信息安全读书的笔记吧!

写完了,开始写day11的总结吧

总结一波:今天学习了迭代对象、迭代器、函数名的用法、以及默认参数的坑、全局变量的修改和定义

1、迭代对象

迭代对象和迭代器,两个很容易搞混,我之前一直以为两个是一样的。直到昨天听老师讲了一遍,仿佛打开了一扇门😂,瞬间明白了。

Python 中的迭代对象内部都有一个 __iter__ 方法,这是一个重要的标志。python 中一切皆对象,可认为迭代对象是一个可迭代重复输出的对象。常见的有:str、list、tuple、dict、set、range

这里我们使用 dir 打印对象的所有方法,看 __iter__ 是否在里面,

  1. >>> name = "python"
  2. >>> dir(name)
  3. ['__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']
  4. >>> "__iter__" in dir(name)
  5. True

可以看出字符串中是有 __iter__ 方法的,同理可以测试列表、元组、字典等。

1.1 迭代对象的优缺点

迭代对象优点:可以直观显示内部数据,有很多方法,访问速度快。迭代对象缺点:占用存储空间大,不可以直接使用 for 循环,属于典型的用空间复杂度换时间复杂度。

但是你可能会说为啥字符串、列表、元组等都是可以使用 for 循环的,这是因为当你使用 for 进行循环迭代对象的时候,Python 内部会默认将其转成迭代器进行迭代循环。不懂迭代器吗,那么请看下面:

2、迭代器

从字面上理解就是可迭代的工具,其最大的优点在于节省内存空间,每次仅迭代读取其中一部分内容,但是相对费时间,典型的用时间复杂度换空间复杂度。并且只能一步一步向下迭代,不可逆向迭代。

怎么判断是不是迭代器呢?答:看是否包含有 __iter____next__ 方法。目前已知的迭代器是读取文件时的句柄f,可以看下面的例子。

  1. >>> with open('test.txt', mode='r', encoding='utf8') as f:
  2. ... print(dir(f))
  3. ... print('__iter__' in dir(f) and '__next__' in dir(f))
  4. ...
  5. ['_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']
  6. True

2.1 迭代对象转成迭代器

通过使用 iter() 将迭代对象转成迭代器

  1. name = "python"
  2. item = iter(name) # 将迭代对象 str 类型的字符串转成一个迭代器
  3. # 2、打印迭代器的内容 使用 next 向后读取
  4. print(item.__next__()) # 或者是 等价 next(item)
  5. print(next(item))
  6. # p
  7. # y

3、全局变量和局部变量

3.1 global

操作全局变量可用 global

  1. 可在局部中创建全局变量 ```python

    1、局部中创建全局变量

    name = “python”

def test(): global age age = 17 print(age, name) # 17 python

test() print(age) # 17

  1. 2.
  2. 也可在局部中修改全局变量
  3. ```python
  4. # 2、局部中修改全局变量
  5. name = "python"
  6. def test():
  7. global name
  8. name = "I love python"
  9. print(name) # I love python
  10. test()


这里需要注意的是,如果不在局部中声明 name 是全局变量,那么就无法修改全局变量,而就变成定义一个叫 name 的局部变量。

3.2 nonlocal

操作局部变量可用 nonlocal ,作用:1.不能修改全局变量,2.用于内层函数对外层函数的数据修改。

例子:

  1. # nonlocal 的用法
  2. def test():
  3. name = "I"
  4. def love():
  5. nonlocal name # 如果此处不声明 nonlocal 的话会报错,找不到 name 变量
  6. name += " love Python"
  7. print(name) # I love Python
  8. love()
  9. test()

4、函数名

这个作为函数的一点补充,函数的名称可以作为:

  • 容器内的成员,将函数名放到容器(list、dict等)内。有点类似于 c++ 中的对象数组
  • 函数名可以作为函数的参数,
  • 函数名也可作为函数的返回值

总的来说,函数名仅仅是一个函数的入口地址,运行时并不做实例化,只有调用时在进行解释并实例化。

  1. def test1():
  2. """直接调用型"""
  3. print("func test1")
  4. def test2(func):
  5. """
  6. :params func: 函数名
  7. """
  8. print("func test2")
  9. func()
  10. def test3():
  11. """
  12. :return: 函数名
  13. """
  14. print("func test3")
  15. return test1
  16. func_list = [test1, test2, test3]
  17. print(func_list)
  18. # [<function test1 at 0x7fd89ecd4ee0>, <function test2 at 0x7fd89ecd4dc0>, <function test2 at 0x7fd89ecd4dc0>]
  19. # 1、函数名存在容器中 调用
  20. func_list[0]() # func test1
  21. # 2、函数名作为函数的参数
  22. func_list[1](test1)
  23. """
  24. func test2
  25. func test1
  26. """
  27. # 3、函数名作为返回值 调用
  28. func_list[2]()()
  29. """
  30. func test3
  31. func test1
  32. """

小试牛刀面试题

注意面试的时候可不会让你拿电脑跑代码的,所以要用心思考。

(1)定义和引用顺序问题:

  1. # 1、默认参数的坑
  2. def func():
  3. print(count)
  4. count = 3
  5. func()
  6. """你的结果是?
  7. 此处报错:UnboundLocalError: local variable 'count' referenced before assignment
  8. 原因:你在定义变量的前面引用变量了。
  9. """

(2)默认参数的坑的题:

  1. # 默认参数的坑 案例2.1
  2. def func(name, lis=[]):
  3. lis.append(name)
  4. print(lis)
  5. func('I')
  6. func('love')
  7. func('python')
  8. """你的结果是?
  9. ['I']
  10. ['I', 'love']
  11. ['I', 'love', 'python']
  12. """

结果没想到吧,这是因为函数默认的参数 lis定义了一个空列表,有存储地址。当你连续调用函数 func 时,传入的参数都添加到 lis 中了,所以就是上面打印的数据了。

弄懂了吗?来来试试第二个题

  1. # 默认参数的坑 案例2.2
  2. def func(name, lis=[]):
  3. lis.append(name)
  4. print(lis)
  5. func('I')
  6. func('love', ['c++'])
  7. func('python')
  8. """你的结果是?
  9. ['I']
  10. ['c++', 'love']
  11. ['I', 'python']
  12. """

你会发现和第一题的答案差异还是很大的,为啥呢?首先第二个调用 func ,传入了一个列表设为 lis2 ,这个 lis2 会替换掉函数参数中的 lis 的值。注意是替换而不是覆盖,本身 lis 对应的存储地址还是有的,所以当第三个调用是 func 时,将 “python” 追加到了 lis 中,就变成了 [‘I’, ‘python’]。

总结:

写总结真的很脑子,锻炼自己的总结能力。