Python快速入门03


  1. 迭代器就是用来迭代取值的工具,单纯的重复并不是迭代
    while True:
        msg = input('>> ').strip()
        print(msg)
  2. #!/usr/bin/env python
    # author:lijipeng
    
    hobbies = ['sing','on foot','table tennis']
    
    index = 0
    while index < len(hobbies):
        print(hobbies[index])
        index+=1
    
    
    #①:满足重复
    #②:并且以每次重新赋值后的Index值作为下一次循环的索引进行取值,反复迭代
    #最终可以取尽列表中所有的值
  3. 上述案例用到的是索引取值,那么对于字典、集合等没有索引的类型怎么取值呢,那么就必须要找到一种不依赖索引来进行迭代取值的方式,这就用到了迭代器。
  4. 可迭代对象:内置有__iter__方法的对象都是可迭代对象,比如字符串,列表、元组、字典、集合、打开的文件
  5. 迭代器对象:调用obj.iter()方法返回的就是一个迭代器对象。
  6. 迭代器对象内置有__iter__和__next__方法的对象。
  7. 执行迭代器对象的.iter()方法得到的仍然是迭代器本身,而执行迭代器.next()方法就会计算出迭代器的下一个值。
  8. 迭代器是Python提供的一种统一的不依赖于索引的迭代取值方式,只要存在多个值,无论是序列类型还是非序列类型都可以按照迭代器的方法取值。
  9. #!/usr/bin/env python
    # author:lijipeng
    
    hobbies = ['sing','on foot','table tennis']
    
    i = iter(hobbies)  # 每次都需要重新获取一个迭代器对象
    while True:
        try:
            print(next(i))
        except StopIteration:  # 捕捉异常,然后终止循环
            break
    
    
    for item in hobbies:
        print(item)
    
    # for循环的工作原理:
    # ①、调用可迭代对象hobbies内置__iter__得到一个迭代器对象
    # ②、调用迭代器对象内置的__next__得到一个值
    # ③、将得到的值赋给item,执行循环体完成一次循环
    # ④、一直到捕捉到StopIteration一场,结束迭代
  10. 基于索引的迭代取值,所有的迭代状态都保存到了索引中,基于迭代器的迭代取值,所有的迭代状态都保存在了迭代器中
  11. 迭代器优点:为序列和非序列的类型提供了一种统一的迭代取值方式;惰性计算:在需要的时候迭代器对象才会调用内置的__next__来计算出一个值,也就是说就迭代器本身来说,同一时刻在内存中只有一个值,因此可以存放无限大的数据流。对于列表、字典、元组等容器类型,需要把所有的元素都存放在内存中,受内存大小限制,可以存放的值是有限的。
  12. 迭代器的缺点:除非取尽,否则无法获取迭代器的长度;只能取下一个值,迭代器产生后就是重复执行next方法直到值取尽,否则就会停留在某个位置,等待下一次next。
    #!/usr/bin/env python
    # author:lijipeng
    hobbies = ['sing','on foot','table tennis']
    
    i = iter(hobbies)
    while True:
        try:
            print(next(i))
        except StopIteration:
            break
    
    # i1 = iter(hobbies)
    
    # 两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值
    while True:
        try:
            print(next(i))
        except StopIteration:
            break
  13. 若函数体包含yield关键字,再调用函数,并不会执行函数体代码,得到的返回值即生成器对象
    def myrange(start, stop, step=1):
        print('start')
        while start < stop:
            yield start
            start += step
    
        print('stop')
    
    
    g = myrange(0, 3)
    print(g)
  14. 生成器内置有__iter__,__next__方法,所以生成器本身也是迭代器
  15. yield关键字作用:可以自定义迭代器,并且可以利用yield可以保存函数状态的特性,返回多个值
  16. yield表达式应用:
    def eater():
        print('ready to eat')
        while True:
            food = yield
            print('get the food %s, and ready to eat' %food)
    
    
    g = eater()
    print(g)
    next(g)
    g.send('骨頭')
    g.send('包子')
  17. 针对表达式形式yield,生成器对象必须先初始化一次,让函数挂起在food=yield,然后g.send()方法为函数体传值,g.send(None)等同于next(g)
  18. # 编写装饰器来完成为所有表达式形式yield对应生成器的初始化操作
    
    def init(func):
        def wrapper(*args,**kwargs):
            g = func(*args,**kwargs)
            next(g)
            return g
    
        return wrapper
    
    @init
    def eater():
        print('ready to eat')
        while True:
            food = yield
            print('get the food:%s and ready to eat' %food)
    
    
    g = eater()
    g.send('薯条')
  19. # yield表达式
    def init(func):
        def wrapper(*args,**kwargs):
            res = func(*args,**kwargs)
            next(res)
            return res
        return wrapper
    
    @init
    def eater(name):
        print('%s 开始吃东西了' %name)
        food_list = []
        while True:
            food = yield food_list
            food_list.append(food)
            # print('%s 吃了 %s'  %(name, food))
    
        print('%s 已经吃完了哈哈')
    
    
    g = eater('张三')
    # next(g)
    print(g.send('骨头'))
    print(g.send('馒头'))
    print(g.send('大力'))
    print(g.send('小李'))
  20. 生成器本质上也是迭代器所以得到生成器后可以用for进行迭代循环,有yield的函数的返回值是一个生成器对象,每next一次就会把函数挂起在yield的位置,yield后面的东西就是该函数的返回值,也就是执行next后的返回值。send一次返回一次。由于调用send方法需要初始化next方法一次,所以可以通过给函数加一层装饰器,简化操作。yield也可以返回多次值,形式为变量名=yield 值。
  21. 三元表达式
    # 三元表达式
    
    x = 1
    y = 2
    
    res = x if x > y else y
    print(res)
  22. 列表生成式
    egg_list = []
    
    for i in range(1,10):
        egg_list.append('%s个鸡蛋' %i)
    
    print(egg_list)
    
    egg_list = ['%s个鸡蛋' %i for i in range(1,10)]
    print(egg_list)
  23. 创建生成器对象有两种方式:一种是调用带yield关键字的函数,另一种就是生成器表达式。
  24. l = [x*x for x in range(1,3)]
    print(l)
    
    g = (x*x for x in range(1,3))
    print(g)
    print(next(g))
    print(next(g))
    # print(next(g))   抛出异常
    # 对比列表生成式,生成器表达式的优点是节省内存(一次只产生一个值在内存中)
    # 如果我们要读取一个大文件的字节数,应该基于生成器表达式的方式来完成
  25. python 可以设置递归深度,同事Python不是一门函数式编程语言,无法对递归进行尾递归优化
    import os
    import sys
    # sys.setrecursionlimit(10000) 设置递归深度
    
     
  26. 什么时候用递归呢?在你不知道要调用多少层的情况下可以考虑用递归
    items = [[1, 2], 3, [4, [5, [6, 7]]]]
    
    
    def foo(items1):
        for i in items:
            if isinstance(i, list):
                foo(i)
            else:
                print(i, end=' ')
    
    
    foo(items)
  27. 编程的世界里常见的流派有哪些呢?面向过程、函数式、面向对象
  28. 面向过程的优缺点:将复杂的问题流程化,进而简单化,可扩展性差
  29. 面向过程的应用场景:功能一旦实现就很少需要改变,或者做一次性任务,如果是复杂的,且需要不断迭代和维护,面向过程就不适用了
  30. 函数式编程注重的是执行结果而非执行的过程,python不是一门函数式编程的语言,但是仍然为我们提供了很多函数式编程好的特性,如lambda,map,reduce,filter。
  31. lambda匿名函数调用方式:第一种是直接匿名函数后加(实际参数),第二种是将匿名函数赋值给另外一个函数,然后再用新的函数名调用传参
    def add(x, y, z):
        return x + y + z
    
    
    res = (lambda x, y, z:x+y+z)(1,2,3)
    print(res)
    
    func = lambda x, y, z:x+y+z
    # print(func)  匿名的本质就是要没有名字,所以此处为匿名函数指定名字是没有任何意义的,打印该函数名 at 0x019B4738>
    res = func(1,2,3)
    print(res)
  32. map示例第一个参数是函数,第二个参数是可迭代对象
    dic = {
        'li': 1000,
        'wang': 2000,
        'seen': 300,
        'niu': 100
    }
    
    print(max(dic, key=lambda k: dic[k]))
    print(min(dic, key=lambda k: dic[k]))
    
    array = [1, 2, 3, 4, 5]
    
    res = map(lambda x: x * x, array)
    print(list(res))
    
    res = map(lambda x: x * 2, array)
    print(list(res))
    
    # map函数得到的结果仍然是迭代器
  33. reduce 第一个参数是函数,第二个参数是可迭代对象,第三个参数是初始值
    from functools import reduce
    
    array=[1,2,3,4,5]
    
    res = reduce(lambda x,y:x+y,array,10)
    print(res)
  34. filter
    from functools import reduce
    
    array=[1,2,3,4,5]
    
    
    
    res = filter(lambda x:x>3,array)
    # filter过滤后的数据也是一个迭代器对象
    print(list(res))
  35. 模块的作用:保证了代码的重用性和程序的结构性和可维护性 

  36. 首次导入模块会做哪三件事情:执行源代码文件;产生一个新的名称空间用来存放源文件执行过程中产生的名字;在当前执行文件所在的名称空间中得到一个名字foo,该名字指向新创建的模块名称空间,若要引用模块名称空间的名字,需要加上该前缀。
  37. 模块的导入顺序为:内置模块,第三方模块,自定义模块
  38. 函数内导入模块则属于局部作用域
  39. from 模块名 import * 只能在模块最顶层使用的方式导入,在函数内非法
  40. # import foo
    # foo.get()
    # foo.change()
    # foo.get()
    # foo.Foo().func()
    
    from foo import x
    from foo import get
    # x = 2
    # x = '你好呀'
    # a = x
    # print('a:%s' %a)
    y = 4
    def get():
        print('你妈呀')
    get()
  41. __all__ = [x] # 在这里定义了*的范围
    x=1
    def get():
        print(x)
    def change():
        global x
        x=0
    class Foo:
        def func(self):
           print('from the func')
  42. from foo import *
    
    get() # 所以在这里调用是会报错的
  43. 起别名的好处:精简代码;避免与当前名字冲突,可以保持调用方式的一致性
    if data_format == 'json':
        import json as serialize #如果数据格式是json,那么导入json模块并命名为serialize
    elif data_format == 'pickle':
        import pickle as serialize #如果数据格式是pickle,那么导入pickle模块并命名为serialize
        
    data=serialize.load(fn) #最终调用的方式是一致的
  44. 循环导入案例1:直接运行run.py
    print('正在导入m1')
    from m2 import y
    
    x='m1'
    print('正在导入m2')
    from m1 import x
    
    y='m2'
    import m1
  45. 循环导入案例2:直接运行m1.py
  46. 循环导入案例3:将导入操作放在赋值的后边,完美解决
    print('正在导入m1')
    x='m1'
    from m2 import y
    print('正在导入m2')
    
    
    y='m2'
    from m1 import x
    import m1
  47. 循环导入案例4:将导入的语句放在函数内,只要在调用函数时才会执行其内部代码
    print('正在导入m1')
    
    def f1():
        from m2 import y
        print(x,y)
    
    x = 'm1'
    
    # 文件:m2.py
    print('正在导入m2')
    
    def f2():
        from m1 import x
        print(x,y)
    
    y = 'm2'
    
    # 文件:run.py内容如下,执行该文件,可以正常使用
    import m1
    
    m1.f1()
  48. sys.path被称为模块的搜索路径,它是一个列表类型
    import sys
    print(sys.path)
  49. import sys
    sys.path.append(r'C:/Users/Administrator/PycharmProjects/digui/haha/xixi')
    import foo
    
    print(sys.path)
    #foo.py
    ...
    if __name__ == '__main__':
        foo.py被当做脚本执行时运行的代码
    else:
        foo.py被当做模块导入时运行的代码
  50. #!/usr/bin/env python #通常只在类unix环境有效,作用是可以使用脚本名来执行,而无需直接调用解释器。
    
    "The module is used to..." #模块的文档描述
    
    import sys #导入模块
    
    x=1 #定义全局变量,如果非必须,则最好使用局部变量,这样可以提高代码的易维护性,并且可以节省内存提高性能
    
    class Foo: #定义类,并写好类的注释
        'Class Foo is used to...'
        pass
    
    def test(): #定义函数,并写好函数的注释
        'Function test is used to…'
        pass
    
    if __name__ == '__main__': #主程序
        test() #在被当做脚本执行时,执行此处的代码
  51. 包就是一个含有__init__.py文件的文件夹,文件夹内可以组织子模块或子包
  52. 创建包的目的是为了被导入使用,包只是模块的一种形式而已,包的本质就是一种模块
  53. 导入包会做三件事情:执行包下的__init__.py文件;产生一个新的名称空间用来存放__init__.py执行过程中产生的名字;在当前执行文件所在的名称空间中得到一个名字pool,该名字指向__init__.py的名称空间,导入包时并不会导入包下所有的子模块与子包。
  54. 1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如import 顶级包.子包.子模块,但都必须遵循这个原则。但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。 2、包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间 3、import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件
  55. 1、相对导入只能在包内部使用,用相对导入不同目录下的模块是非法的

    2、无论是import还是from-import,但凡是在导入时带点的,点的左边必须是包,否则语法错误