Python之路day11-迭代器_迭代对象_函数名_global_nonlocal

200次阅读
没有评论

前言时刻:

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

写完了,开始写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

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

    # 1、局部中创建全局变量
    name = "python"
    
    def test():
       global age
       age = 17
       print(age, name)  # 17 python
    
    test()
    print(age)   # 17
  2. 也可在局部中修改全局变量

    # 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.1def func(name, lis=[]):    lis.append(name)    print(lis)func('I')func('love')func('python')"""你的结果是?['I']['I', 'love']['I', 'love', 'python']"""

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

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

# 默认参数的坑 案例2.2def 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']。

总结:

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

1
西园公子
版权声明:本站原创文章,由西园公子2021-06-10发表,共计6060字。
转载提示:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
载入中...