• Python学习笔记第四周


    目录

      一、基础概念

        1、装饰器

          1、装饰器本质就是函数

          2、实现装饰器知识储备

            1、函数即变量

            2、高阶函数

            3、嵌套函数

          例子

            1、将一个函数名作为实参传递给另外一个函数

            2、返回值为函数名

            3、函数嵌套函数

            4、完整装饰器例子

            5、装饰器高级版本

        2、列表生成器

        3、生成器

        4、斐波那契数列

        5、迭代器

        6、内置函数

        7、json、pickle序列化

        8、软件目录结构规范

    一、基础概念

    1、装饰器:

      1、装饰器本质上就是函数,用来装饰其他函数

      原则:装饰器写好后,原则上不能修改其他函数源代码

        1、不能修改被装饰函数源代码

        2、不能修改被装饰函数调用方式

        总结成一点就是装饰器对被装饰函数完全透明,用这个函数的人完全不知道装饰器的存在

      2、实现装饰器的知识储备

        1、函数即变量

         在python中,变量定义值,在内存中开辟一块空间存放该变量值,当变量名不再引用该变量值后,python解释器会自动回收该值所在内存,函数也是如此,通过函数名调用函数体,当函数名不再引用该函数体,函数体从内存中被解释器回收。

              2、高阶函数

             a)把一个函数名作为实参传递给另外一个函数,在不修改被装饰函数源代码情况下为其添加功能

             b)返回值为该函数名,不修改函数的调用方式

        3、嵌套函数

          在一个函数内通过def声明一个函数

      装饰器=高阶函数+嵌套函数

     例子

      1、将一个函数名作为实参传递给另外一个函数

    def test1(func):  #传入函数名作为实参
        func()
    
    def bar():
        print('in the bar!')
        
    test1(bar)
    #输出:
    in the bar!
    import time
    
    def bar():
        print('in the bar!')
    
    def test1(func):
        start_time = time.time()
        func()# 运行bar函数
        stop_time = time.time()
        print('the func running time is %s'  %(stop_time-start_time))
    
    
    test1(bar)
    #输出:
    in the bar!
    the func running time is 8.58306884765625e-05

      2、返回值为函数名

    import time
    
    def bar():
        print('in the bar!')
    
    def test1(func):
        print(func)
        return func
    
    
    bar = test1(bar)   #将bar函数名作为参数传递给函数test1内,同时返回值为bar函数名而不是bar函数执行结果,并将其重新赋值给bar
    bar() #执行bar()会先执行test1,打印bar对应的内存地址,然后执行bar函数对应的函数体内容
    #输出:
    <function bar at 0x289a464>
    in the bar!

      3、函数嵌套函数

    def foo():
        print('in the  foo')
        def bar():
            print('in the bar')
        return bar() #执行foo时会返回bar函数执行结果
    
    foo()
    #输出:
    in the  foo
    in the bar

      4、完整装饰器例子

    import time
    
    def deco(func):
        def wrapper(*args,**kwargs):  #无论原始函数自身带任何参数均可以在这包含
            start_time = time.time()
            func(*args,**kwargs)   #当test1传入时,执行test1的返回结果,如果源函数携带参数,这里可以在执行原函数时带源函数所带参数
            stop_time = time.time()
            print('the func time is %s'  %(stop_time-start_time))
        return wrapper  #直接返回函数名
    
    @deco   #@deco 等价于       test1 = deco(test1)
    def test1(name):
      time.sleep(3)
      print('in the test1')
      print('the name is %s' %name)
    
    
    test1('gavin')
    #输出:
    in the test1
    the name is gavin
    the func time is 3.0022261142730713
    import time
    
    def deco(func):
        def wrapper(*args,**kwargs):  #无论原始函数自身带任何参数均可以在这包含
            start_time = time.time()
            func(*args,**kwargs)   #当test1传入时,执行test1的返回结果,如果源函数携带参数,这里可以在执行原函数时带源函数所带参数
            stop_time = time.time()
            print('the func time is %s'  %(stop_time-start_time))
        return wrapper  #直接返回函数名
    
    @deco   #@deco 等价于       test1 = deco(test1)
    def test1(name):
      time.sleep(3)
      #print('in the test1')
      #print('the name is %s' %name)
      return name  #返回name值
    
    
    print(test1('gavin'))
    #输出:
    the func time is 3.0015320777893066
    None   #用该方法没法返回源函数需要返回的参数
    import time
    
    def deco(func):
        def wrapper(*args,**kwargs):  #无论原始函数自身带任何参数均可以在这包含
            start_time = time.time()
            res = func(*args,**kwargs)   #当test1传入时,执行test1的返回结果,如果源函数携带参数,这里可以在执行原函数时带源函数所带参数 
            stop_time = time.time()
            print('the func time is %s'  %(stop_time-start_time))
            return res   #当需要被修饰函数有返回值时,可以在装饰器中将其返回
        return wrapper  #直接返回函数名
    
    @deco   #@deco 等价于       test1 = deco(test1)
    def test1(name):
      time.sleep(3)
      #print('in the test1')
      #print('the name is %s' %name)
      return name
    
    
    print(test1('gavin'))
    #输出
    the func time is 3.00175404548645
    gavin

       5、装饰器高级版本:通过装饰器来划分不同的登录认证界面

    #版本一,通过不带参数的auth装饰器来完成home与bbs认证
    user,passwd = 'gavin','123'
    def auth(func):
        def wrapper(*args,**kwargs):
            username = input('usernmae: ').strip()
            password = input('password: ').strip()
            if user == username and passwd == password:
                print('33[32;1mauthticatin passed33[32;0m'.center(50,'@'))
                func(*args,**kwargs)
            else:
                exit('wrong input!')
        return wrapper
    
    
    
    
    
    @auth
    def home():
        print('in the home page!')
    
    @auth
    def bbs():
        print('in the bbs page!')
    
    home()
    bbs()
    #输出:
    usernmae: gavin
    password: 123
    @@@@@@@@@authticatin passed@@@@@@@@@
    in the home page!
    usernmae: gavin
    password: a
    wrong input!
    user,passwd = 'gavin','123'
    def auth(func):
        def wrapper(*args,**kwargs):
            username = input('usernmae: ').strip()
            password = input('password: ').strip()
            if user == username and passwd == password:
                print('33[32;1mauthticatin passed33[32;0m'.center(50,'@'))
                res = func(*args,**kwargs)  #将home函数对应的运行结果返回
                return res
            else:
                exit('33[31;1mwrong input!33[32;0m')
        return wrapper
    
    
    
    
    
    @auth
    def home():
        return 'home page'  #需要装饰器可以返回该返回值
    
    @auth
    def bbs():
        print('in the bbs page!')
    
    print(home())
    bbs()
    #输出:
    usernmae: gavin
    password: 123
    @@@@@@@@@authticatin passed@@@@@@@@@
    home page
    usernmae: gavin
    password: 123
    @@@@@@@@@authticatin passed@@@@@@@@@
    in the bbs page!
    user,passwd = 'gavin','123'
    def auth(auth_type):  #装饰器携带的参数会在第一层传入装饰器
        def outer(func): #装饰器要装饰的源函数会在第二层传入到装饰器
            def wrapper(*args,**kwargs):
                if auth_type == 'local':
                    username = input('usernmae: ').strip()
                    password = input('password: ').strip()
                    if user == username and passwd == password:
                        print('33[32;1mauthticatin passed33[32;0m'.center(50,'@'))
                        res = func(*args,**kwargs)  #将home函数对应的运行结果返回
                        return res
                    else:
                        exit('33[31;1mwrong input!33[32;0m')
                elif auth_type == 'ldap':
                    print('暂时不支持')
                    func(*args,**kwargs)
            return wrapper
        return outer
    
    
    
    
    
    @auth(auth_type = 'local')
    def home():
        return 'home page'  #需要装饰器可以返回该返回值
    
    @auth(auth_type = 'ldap')
    def bbs():
        print('in the bbs page!')
    
    print(home())
    bbs()
    #输出
    usernmae: gavin
    password: 123
    @@@@@@@@@authticatin passed@@@@@@@@@
    home page
    暂时不支持
    in the bbs page!

    2、列表生成器

    >>> a = [ i * 2 for i in range(10)]
    >>> a
    [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

    3、生成器:generator

    为了避免列表中数据过多占用太多内存空间,导致系统不可用,使用生成器来替代列表产生数据序列

    >>> b = (i * 2 for i in range(10))
    >>> b
    <generator object <genexpr> at 0x10223d7d8> 

    此时b为生成器,只有在调用的时候才会产生数据,所以使用通常的b[5]调用列表的方法是没法得到数据的

    >>> b[2]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'generator' object is not subscriptable

    生成器只有在调用时才会产生相应的数据,同时只记录当前的位置,只有通过__next__()方式或者next()内置函数才能调用

    >>> b.__next__()
    0
    >>> b.__next__()
    2
    >>> b.__next__()
    4
    >>> c = next(b)
    >>> print(c)
    6

    >>> b = (i * 2 for i in range(10)) 

    >>> for n in b:print(n) #也可以使用for循环一次性全部取出,这样可以不用使用next方法同时避免最后遇到StopIteration错误
    ...
    0
    2
    4
    6
    8
    10
    12
    14
    16
    18

    4、斐波那契数列

    fibonacci除了第一个和第二个数外,其他任意数都是前面两个数相加得到的

    def fib(max):
        n, a, b = 0, 0, 1
        while n < max:
            yield b  #yield类似断点,执行到这里会跳出生成器,然后下一次进入生成器会从这个位置进入继续执行
            a, b = b, a + b  #yield会保存函数的中断状态
            n = n + 1
        return 'Done'
    
    res = fib(6)
    for n in res:   #同样斐波那契函数也可以使用for循环方式取到所有想要的数值,同时也避免使用next方法,但是有一点for循环没法做到,就是返回return的值
        print(n)
    #输出
    1
    1
    2
    3
    5
    8

    如果想要取到return返回的值,需要使用下面的方法

    def fib(max):
        n, a, b = 0, 0, 1
        while n < max:
            yield b  #yield类似断点,执行到这里会跳出生成器,然后下一次进入生成器会从这个位置进入继续执行
            a, b = b, a + b  #yield会保存函数的中断状态
            n = n + 1
        return 'Done'
    
    res = fib(6)
    
    while True:
        try:
            x = next(res)  #next()为内置函数,类似于__next__()
            print('res:', x)
        except StopIteration as e:  #如果想要获得return返回的值,必须抓到StopIteration错误
            print('Generator return valuel:', e.value)
            break #当结束后会进入exception,同时需要跳出该循环
    #输出
    res: 1
    res: 1
    res: 2
    res: 3
    res: 5
    res: 8
    Generator return valuel: Done

    通过协程方法达到多线程效果:

    #版本一:单独传入next与send,查看yield与send方法使用
    import time
    
    def consumer(name):
        print('%s准备吃包子啦' %name)
        while True:
            baozi = yield #yield保存当前状态,返回时直接返回到这个状态,但是单纯调用next不会给yield传值
            print('%s包子来啦,被%s吃了!' %(baozi,name))
    
    
    eat = consumer('gavin')
    eater = next(eat)  #只唤醒yield,不会给它传值
    baozi1 = '韭菜馅'
    eat.send(baozi1) #send可以调用yield,同时给yield传值,也就是唤醒yield同时给它传值
    #输出
    gavin准备吃包子啦
    韭菜馅包子来啦,被gavin吃了!
    #版本二:通过协程方式达到并行效果
    import time
    
    def consumer(name):
        print('%s准备吃包子啦' %name)
        while True:
            baozi = yield #yield保存当前状态,返回时直接返回到这个状态,但是单纯调用next不会给yield传值
            print('%s包子来啦,被%s吃了!' %(baozi,name))
    
    
    def producter():
        c1 = consumer('gavin')#这个动作只是把它变成生成器
        c2 = consumer('alex')
        c1.__next__()#需要执行next才会执行生成器
        c2.__next__()
        print('老子开始做包子啦!')
        for i in range(1,5):
            time.sleep(1)
            print('做了一个包子,分两半,一个白菜馅,一个韭菜馅')
            c1.send(i)
            c2.send(i)
    
    producter()
    #输出
    gavin准备吃包子啦
    alex准备吃包子啦
    老子开始做包子啦!
    做了一个包子,分两半,一个白菜馅,一个韭菜馅
    1包子来啦,被gavin吃了!
    1包子来啦,被alex吃了!
    做了一个包子,分两半,一个白菜馅,一个韭菜馅
    2包子来啦,被gavin吃了!
    2包子来啦,被alex吃了!
    做了一个包子,分两半,一个白菜馅,一个韭菜馅
    3包子来啦,被gavin吃了!
    3包子来啦,被alex吃了!
    做了一个包子,分两半,一个白菜馅,一个韭菜馅
    4包子来啦,被gavin吃了!
    4包子来啦,被alex吃了!

    5、迭代器

    可直接作用于for循环的数据结构包括:

      1:集合类型数据:list、tuple、dict、set、str

      2、generator,包括生成器和带yield的generator function函数

    这些可以直接作用于for循环对象统称为可迭代对象Iterable

    可以使用isinstance()判断一个对象是否是Iterable

    >>> from collections import Iterable
    
    >>> isinstance([], Iterable)
    True
    >>> isinstance({}, Iterable)  
    True
    >>> isinstance((), Iterable)   
    True
    >>> isinstance((x for x in range(20)), Iterable) 
    True

    生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopItrable错误表示没法继续返回下一个值

    可以被next()函数调用并不断返回下一个值的对象称为迭代器Iterator

    >>> isinstance((x for x in range(20)), Iterator)
    True
    >>> isinstance({}, Iterator)                      
    False
    >>> isinstance([], Iterator)  
    False
    >>> isinstance((), Iterator)  
    False

    生成器都是Iterator对象,但是list dict set虽然是Iterable但是不是Iterator,如果想把它们变为Iterator,可以使用iter()函数

    >>> isinstance(iter(()), Iterator)
    True
    >>> isinstance(iter([]), Iterator)    
    True
    >>> isinstance(iter({}), Iterator)  
    True

    Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

    Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

    6、内置函数

    >>> abs(-1)  #取绝对值
    1
    >>> print(all([-1])) #如果里面包括0为false其余都为true,这个相当于and,只要有false就为false
    True
    >>> print(all([0,1]))
    False
    >>> print(any([0,1]))   #只要有真就为true,不管有没有false
    True
    >>> print(any([0,-1]))
    True
    >>> print(ascii('ä¸中å午'))  #将值转换为asii码
    'xe4xb8u4e2dxe5u5348'
    >>> print(bin(1))  #将十进制转为二进制
    0b1
    >>> bool([])
    False
    >>> bool([1])  #布尔函数,判断真假
    True
    >>> bool([-1])
    True
    >>> a = bytearray('abcde', encoding='utf-8')   #可以通过asii码的形式修改字符串
    >>> print(a[1])
    98
    >>> a[1] = 50  #将b替换为2
    >>> print(a)
    bytearray(b'a2cde')
    >>> print(callable([]))  #是否可以包含括号,列表不能包含括号
    False
    >>> def func():print('1')
    ... 
    >>> print(callable(func)) #函数可以包含括号
    True
    >>> print(chr(98)) #通过数字得到对应的asii码
    b
    >>> print(ord('b'))#通过asii码得到对应的数字
    98
    >>> code = "for i in range(5):print(i)"
    >>> exec(code)  #可以将字符串对应的函数执行
    0
    1
    2
    3
    4
    >>> 
    >>> 
    >>> a = dict()  #定义字典
    >>> a
    {}
    >>> divmod(5,3)  #取两个数字的商和余数
    (1, 2)
    >>> a = '''{'a':1,'b':'aaaaa'}'''
    >>> c = eval(a)  #取引号
    >>> c
    {'a': 1, 'b': 'aaaaa'}
    >>> calc = lambda n:print(n)  #匿名函数,如果不反复调用执行一次就结束可以使用匿名函数,执行完毕后立即回收内存
    >>> 
    >>> calc(5)
    5
    >>> calc = lambda n:print(n+1)
    >>> calc(5)
    6
    >>> res = filter(lambda n:n>5,range(10))  #过滤器:只取大于5的数值
    >>> for i in res:print(i)
    ... 
    6
    7
    8
    9
    >>> res1 = map(lambda n:n*n,range(5))  #和匿名函数配合
    >>> for i in res:print(i)
    >>> for i in res1:print(i)
    ... 
    0
    1
    4
    9
    16
    >>> import functools  #reduce函数从内置函数中去除
    >>> res2 = functools.reduce(lambda x,y:x+y,range(10)) #操作x+赋值给x
    >>> print(res2)
    45
    >>> float(10)  #将整数变为浮点数
    10.0
    >>> madlib = " I {verb} the {object} off the {place} ".format(verb="took", object="cheese", place="table")  #format是准备替换%s的内置函数,匹配更加精确,通过{}与format方法进行引用,此例format通过变量名进行引用
    >>> madlib
    ' I took the cheese off the table '
    >>> '{},{}'.format('a','b')  #通过顺序方式依次引用a  b
    'a,b'
    >>> a = set([1,1,1,1,33,4,1,45])  #将列表变为集合
    >>> a
    {1, 4, 45, 33}
    >>> b = frozenset([1,1,1,1,33,4,1,45])  #被冻结的集合,没法添加
    >>> b
    frozenset({1, 4, 45, 33})
    >>> print(hash('I have a apple')) #做hash操作
    7983704463637394503
    >>> len('I have a apple')
    14
    >>> print(hex(255)) #将十进制转换为16进制
    0xff
    >>> def test():local_var = 333,print(locals()) 
    ... 
    >>> test()
    {}
    >>> print(globals())
    {'test': <function test at 0x101a479d8>, 'res': <filter object at 0x101945b38>, 'functools': <module 'functools' from '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/functools.py'>, '__name__': '__main__', 'a': {1, 4, 45, 33}, '__builtins__': <module 'builtins' (built-in)>, 'b': frozenset({1, 4, 45, 33}), 'res1': <map object at 0x101a534e0>, 'res2': 45, 'c': {'a': 1, 'b': 'aaaaa'}, '__doc__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, 'calc': <function <lambda> at 0x101a478c8>, '__spec__': None, 'i': 16, 'Iterable': <class 'collections.abc.Iterable'>, 'Iterator': <class 'collections.abc.Iterator'>, 'func': <function func at 0x101a477b8>, 'madlib': ' I took the cheese off the table ', 'code': 'for i in range(5):print(i)', '__package__': None}
    >>> print(globals().get('local_var'))
    None
    >>> print(max([1,2,3,4]))  #打印最大
    4
    >>> print(min([1,2,3,4]))   #打印最小
    1
    >>> print(oct(255))  #打印8进制
    0o377
    >>> a = 'Hello world'
    
    #str与repr的区别在于str与repr虽然都是显示字符串、但是str显示的更人性化、所以一般和print一起调用,而repr一般是给解释器使用所以和eval()一起使用的多一些
    >>> b = str(a)
    >>> c = repr(a)  #对该字符串再增加引号
    >>> d = eval(c)  #将c增加的引号脱去
    >>> print(a==b)
    True
    >>> print(a==c)
    False
    >>> print(a==d)
    True
    >>> print(str(a))
    Hello world
    >>> print(repr(a))
    'Hello world'
    >>> print(eval(repr(a)))
    Hello world
    >>> print(list(reversed([3,4,55,23,45]))) #对列表里的数据倒序排列
    [45, 23, 55, 4, 3]
    >>> round(1.3345,3)#去小数点3位
    1.335
    >>> slice(2,6)
    slice(2, 6, None)
    >>> d = range(20)
    >>> d
    range(0, 20)
    >>> d[slice(2,5)]  #分割
    range(2, 5)
    >>> sum([1,44,32,11,45])#将列表中的数据相加
    133
    >>> type([1,44,32,11,45]) #打印数据类型
    <class 'list'>
    >>> a = {6: 2,8:0, 1:4,-5:6,99: 11,4:22} 
    >>> print(a)
    {1: 4, 99: 11, 4: 22, 6: 2, 8: 0, -5: 6}
    >>> print(sorted(a.items())) #对字典按照key值来排序
    [(-5, 6), (1, 4), (4, 22), (6, 2), (8, 0), (99, 11)]
    >>> print(sorted(a.items(),key=lambda x:x[1])) #对字典按照value值来排序
    [(8, 0), (6, 2), (1, 4), (-5, 6), (99, 11), (4, 22)]
    >>> print(sorted(a.items(),key=lambda x:x[1],reverse = False ))#对字典按照value值来排序,默认reverse = False采用正序排序
    [(8, 0), (6, 2), (1, 4), (-5, 6), (99, 11), (4, 22)]
    >>> print(sorted(a.items(),key=lambda x:x[1],reverse = True ))#对字典按照value值来排序,reverse = True采用反序排序

    [(4, 22), (99, 11), (-5, 6), (1, 4), (6, 2), (8, 0)]
    >>> print(sorted(a.items(),reverse = True )) #对字典的key值进行反向排序
    [(
    99, 11), (8, 0), (6, 2), (4, 22), (1, 4), (-5, 6)]
    >>> a = [1,2,3,4]
    >>> b = ['a','b','c','d']
    >>> for i in zip(a,b):print(i) #合并打印
    ...
    (
    1, 'a') (2, 'b') (3, 'c') (4, 'd')
    >>> c = ['a','b']
    >>> for i in zip(a,c):print(i) #如果一方的值少,会按照少的那方的数据个数来打印
    ...
    (
    1, 'a') (2, 'b')
    >>> __import__('os') #和import一样,但是是采用字符串方式来进行引用
    <module 'os' from '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/os.py'
    > >>> __import__('os',globals(),locals(),['path','pip'])
    <module 'os' from '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/os.py'>

    7、json、pickle与序列化

    用于序列化的两个模块

    • json 用于字符串与python数据类型间进行转换
    • pickle用于python特有的类型与python数据类型间进行转换

    Json模块提供了四个功能:dumps、dump、loads、load

    pickle模块提供了四个功能:dumps、dump、loads、load

    json默认情况下只字符串、字典、列表进行序列化,因为json是通用的,所有语言都支持json,在不进行特殊自定义情况下,不能序列化函数

    import json
    info = {
        'name': 'alex',
        'age': 23,
        'job': 'IT'
    }
    
    print(repr(info))
    #输出
    {'job': 'IT', 'name': 'alex', 'age': 23}
    info_json = json.dumps(info,sort_keys=True)  #sort_keys用来对序列进行排序
    print(info_json)
    #输出
    {"age": 23, "job": "IT", "name": "alex"}
    info_json1 = json.dumps(info,sort_keys=True, indent=4) #indent可以对dumps的序列进行优化,表示数值离括号的距离
    print(info_json1)
    #输出
    {
        "age": 23,
        "job": "IT",
        "name": "alex"
    }
    info_json2 = json.dumps(info,sort_keys=True, separators=(',',':')) #在网络传输过程中为了节省传输带宽,可以对无用的字符进行压缩,这里是对,和:进行压缩以节省更多传输空间
    print(info_json2)
    #输出
    {"age":23,"job":"IT","name":"alex"}
    import json
    info = {
        'name': 'alex',
        'age': 23,
        'job': 'IT'
    }
    
    with open('seriliaztion.txt','w') as f:
        f.write(repr(info))  #按照普通方式写入文件,普通方式写入要求字符串格式进行写入
    
    
    with open('seriliaztion.txt','r') as f:
        for line in f:
            print(type(line))
            print(line)  #对该形式存入的输出只能按照字符串方式进行打印
    
    #输出
    <class 'str'>
    {'age': 23, 'name': 'alex', 'job': 'IT'}
    
    with open('seriliaztion-json.txt','w') as f:
        f.write(json.dumps(info,sort_keys=True)) #在写入时对其进行json编码
    
    
    with open('seriliaztion-json.txt','r') as f:
        for line in f:
            print(json.loads(line)['name'])  #对此进行解码,在解码后可以按照字典方式针对key打印value
            print(json.loads(line)['age'])
            print(json.loads(line)['job'])
    #输出
    alex
    23
    IT
    with open('seriliaztion-json.txt','r') as f:
        data = json.load(f) #等同于data = json.loads(f.read())
    print(data['job'])
    #输出
    IT
    #pickle与json的区别在于pickle不需要特殊定义就默认支持将函数序列化,同时pickle生产的文件是二进制文件,需要加b
    import pickle
    def sayhi(name):
        print('hi {}'.format(name))
    
    info = {
        'name': 'gavin',
        'age': 30,
        'func': sayhi
    }
    
    with open('seriliaztion-pickle.txt','wb') as f:  #需要加wb
        pickle.dump(info,f)  #等同于f.write(pickle.dumps(info))

     8、软件目录结构规范

     (摘抄于http://www.cnblogs.com/alex3714/articles/5765046.html)

    为什么要设计好目录结构?

    "设计项目目录结构",就和"代码编码风格"一样,属于个人风格问题。对于这种风格上的规范,一直都存在两种态度:

    1. 这种个人风格问题"无关紧要"。理由是能让程序work就好,风格问题根本不是问题。
    2. 规范化能更好的控制程序结构,让程序具有更高的可读性。

    "项目目录结构"其实也是属于"可读性和可维护性"的范畴,设计一个层次清晰的目录结构,就是为了达到以下两点:

    1. 可读性高: 不熟悉这个项目的代码的人,一眼就能看懂目录结构,知道程序启动脚本是哪个,测试目录在哪儿,配置文件在哪儿等等。从而非常快速的了解这个项目。
    2. 可维护性高: 定义好组织规则后,维护者就能很明确地知道,新增的哪个文件和代码应该放在什么目录之下。这个好处是,随着时间的推移,代码/配置的规模增加,项目结构不会混乱,仍然能够组织良好。

    所以,保持一个层次清晰的目录结构是有必要的。更何况组织一个良好的工程目录,其实是一件很简单的事儿。

    目录组织方式

    假设你的项目名为foo, 最方便快捷目录结构这样就足够了:

    Foo/

    |-- bin/
    |   |-- foo
    |
    |-- foo/ | |-- tests/ | | |-- __init__.py | | |-- test_main.py | | | |-- __init__.py | |-- main.py | |-- docs/ | |-- conf.py | |-- abc.rst | |-- setup.py |-- requirements.txt |-- README

    简要解释一下:

    1. bin/: 存放项目的一些可执行文件,当然你可以起名script/之类的也行。
    2. foo/: 存放项目的所有源代码。(1) 源代码中的所有模块、包都应该放在此目录。不要置于顶层目录。(2) 其子目录tests/存放单元测试代码; (3) 程序的入口最好命名为main.py
    3. docs/: 存放一些文档。
    4. setup.py: 安装、部署、打包的脚本。
    5. requirements.txt: 存放软件依赖的外部Python包列表。
    6. README: 项目说明文件。

    除此之外,有一些方案给出了更加多的内容。比如LICENSE.txt,ChangeLog.txt文件等

    下面,再简单讲一下这些目录的理解和个人要求

    关于README的内容

    每个项目都应该有的一个文件,目的是能简要描述该项目的信息,让读者快速了解这个项目。

    它需要说明以下几个事项:

    1. 软件定位,软件的基本功能。
    2. 运行代码的方法: 安装环境、启动命令等。
    3. 简要的使用说明。
    4. 代码目录结构说明,更详细点可以说明软件的基本原理。
    5. 常见问题说明。

    有以上几点是比较好的一个README。在软件开发初期,由于开发过程中以上内容可能不明确或者发生变化,并不是一定要在一开始就将所有信息都补全。但是在项目完结的时候,是需要撰写这样的一个文档的。

    关于requirements.txt和setup.py

    setup.py

    一般来说,用setup.py来管理代码的打包、安装、部署问题。业界标准的写法是用Python流行的打包工具setuptools来管理这些事情。这种方式普遍应用于开源项目中。不过这里的核心思想不是用标准化的工具来解决这些问题,而是说,一个项目一定要有一个安装部署工具,能快速便捷的在一台新机器上将环境装好、代码部署好和将程序运行起来。

    刚开始接触Python写项目的时候,安装环境、部署代码、运行程序这个过程全是手动完成,遇到过以下问题:

    1. 安装环境时经常忘了最近又添加了一个新的Python包,结果一到线上运行,程序就出错了。
    2. Python包的版本依赖问题,有时候我们程序中使用的是一个版本的Python包,但是官方的已经是最新的包了,通过手动安装就可能装错了。
    3. 如果依赖的包很多的话,一个一个安装这些依赖是很费时的事情。
    4. 新同学开始写项目的时候,将程序跑起来非常麻烦,因为可能经常忘了要怎么安装各种依赖。

    setup.py可以将这些事情自动化起来,提高效率、减少出错的概率。"复杂的东西自动化,能自动化的东西一定要自动化。"是一个非常好的习惯。

    setuptools的文档比较庞大,刚接触的话,可能不太好找到切入点。学习技术的方式就是看他人是怎么用的,可以参考一下Python的一个Web框架,flask是如何写的: setup.py

    当然,简单点自己写个安装脚本(deploy.sh)替代setup.py也未尝不可。

    requirements.txt

    这个文件存在的目的是:

    1. 方便开发者维护软件的包依赖。将开发过程中新增的包添加进这个列表中,避免在setup.py安装依赖时漏掉软件包。
    2. 方便读者明确项目使用了哪些Python包。

    这个文件的格式是每一行包含一个包依赖的说明,通常是flask>=0.10这种格式,要求是这个格式能被pip识别,这样就可以简单的通过 pip install -r requirements.txt来把所有Python包依赖都装好了。

    关于配置文件的使用方法

    注意,在上面的目录结构中,没有将conf.py放在源码目录下,而是放在docs/目录下。

    很多项目对配置文件的使用做法是:

    1. 配置文件写在一个或多个python文件中,比如此处的conf.py。
    2. 项目中哪个模块用到这个配置文件就直接通过import conf这种形式来在代码中使用配置。

    这种做法我不太赞同:

    1. 这让单元测试变得困难(因为模块内部依赖了外部配置)
    2. 另一方面配置文件作为用户控制程序的接口,应当可以由用户自由指定该文件的路径。
    3. 程序组件可复用性太差,因为这种贯穿所有模块的代码硬编码方式,使得大部分模块都依赖conf.py这个文件。

    所以,我认为配置的使用,更好的方式是,

    1. 模块的配置都是可以灵活配置的,不受外部配置文件的影响。
    2. 程序的配置也是可以灵活控制的。

    能够佐证这个思想的是,用过nginx和mysql的同学都知道,nginx、mysql这些程序都可以自由的指定用户配置。

    所以,不应当在代码中直接import conf来使用配置文件。上面目录结构中的conf.py,是给出的一个配置样例,不是在写死在程序中直接引用的配置文件。可以通过给main.py启动参数指定配置路径的方式来让程序读取配置内容。当然,这里的conf.py你可以换个类似的名字,比如settings.py。或者你也可以使用其他格式的内容来编写配置文件,比如settings.yaml之类的。

        

      

  • 相关阅读:
    212-@Slf4j注解起到了什么作用?
    212-如何实现定时器扫描?
    211-redis单线程问题?
    211-Feign中的@RequestParm与@RequestBody注解的使用
    210-java加载类的时候,发生了什么了呢?
    209-thymeleaf简单使用
    JS自定义 Map
    java Main方法 获取 maven 的resource 下的xml文件
    springboot+thymeleaf 访问静态资源解决(static)
    两个list 合并后去除掉重复removeAll()的,然后再随机获取最后list中的 几个值
  • 原文地址:https://www.cnblogs.com/xiaopi-python/p/6370150.html
Copyright © 2020-2023  润新知