2、Python 基础入门


一、python 使用注意事项

  1. python帮助系统使用help(需要查询的命令)函数, dir()help()很类似,只不过它返回的是一个模组中一系列的被定义过的方法的列表

  2. python2 和 python3 的主要区别:

    • python 2 输入使用raw_input(); python 3 使用input()
    • python 2 输出不换行使用print 'hello', ; python 3 使用print("Hello:", end=" ")
    • python 2 整除使用 a / b a / b a/b, 浮点除法使用 a / f l o a t ( b ) a / float(b) a/float(b); python 3 整除使用 a / / b a // b a//b, 浮点除法使用 a / b a / b a/b
    • xrange 重命名为 range, 返回迭代器对象
  3. python的输入 input() 和输出 print()

    • int(input()): 将输入的单个字符型数据转换为整数(多个就不能转换了), num = input('显示一个字符串来提醒用户输入: ')函数可以输入多个字符,num以字符串的形式存在,num1 = num.split() # 返回以空格分割的字符组成的列表
    • print('str1', 'str2')遇到逗号会输出一个空格,打印完字符串会自动添加一个换行符print("Hello:", end=" ")end = " "参数告诉print函数用空格代替换行符\n
  4. python 的内存管理机制:

    • 垃圾回收机制:
      • 以引用计数为主,分代收集为辅
      • 当系统判断没有变量引用某内存地址时(引用计数为 0),系统就会回收这部分内存地址
    • 内存池机制:
      • 概念:预先在内存中申请一定数量的,大小相等的内存块留作备用(以防频繁调用 new/malloc 导致大量的内存碎片)
      • 使用:针对小对象(eg:<=512 bytes)Pymalloc 会在内存池中申请内存空间,大对象(eg:>512 bytes),则会使用 PyMem_RawMalloc()PyMem_RawRealloc() 来申请新的内存空间
    • 缓存的重用机制
      • 缓存:程序会在内存里开辟一个空间,用来存放读取比较频繁且内存占用较少的对象
      • 重用:顾名思义,重复使用(直接寻找缓存地址)
        在这里插入图片描述
  5. python 中对于2 < a < 13 的判断可以写成if 2 < a < 13: or if 2 < a and a < 13:

  6. E 记法:E的意思就是指数为10,E后边的数字就是10的多少次幂。Eg: 1e2=100.0 # E不区分大小写,但前面的 1 不能省略

  7. python最重要的就是缩进,代码块以缩进的方式体现,if, else, while等不需要加括号,但需在最后加入冒号,print函数要有括号()

  8. 原始字符串:只需在字符串前面加上英文字母 r 即可;习惯上用全部大写的变量名表示常量,eg: PI = 3.14159265359

  9. python 语言的几个要件:数据和过程
    这里写图片描述

  10. 浮点数的精度问题

    # 由于计算机内部使用二进制保存数值,十进制的小数转为二进制的时候就可能产生误差
    0.1 + 0.2
    >>> 0.30000000000000004
    
    # 所以浮点数的相等判断不能简单的使用相等关系符判断
    0.1 + 0.2 == 0.3
    >>> False
    
    # 1、两个浮点数做差小于一个很小的数(eg: 0.00001)
    (0.1 + 0.2) - 0.3 < 1e-5
    >>> True
    
    # 2、使用 round 函数设置小数点后的有效位数(四舍五入到指定的小数位)后再进行判断
    round(0.1+0.2, 5) == round(0.3, 5)
    >>> True
    
  11. python 变量机制:引用数据对象,变量的类型就是它所指向数据对象的类型,变量类型可以随时改变


二、python 常用操作符

  • 按照优先级排列:
    • 幂运算(**) > 正负号(+, -) > 算术操作符(+, - , *, /, %取余, //取整) > 比较操作符(==, >, <, !=, 可链式使用) >逻辑运算符(and, or, not)
    • 注意:and 运算符从左到右计算表达式,若所有值为真,则返回最后一个值,若存在假,则返回 0; or 运算符也是从左到右计算表达式,若有一个为假,则返回0,否则返回第一个为真的值。eg: 3 and 4 --> 4 ; 3 or 4 --> 3
    • 位运算符:把数字看作二进制来进行计算
      在这里插入图片描述
  • 绝对值 abs(), 四舍五入 round(), 最大最小值 max(),min()
  • 一些常用的数学函数如 sqrt/sin/cos 等都在 math 模块
    • import math
    • math.sqrt(2)
  • 赋值语句小技巧
    • 级联赋值语句:x = y = z = 1
    • 多个变量分解赋值:a, b = ['hello', 'world']
    • 变量交换:a, b = b, a
    • 自操作:i += 1, n *= 45

三、python 常用数据类型

3.1、可变数据类型和不可变数据类型

这里写图片描述

  1. 不同数据类型可以相互转换,相同数据类型可以进行拼接。
  2. 使用 type() 或者 isinstance() 函数可查看数据的类型。eg: type(x) 或者 isinstance(x, int)
  3. python是一门动态语言(脚本语言),它无需直接声明变量,但使用变量之前,需要先对其赋值,python会根据你的赋值决定数据类型,它会在你新建一个变量时开辟内存。
  4. 不可变数据类型赋值过后,其内存的内容是不可变的;可变数据类型赋值过后,其内存内容是可变的。可用 id()查看变量的内存地址。
    x = [500, 501, 502]
    y = x                 # y指向x的内存地址, 系统并没有为y分配新的内存
    z = x                 # z指向x的内存地址, 系统并没有为z分配新的内存
    z = [1, 2, 3]         # 此时系统为z分配新的内存, x = [500, 501, 502]保持不变
    y[1] = 600            # 此时x的值发生改变,x = [500, 600, 502] = y
    y = [700, 800]        # 此时系统为y分配新的内存,x = [500, 600, 502] 保持不变
                          # y = x 属于传引用操作,通过对y进行索引或者切片重新赋值时x的值会发生变化
                          # 只有,直接对y直接进行赋值时,系统为y分配了新的内存,此时x的值才不会发生改变。
    

3.2、对象赋值、浅拷贝和深拷贝

3.2.1、对象赋值

在这里插入图片描述

  • 首先,创建了一个名为 will 的变量,这个变量指向一个 list 对象,从第一张图中可以看到所有对象的地址(每次运行,结果可能不同)
  • 然后,通过 will 变量对 wilber 变量进行 赋值,那么 wilber 变量将 指向 will 变量对应的对象(内存地址)
    • 也就是说 wilber is will; wilber[i] is will[i]
    • 可以理解为,Python 中,对象的赋值都是进行对象引用(内存地址)传递
  • 最后,第三张图中,由于 willwilber 指向同一个对象,所以对 will 的任何修改都会体现在 wilber
    • 这里需要注意的一点是,str 是不可变类型,所以当修改的时候会替换旧的对象,产生一个新的地址 39758496

3.2.2、浅拷贝

在这里插入图片描述

  • 首先,依然使用一个 will 变量,指向一个 list 类型的对象
  • 然后,通过 copy 模块里面的浅拷贝函数 copy(),对 will 指向的对象进行浅拷贝,然后浅拷贝生成的新对象赋值给 wilber 变量
    • 浅拷贝会创建一个新的对象,这个例子中 wilber is not will
    • 但是,对于对象中的元素,浅拷贝就只会使用 原始元素的引用(内存地址),也就是说 wilber[i] is will[i]
  • 最后,当对 will 进行修改的时候
    • 由于 list 的第一个元素是不可变类型,所以 will 对应的 list 的第一个元素会使用一个新的对象39758496
    • 但是 list 的第三个元素是一个可变类型,修改操作不会产生新的对象,所以 will 的修改结果会相应的反应到 wilber
  • 总结一下,当我们使用下面的操作的时候,会产生浅拷贝的效果:
    • 使用切片操作
    • 使用工厂函数(如list/dir/set
    • 使用copy模块中的 copy() 函数

3.2.3、深拷贝

在这里插入图片描述

  • 首先,同样使用一个 will 变量,指向一个 list 类型的对象
  • 然后,通过 copy 模块里面的深拷贝函数 deepcopy(),对 will 指向的对象进行深拷贝,然后深拷贝生成的新对象赋值给 wilber变量
    • 跟浅拷贝类似,深拷贝也会创建一个新的对象,这个例子中 wilber is not will
    • 但是,对于对象中的元素,深拷贝都会重新生成一份(有特殊情况,下面会说明),而不是简单的使用原始元素的引用(内存地址)
      • 例子中 will 的第三个元素指向39737304,而 wilber 的第三个元素是一个全新的对象 39773088,也就是说,wilber[2] is not will[2]
      • 但 list 中的前两项是字符串和数字,它们属于 不可变数据类型,为了提升效率,在 python 语言中,内存中只存一份不可变对象,并将其地址(即引用)赋值给其它变量,所以说wilber[0 or 1] is will[0 or 1]
  • 最后,当对 will 进行修改的时候
    • 由于 list 的第一个元素是不可变类型,所以 will 对应的 list 的第一个元素会使用一个新的对象39758496
    • 但是 list 的第三个元素是一个可变类型,修改操作不会产生新的对象,但是由于 wilber[2] is not will[2],所以will 的修改不会影响 wilber

3.2.4、总结

在这里插入图片描述

  • 容器类型(list、tuple、dict、set)的赋值、浅拷贝和深拷贝

    • numpy 数据的 .copy() 方法是深拷贝
    • 赋值(使用 =
      • 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒
      • 修改不可变对象(str、tuple)需要开辟新的空间
      • 修改可变对象(list等)不需要开辟新的空间
    • 浅拷贝(使用 copy.copy()
      • 浅拷贝是在另一块地址中创建一个 新的变量或容器 ,但是容器内的元素的地址均是源对象的元素的地址的拷贝
      • 也就是说新的容器中的元素指向了旧的地址( 新瓶装旧酒
    • 深拷贝(使用 copy.deepcopy()
      • 深拷贝是在另一块地址中创建一个 新的变量或容器,同时容器内的元素的 地址也是新开辟的 仅仅是值相同而已,是完全的副本
      • 也就是说新的容器中的元素指向了新的地址( 新瓶装新酒
      • 注意:对于容器中的 不可变元素类型,考虑到效率,依然使用原始的引用
  • 非容器类型:对于非容器类型(如数字、字符串等原子类型的对象)没有被拷贝一说


四、序列序列(列表,元组,字符串)

1. 序列定义-----最基本的数据结构

  • 在 Python 中最基本的数据结构是序列(Sequence),序列中的每个元素被分配一个序号——即元素的位置,也称为索引,第一个索引是 0

2. 通用序列操作

  • 不同类型的序列可以相互转换,相同类型的序列可以进行拼接

    • 转换序列类型:eg: a = 'hhh', list(a) = ['h', 'h', 'h']
    • 加(adding):相同类型的序列可以进行相加(拼接), eg: 'hello' + ' world' = 'hello world' # 注意空格
    • 乘(multiplying):可以复制同一序列 n 次进行扩展,eg: a = [5], a*3 = [5, 5, 5]
  • 索引(indexing,x[i],假设序列长度为n)

    • 正序索引:序列的正序索引值 i0 开始到 n-1
    • 逆序索引:序列的逆序索引值 i-n 开始到 -1,逆序索引值就等于它的正序索引的值减去字符串的长度
      这里写图片描述
  • 切片(slicing,x[lower: upper: step] 三个边界均为可选项,默认step=1)

    • 正序切片(step 为正数):省略lower意味着从开头开始分片,省略upper意味着一直分片到结尾eg: x[:5], x[5:], x[3:5], x[:]
    • 逆序切片(step 为负数):省略lower意味着从结尾开始分片,省略upper意味着一直分片到开头eg: x[::-1], x[:-5:-1], x[-2::-1]
    • 切片的时候,即便 startend 索引越界也不会出现问题,利用这一特性,我们可以限定输入序列的最大长度;需要注意的是,索引时下标不能越界!
    • 注意:对 list 进行切片赋值的时候eg: a[2:7] = [1, 2, 3],即便它们的长度不同也依然可以替换!
  • 掩膜处理(mask)

    import numpy as np
    
    labels = np.array([0, 1, 2, 3])
    mask = np.array([False, True, True, False])
    
    labels[mask]-->array([1, 2])   # 把 mask 元素值为真对应 labels 中的元素挑选出来
    
  • enumerate(seq)

    • 产生用于迭代的 (索引,值) 对,可以通过 list(enumerate(seq)) 将(索引,值)在 列表 中打印出来
  • 计算序列中所包含元素的数量(len)、找出最大/最小元素(max/min)、求得序列中所有元素的和(sum)、迭代(iteration)、

  • 检查某个元素是否属于序列的成员(成员资格in/not in——eg:检查用户名和PIN码)

    # 检查用户名和PIN码
    database = [
    			['bob', '1234'],
    			['pet', '5678'],
    			['sam', '9898']
    		   ]
    		   
    username = input('User Name:')
    pin = input('PIN Code:')
    
    if [username, pin] in database: 
    	print('Access granted!')
    

3. 列表、元组、字符串(列表是可变的,而元组和字符串是不可变的)

  • 列表(一个"打了激素"的数组)
    • 数组是把一大堆同种类型的数据挨个摆在一块,然后通过数组下标进行索引;而列表是把一大堆各种类型的数据挨个摆在一块,然后通过列表下标进行索引
    • 列表的增删改查拷
      • 增加列表元素:list.append('str') or list.extend('str') ,这两者的区别在于后者会展开所添加的序列,亦可使用列表的 insert 方法在指定位置添加元素list.insert(index, obj)
      • 删除列表元素:list.remove('删除首次出现元素') or list.pop(index) or del list[i] or del list
      • 改变列表元素:使用索引或切片对相应的位置进行赋值,或者使用赋值语句重写整个序列
      • 查看列表元素:使用索引或切片来实现
      • 拷贝列表元素:list2 = list1[:] # 深拷贝,新增列表,值不会同时变化(切片) list2 = list1 #浅拷贝,只增加了一个标签,值会同时变化
    • 列表的常用方法
      • 列表排序
        • list.sort() # 正序,从小到大排序,从大到小排序可用:list.sort(reverse=True)
        • list.reverse() # 逆序排列,将整个列表原地翻转(不进行比较),可用切片实现list[: : -1]
        • sorted(list) # 正序排列(从小到大) sorted(list, reverse=True) # 逆序排列(从大到小)
        • sorted 函数按照大小、长短、英文字母的顺序给列表中的元素进行排序,它返回新的有序列表(对原 list 没有影响),而 list 中的sort()是就地排序(没有返回值),对原 list 产生影响。
      • 列表元素索引查询list.index(v) # 找到 v 的首次出现位置
      • 列表元素频次统计list.count(v)
  • 元组(戴了枷锁的列表)
    • 封闭的列表,一旦定义,就不可改变(不能添加、删除、修改),使用小括号()。
    • 所有的多对象的、逗号分隔的、没有明确用符号定义的这些集合默认的类型都是元组。
    • 为什么需要元组?
      • 旧式字符串格式化中的参数要用元组表示
      • 可在字典中当作键来使用
      • 它是一些内建函数和方法的返回值
  • 字符串
    • 转义字符含义及字符串格式化符号

      • \ 继续上一行,\n 换行符,- 用于左对齐

      • 字符串前加 r 表示将其转成原始字符串,加 b 表示将其转成二进制字符串,加 u 表示将其转成 Unicode 编码的字符串。 **注意:**由于转义字符串可能给目录的表达带来某些问题,所以无论在 win 或 linux 下,优先考虑使用正斜杠 / 来分隔目录

      • 传统字符串格式化符号:% 占位符

        • 占位符 m.n%f or m.n%d: m 代表设定的总位数(包括小数点,不够则补空格),n 代表设定小数点后的位数或整数前的位数(不够则补 0)
        • 在字符串内部,%s表示用字符串替换,%d表示用整数替换,有几个%?占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个%?括号可以省略。eg: "%.2f + %.3f = %.1f " % (1,2,1+2) # 输出 '1.00 + 2.000 = 3.0 ' print('%.3d' % 5) # 输出 005在字符串内部,%s表示用字符串替换,%d表示用整数替换,有几个%?占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个%?括号可以省略。eg: "%.2f + %.3f = %.1f " % (1,2,1+2) # 输出 '1.00 + 2.000 = 3.0 ' print('%.3d' % 5) # 输出 005
      • 现代字符串格式化符号:str.format() 方法

        • 字符串中花括号 {} 的部分会被 format 传入的参数替代,传入的值可以是字符串,也可以是数字或者别的对象。
        • 位置参数'{2:.2f} {1} {0}'.format('a', 'b', 5) # 冒号表示格式化符号的开始, 输出'5.00 b a', 可以用数字指定传入参数的相对位置
        • 关键字参数'{color} {n} {x}'.format(n=10, x=1.5, color='blue') ,还可以指定传入参数的名称
        • format()方法的填充与对齐: 格式为:[填充字符][对齐方式][宽度]
          • 填充字符:只能是一个字符,不指定的话默认是用空格填充
          • 对齐方式:^、<、>分别表示居中、左对齐、右对齐
          • 宽度: m.nf: m 代表设定的总位数(包括小数点,不够则补填充字符),n 代表设定小数点后的位数或整数前的位数(不够则补 0),注意:如果不追求具体的显示格式,可以不必指定变量类型,系统会根据变量类型自动匹配对应的占位符
          # str
          '{:>8}'.format('189')     >>> '     189'
          '{:0>8}'.format('189')   >>>  '00000189' 
          '{:a>8}'.format('189')   >>>  'aaaaa189'
          
          # float
          '{:>8.3f}'.format(1.5)    >>> '   1.500'
          '{:0>8.3f}'.format(1.5)  >>>  '0001.500'
          '{:->8.3f}'.format(1.5)   >>> '---1.500'
          
          # seq
          alist = [5, 25, 125]
          'use * to unpack seq: {}, {}, {}'.format(*alist)  >>> 'use * to unpack seq: 5, 25, 125'
          
          # dict,在模板里只需要填入字典里对应的名称即可
          adict = {'x': 5, 'xx': 25, 'xxx': 125}
          'use ** to unpack dict: {x}, {xx}, {xxx}'.format(**adict)   >>> 'use ** to unpack dict: 5, 25, 125'
          
      • Python3.6 以后字符串格式化:f 字符串,可参考 https://geek-docs.com/python/python-tutorial/python-fstring.html

        # f 字符串的前缀为f,并使用 {} 括号评估值
        
        #!/usr/bin/env python3.8
        name = 'Peter'
        age = 23
        print(f'{name} is {age} years old')  # Peter is 23 years old
        
        # 多行 f 字符串
        msg = (
            f'Name: {name}\n'
            f'Age: {age}\n'
        )
        
        print(msg)  
        # Name: John Doe
        # Age: 32
        
        # f字符串转义字符
        print(f'This was a \'great\' film')  # This was a 'great' film
        
        
        #  f 字符串格式化浮点数
        val = 12.3
        print(f'{val:.2f}')  # 12.30
        print(f'{val:.5f}')  # 12.30000
        
        #  f 字符串格式化宽度
        for x in range(1, 11):
            print(f'{x:02} {x*x:3} {x*x*x:4}')
        # 输出如下
        01   1    1
        02   4    8
        03   9   27
        04  16   64
        05  25  125
        06  36  216
        07  49  343
        08  64  512
        09  81  729
        10 100 1000
        
        #  f 字符串对齐字符串
        s1 = 'a'
        s2 = 'ab'
        s3 = 'abc'
        s4 = 'abcd'
        
        print(f'{s1:>10}')
        print(f'{s2:>10}')
        print(f'{s3:>10}')
        print(f'{s4:>10}')
        
        # 输出如下:
                 a
                ab
               abc
              abcd
        
    • 改变字符串的值

      • 虽然字符串属于不可变数据类型,但我们可以通过重新赋值的方法来改变它(实质是创建了一个新的同名变量,然后原来的变量被垃圾回收了,可用 id() 来验证)
      • str1 = str1[:140]
      • str1 = str1.replace('old', 'new')
    • 常用的字符串方法

      • 'sep'.join(seq)以字符sep作为分隔符,插入到序列seq的所有字符之间。它用来连接序列中的元素,需要被连接的序列元素都必须是字符串

      • str1.strip(chars):删除字符串(不包括内部)两侧所有的空格(包括 \n),chars 参数可定制删除的字符(首尾),为可选参数

      • str1.upper/lower/swapcase/captilize/title():转换字符串中的所有小/大写字符为大/小写、大小写互换、首字母大写、所有单词首字母大写,这些方法均不会改变原来字符串的值

      • str1.split(sep=' ', maxsplit=-1):不带参数,默认是以空格为分割符切片字符串。如果maxsplit参数有设置,则分割成 maxsplit+1个子字符串,返回切片后包含子字符串的列表。注意:python 2 中不能使用此关键字参数

      • str1.count(sub, start= 0,end=len(string)):返回[start, end) 范围内 str1 中子字符串 sub 的数目

      • str1.startswith(str2) & str1.endswith(str2):str1 是否以 str2 开头或结尾,若是则返回 True,否则返回 False

      • ord(char):返回单个字符的 Unicode 码,eg: ord('a') = 97

      • chr(num):根据 Unicode 码返回相应的字符,eg: chr(97) = 'a'

        import numpy as np
        
        # eg1
        with open(r'E:\record.txt') as f:
            for each_line in f:
                (role, line_speaking) = each_line.split(':', 1)  # 以冒号':'为分隔符,分成两个子字符串组成的列表
        
        # eg2
        annotation = '0--Parade/0_Parade_Parade_0_904 360.92 97.92 623.91 436.46 \n'
        annotation = annotation.strip().split(' ')  
        # ['0--Parade/0_Parade_Parade_0_904', '360.92', '97.92', '623.91', '436.46']
        
        # py3 中为 map object, str-->float
        bbox = list(map(float, annotation[1:]))
        # 把所有的 bbox 重新排列一下,因为一张图中可能有多张人脸
        boxes = np.array(bbox, dtype=np.float32).reshape(-1, 4)  # [[448.51 329.63 570.09 478.23]]
        
      • 使用 find 方法和 enumerate 实现字符串模糊匹配索引

        	# find: 在一个字符串中查找另一个字符串
        	# 找到了返回第一次出现的位置, 没找到返回 -1
        	
        	a = [u'左转信号灯_红', u'机动车信号灯_绿', u'左转信号灯_红', u'左转信号灯_红']
        	[i for i,x in enumerate(a) if x.find(u'左转')!=-1]
        	
        	>>> [0, 2, 3]
        
      • 使用 ljust 方法和 rjust 实现字符串输出对齐

        display_text = '%s %f %s' % (val.ljust(max_filename_len), max_prob, str(max_prob_index).rjust(3))
        print(display_text )
        

五、字典&&集合

1. 字典(当索引不好用时)

  • 字典简介
    • 序列是通过编号对其进行索引,字典是一种通过名字(key)来引用值(value)的数据结构。序列讲究顺序,字典讲究映射,不讲顺序。
    • python对的要求相对严格一些,要求它们必须是可哈希(Hash)的对象,不能是可变类型(包括变量、列表、字典本身等),但可以是(数字、字符串、元组等)不可变类型;python对是没有任何限制的,它可以是任意类型的python对象。
    • 用中括号 [ ] 括起来的数据叫做 列表,但用大括号 { } 括起来的数据不一定是 字典,若没有反应出数据的映射关系,则叫做集合
  • 字典的创建
    • dict1 = {},可以加入键值对进行初始化dict1 = {'one':1, 'two':2}
    • dict1 = dict(),可以传入包含键值对的列表来初始化,dict1 =dict([('one',1), ('two', 2)])
  • 字典的增删改查以及拷贝和引用
    • 增: dict1['three'] = 3
    • 删: dict1.pop('two') or del dict1['two']
    • 改:
      • 改值: dict1['one'] = 5 or dict1.update([('one', 11), ('three', 3)]) 改值+新增键值对,输出 {'one':11, 'two':2, 'three':3)}
      • 改键:dict1['new_key'] = dict1.pop('old_key')
    • 查:
      • dict1['one'], 通过键来查看其对应的值,注意不要用小括号(),会报TypeError:'dict' object is not callable的错误
      • dict1.__getitem__('one'),通过键来查看其对应的值,等价于 dict1['one']
    • 拷贝和引用dict2 = dict1.copy(),值不会同时变化,内存地址不同; dict2 = dict1,值会同时变化,内存地址相同
  • 获取字典dict1的键、值和项(键,值)列表
    • dict1.keys(); dict1.values(); dict1.items()
    • 可通过list(dict1.keys())转化为列表
  • str 和 dict 相互转换
    • str2dict: mydict=eval(mystr)
      • eval 中的第一个参数是字符串,而字符串中的内容一定要是可执行的代码
      • eval 这个函数会把里面的字符串参数的引号去掉,把中间的内容当成 Python 的代码,eval 函数会执行这段代码并且返回执行结果
      • 字符串转字典:eval("{'name': '小夏', 'age': 30}")
      • 字符串转列表:eval("[1, 2, 3, 4]")
      • 字符串转元组:eval("(1, 2, 3, 4)")
      • 字符串转换数据类型:a = eval('2 + 2'), a 为 int 型变量
      • 字符串函数执行:self.network = eval(self.netw_name + '(self.cfg, self.logger, *self.netw_args)')
    • dict2str: mystr=str(mydict)
def my_function(x, y):
    return x + y

# 当可变关键字参数通过字符串来传递时,可以像下面那样使用
param_str = "{'x': 3, 'y': 4}"
params = eval(param_str)
result = my_function(**params)  
print(result)  # 7

from cls_dataset import ClsDataset
self.train_data = eval(cfg.DATA.NAME + '(txt_path=cfg.DATA.TRAIN_TXT_PATH, transform=self.train_transform)')
# 等价于下面这句,前提是 lsDataset 要提前导入
self.train_data = ClsDataset(txt_path=cfg.DATA.TRAIN_TXT_PATH, transform=self.train_transform)
  • easydict
    • 安装:pip install easydict
    • 作用:可以用 . 以属性的方式去访问字典的值
    • 实践代码:
      from easydict import EasyDict as edict
      
      # 创建一个空的 easydict
      config = edict()
      
      # 配置网络的一些超参数(初始化 easydict)
      config.BATCH_SIZE = 384
      config.EPS = 1e-14
      config.LR_EPOCH = [6, 14, 20]
      
      
      # 用传统字典初始化一个 easydict
      d = {'BATCH_SIZE':64, 'LR_EPOCH': [6, 14, 20], 'bar':{'x':1, 'y':2}}
      config = edict(d)
      

2. 集合(在我的世界里,你就是唯一)

  • 集合简介
    • 集合几乎所有的作用就是确保里边元素的唯一性,集合内不可能存在两个相同的元素。
    • 由于集合中的元素是无序的,所以不能像序列那样用下标来进行访问,但是可以使用迭代把集合中的数据一个个的读取出来。
  • 集合的常见操作
    • |:取两个集合的并集
    • &:去两个集合的交集
    • -:取两个集合的差集
    • in:判断元素是否属于集合
    • add(x):集合中添加元素
    • update(seq):集合中添加多个元素,相当于extend
    • remove(x):集合中删除指定元素
    • pop():删除集合任意元素并返回其值
    • clear():清空集合使其成为空集

六、推导式

1. 列表、字典和集合推导式

  • 执行效率要比使用循环操作高的多
  • 列表推导式会保留元素的顺序,而字典和集合推导式是无序类型,所以会添加元素时会随机操作
    这里写图片描述

2. 生成器推导式和生成器函数(yield)

  • 生成器包含两种:生成器推导式和生成器函数(yield)
    • 其特点是一边循环一边计算的机制,可极大的节省内存,但只能迭代一次
    • 可使用返回的迭代器作为参数,逐次调用内置的 next 函数,即可使其按照生成器表达式来输出下一个值
  • 注意生成器推导式和其它推导式的区别是:最外面是一个小括号()
    这里写图片描述
    def next(iterator, default=None): # real signature unknown; restored from __doc__
        """
        next(iterator[, default])
        
        Return the next item from the iterator. If default is given and the iterator
        is exhausted, it is returned instead of raising StopIteration.
        """
    
    # 1、生成器推导式
    a = (x**2 for x in range(10) if x < 3)
    
    next(a, -1)
    >>> 0
    next(a, -1)
    >>> 1
    next(a, -1)
    >>> 4
    next(a, -1)  # 默认值改为 -1, 不再触发 StopIteration
    >>> -1
    
    # 2、使用 yield 语句而不是 return 语句返回结果的生成器函数
    # 每次调用 next() 的时候执行,遇到 yield 语句返回,再次执行时从上次返回的 yield 语句处继续执行
    def odd():
        print('step 1')
        yield 1
        print('step 2')
        yield(3)
        print('step 3')
        yield(5)
    
    >>> o = odd()
    >>> next(o)
    step 1
    1
    >>> next(o)
    step 2
    3
    >>> next(o)
    step 3
    5
    >>> next(o)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    

七、可迭代对象(iterable),迭代器对象(iterator),生成器对象(generator)的关系

  • 集合数据类型如 listdictstrset 等是可迭代对象 iterable 但不是 iterator,不过可以通过 iter() 函数获得一个迭代器对象 iterator (内容放在内存,时间优先)

  • 生成器推导式和生成器函数(yield)都是生成器对象 generator 类型,而且它们属于 一种高级迭代器对象 iterator 类型,表示一个惰性计算的序列(内容不放内存,用时再存,空间优先),且只能迭代一次

  • 凡是可作用于 for 循环的对象都是可迭代对象 iterable 类型,Python 的 for 循环本质上就是通过不断调用next()函数实现的
    在这里插入图片描述

  • for 语句的内部实现,需要三个条件实现迭代器协议: __iter__()、 __next__()、StopIteration

    • __iter__():获取迭代器对象
    • __next__():从迭代器中获取值
    • StopIteration:检测异常来跳出
    for element in iterable:
        # do something with element
    
        # create an iterator object from that iterable
        iter_obj = iter(iterable)  # iter() 内置函数调⽤的是对象的 __iter__() ⽅法
    
        # infinite loop
        while True:
            try:
                # get the next item
                element = next(iter_obj)  # next() 内置函数调⽤的是对象的 __next__() ⽅法
    
                # do something with element
            except StopIteration:
                # if StopIteration is raised, break from loop
                break
    
    
    # 通过内置函数 iter 将可迭代对象列表转换为迭代器对象
    myIter = iter([1, 2, 3])
    
    # 通过内置函数 next 依次获取迭代器的下一个元素
    print(next(myIter))  # 1
    print(next(myIter))  # 2
    print(next(myIter))  # 3
    print(next(myIter))  # StopIteration
    
  • python 四大神器:迭代器(iterator)、生成器(generator)、装饰器(decorator)、上下文管理器(context manager)

    • 装饰器:对函数和类的封装
    • 上下文管理器(with):对任意形式代码块的封装(__init__() optional、__enter__()、__exit__()

八、Python 控制流(顺序/分支/循环)

1. Python 程序运⾏完整流程

  • Python 编译器 :将源代码在内存中编译byte code
  • Python 虚拟机 :调⽤ Built-in 库⽂件,同时将内存中的 byte code 逐⾏解析并执⾏,运⾏结束后, byte code 在内存中直接销毁
  • 若存在 import 脚本,则将其 byte code 持久化到硬盘上,成为 .pyc ⽂件,第⼆次运⾏相同且⽆修改的模块时,免去编译过程,直接读取编译好的 .pyc 放⼊虚拟机中执行
  • Note:python 解释器由编译器和虚拟机构成

2. 控制流简介

  • if/elif/else,可使用 and or not 进行多条件组合判断
  • for ,while(需要加分号进行缩进),在 for 和 while 循环中也可以使用 else 语句
  • 跳出循环:break , continue,不需要加分号
  • 占位语句:pass,在 python 中空代码是非法的

3. for 循环详解

  • for 循环格式
    这里写图片描述
    • 在 for 循环中常用 _ 作为 item 来表示:it’s a dummy variable, its value isn’t used in the loop body
  • for 循环相关函数
    • range() 函数用法:range([start], stop, [step=1]) #[]为可选参数,步长默认为1,stop的值取不到,它产生一个数字序列,可以通过list()函数将其转换为列表。
    • enumerate(seq, start=0): 产生用于迭代的 (索引,值)
      • 可以通过 list(enumerate(seq)) 将(索引,值)在 列表 中打印出来
      • 可以通过 start 参数指定函数开始计数时所用的值
        这里写图片描述
    • zip (seq1, seq2, …):并行迭代,接受一系列可迭代的对象作为参数,将对象中对应的元素打包成一个个tuple,然后返回由这些 tuples 组成的 list。若传入参数的长度不等,则返回 list 的长度和参数中长度最短的对象相同。 这里写图片描述
  • zip(*x) 函数:函数中的对象至少是二维数组,使用 * 进行解包,把数组中的每个对应元素提取出来,组成一个新的元组的可迭代对象
import torch


def collate_fn(batch):
    """Merges a list of samples to form a mini-batch of Tensor(s)"""
    imgs, labels_value, label_name = zip(*batch)
    return torch.stack(imgs, dim=0), torch.tensor(labels_value, dtype=torch.long), list(label_name)  # 2,3,2,2; 2; 2


if __name__ == "__main__":
    # zip() 函数: 将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组
    a = [1, 3, 5, 7, 9]
    b = [2, 4, 6, 8, 10]
    print(zip(a, b))  # 返回一个 zip 对象,其内部元素为元组, <zip object at 0x7f7e4b6642c0>
    print(list(zip(a, b)))  # 可以转化为列表或元组, [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]

    # 可通过循环迭代每个 tuple 对应的元素
    for i, j in zip(a, b):
        print(i, j)

    # zip(*)函数:函数中的对象至少是二维数组,使用 * 进行解包,把数组中的每个对应元素提取出来,组成一个新的元组的可迭代对象
    c = [[1, 5], [2, 6], [3, 7], [4, 8]]
    print(zip(*c))  # 返回一个 zip 对象,其内部元素为元组, <zip object at 0x7f7e4b664340>

    # 可通过赋值取出每个 tuple
    c1, c2 = zip(*c)
    print(c1, c2)  # (1, 2, 3, 4) (5, 6, 7, 8)

    # zip(*) 在 pytorch 中的使用方式
    a = torch.randn((3, 2, 2))
    b = torch.randn((3, 2, 2))
    batch = [(a, 1, 'dog'), (b, 0, 'cat')]
    collate_fn(batch)  # 主要调用的是 zip(*batch)

九、Python 字符编码及工作环境配置

  • Python 字符编码

    • ASCII:最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码python2 中默认使用 ASCII。
    • Unicode:它把所有语言都统一到一套编码里,这样就不会再有乱码问题了,但是它每个字符用3~4个字节表示,浪费空间
    • UTF-8:用 Unicode 编码 (通常2bytes) 比 ASCII 编码 (1byte) 需要多一倍的存储空间,在存储和传输上就十分不划算。所以,本着节约的精神,又出现了把Unicode编码转化为 “可变长编码” 的 UTF-8 编码。常用的英文字母被编码成 1 个字节,汉字通常是 3 个字节,只有很生僻的字符才会被编码成4-6个字节,python3 中默认使用 UTF-8。
    • 在计算机内存中,统一使用 Unicode 编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8 编码。
      • 内存中使用的编码是 unicode,用空间换时间(程序都需要加载到内存才能运行,因而内存应该是尽可能的保证快)
      • 硬盘中或者网络传输用 utf-8(或 gbk等),网络I/O延迟或磁盘I/O延迟要远大与utf-8的转换延迟,而且I/O应该是尽可能地节省带宽,保证数据传输的稳定性。
        这里写图片描述
    • encode 和 decode 方法
      • decode 的作用:是将其他编码的字符串转换成 Unicode 编码,如 str1.decode(‘gbk’),表示将 gbk 编码的字符串 str1 转换成 Unicode 编码
      • encode 的作用:是将 Unicode 编码转换成其他编码的字符串,如 str2.encode(‘UTF-8’),表示将Unicode 编码的字符串 str2 转换成 UTF-8 编码
      • 因此,使用 Python 转码的时候一定要先搞明白,字符串 str 是什么编码,先 decode 成 Unicode,然后再 encode 成其他需要的编码格式
        这里写图片描述
  • 源码的保存(硬盘)与加载(内存)

    • 保存到硬盘时,若不指定保存时的编码方式(字符到二进制),在 linux 操作系统中为 utf-8,windows 操作系统中为 gbk
      • 在 linux 操作系统的终端中可使用locale查看
      • 或者在 python 程序中导入 import locale,然后通过 print(locale.getdefaultlocale())查看
    • 加载到内存时,使用源码开头声明的编码方式(需和保存时一致),若不声明,python2 默认使用 ASCII,python3 默认使用 utf-8
      • python3 中会自动将加载后的源码转换成 unicode 编码(内存中),此时只能 encode,不能 decode
      • python2 中不会主动帮你转 unicode,所以加载到内存中的编码方式不会改变,为二进制码流(内存中)
      # 更改 py2 解释器的默认编码方式(ASCII-->UTF-8)
      import sys
      import locale
      reload(sys)  # 这里必须reload一下才能找到 setdefaultencoding method
      sys.setdefaultencoding('utf-8')
      
      # 打印python解释器的默认编码方式和操作系统的默认编码方式
      print(sys.getdefaultencoding())
      print(locale.getdefaultlocale())
      
      # py2 通过在codecs.open中设置保存的编码方式
      import codecs
      with codecs.open("filename", "w", encoding="utf-8") as f:
          f.write(u)
      
      with codecs.open("somefile", "r", encoding="utf-8") as f:
          content = f.read()
      
  • python 中的字符串表示形式:

    • 在 python2 中,str contains sequences of 8-bit values, unicode contains sequences of Unicode characters
    • 在 python3 中,bytes contains sequences of 8-bit values, str contains sequences of Unicode characters

    在这里插入图片描述

    # coding:utf-8
    # py2,ubuntu
    x = '满'
    
    print x, repr(x), len(x), type(x)
    print x.decode('utf-8'), repr(x.decode('utf-8')), len(x.decode('utf-8')), type(x.decode('utf-8'))
    # python2 中 print x,会按照终端的编码执行 x.decode('终端编码'),变成 unicode 后,再打印
    # python3 中,是什么就打印什么
    
    # 输出结果,注意切片的时候长度的提取,必要时转成 unicode 来切片'\xe6\xbb\xa1' 3 <type 'str'>u'\u6ee1' 1 <type 'unicode'>
    
    
    # python3 总是返回 str
    def to_str(bytes_or_str): 
    	if isinstance(bytes_or_str, bytes):
    		value = bytes_or_str.decode(‘utf-8)
    	else: 
    		value = bytes_or_str
    return value  # Instance of str
    
    
    # python3 总是返回 bytes 
    def to_bytes(bytes_or_str): 
    	if isinstance(bytes_or_str, str):
    		value = bytes_or_str.encode(‘utf-8)
    	else: 
    		value = bytes_or_str
    return value  # Instance of bytes
    
    
    #######################################
    
    
    # python2 总是返回 unicode 
    def to_unicode(unicode_or_str): 
    	if isinstance(unicode_or_str, str): 
    		value = unicode_or_str.decode(‘utf-8)
    	else: 
    		value = unicode_or_str
    return value  # Instance of unicode
    
    # python2 总是返回 str 
    def to_str(unicode_or_str): 
    	if isinstance(unicode_or_str, unicode): 
    		value = unicode_or_str.encode(‘utf-8)
    	else: 
    		value = unicode_or_str
    return value  # Instance of str
    

  • Anaconda:集成了很多关于Python科学计算的第三方库

    • Jupyter QtConsole(IPython ):验证思路
    • Jupyter Notebook(New-----Python[Root]-----新建.ipynb文件):分享和演示
    • Numpy、Scipy、Pandas、Matplotlib…
    • 在终端/命令行中安装第三方库:conda install package
    • 在终端中查看安装了哪些工具包:conda list
    • 在终端中产生一个自定义环境:
      conda create -n py35(可换成其它名字) python=3.5
      source activate py35              # 使用时激活环境
      source deactivate                 # 离开此环境
      
  • Pycharm(编写完整项目与调试):

    • 设置编译器和编码方式
      # -*- coding: utf-8 -*-
      # !/usr/bin/env python2
      # 文件头部的编码方式要与编译器中保存的编码方式一致
    • 环境配置--------Pycharm 常用配置及快捷键

十、参考资料

1、吐血总结,彻底明白 python3 编码原理
2、Python字符编码之理解
3、python 之路,致那些年,我们依然没搞明白的编码
4、廖雪峰字符串和编码
5、Python字符编码详解
6、图解 Python 深拷贝和浅拷贝
7、谈谈 Python 中的深拷贝和浅拷贝
8、Python 直接赋值、浅拷贝和深度拷贝解析

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值