zzzzou 阅读(10) 评论(0)

Python基础知识2

标签:python

目录:

[TOC]


一、二进制

计算机内部使用二进制表示数据。1位二进制可以代表2种状态,n位二进制可以表示2^n种状态,一般使用如下图示来计算一个字节的二进制和十进制互转:

        128    64    32    16    8    4    2    1
256     1      1     1     1     1    1    1    1
78      0      1     0     0     1    1    1    0
33      0      0     1     0     0    0    0    1
6       0      0     0     0     0    1    1    0

二、文字编码-基础

为什么要有文字编码?

不论是文字、图片、视频还是其他类型的数据,在计算机内部都是以二进制的形式存储和表示。对于文字类型的数据,二进制需要通过文字编码表的映射关系,将二进制数据转换为对应的文字。文字编码表预先收录了字形二进制数据的对应关系。但不同的国家因文字不同,所以产生了不同的编码表,编码表也叫作编码格式。

有哪些编码格式?

因不同国家的文字不同,各国都编写了适合本国语言的编码格式。如GBK是中国的编码格式,ASCII是美国的编码格式,SHIFT_JIS是日本的编码格式等等。不同的编码格式给出了不同的二进制和字形的对应关系标准,所以不同编码格式之间不能混用,否则会出现乱码。

如何解决不同国家不兼容的编码格式?

正因为编码格式互相不兼容,国际标准组织才出面编写了一个全球各国通用的unicode编码格式,此编码格式将全世界的语言都囊括进去,不论是哪一个国家,只要使用unicode进行编码,输出的文件都可以在全球通用,所以unicode也称为:万国码(当然,读取的时候也要使用unicode编码进行读取)

unicode编码格式的缺点

但是,unicode编码格式要求一个字符至少对应2个字节的二进制数据,这就导致对于编码长度小于2个字节的语言比如英文来说,存储空间加倍,不但影响存储,还影响传输效率。

如何既能全球通用还可以规避unicode的缺点?

此时UTF-8编码格式应运而生,UTF-8在unicode基础上,使用可变长编码的方法,使用1-4个字节来表示一个字符,优先使用1字节,不够的话再增加1字节,以此类推。通过变长的方式用于优化unicode在存储和传输时带来的资源浪费。

python3的编码格式是什么样的?

简单来说,python3在内存中操作字符串时的编码格式统一是unicode,而在读写文件时使用的编码格式默认是UTF-8。


三、浮点数

python中的浮点数包括:有限小数(如:1.2)和无限循环小数(如:1/3)。
python中的浮点数精度最长只有小数点后16位,可以使用decimal模块来提高精度。使用python3的/运算得到的结果就是浮点数。此外,浮点数还用于科学计数法,如1000000的科学计数法写法是:1.0E6
当超出浮点数当前精度的时候会发生四舍五入的操作。


四、列表

如何理解列表?

列表可以简单的被理解成一个存储容器。此容器可以存放不同种类的对象,可以新增一个对象、删除一个对象、或者替换一个对象。列表类似于数组,只不过python的列表是动态的,不仅可以动态扩展和缩小长度,还可以存放不同的数据类型。列表使用如下方式声明:

    li = [0,1,2,3,4]
    或者
    li = list(range(5))

列表的操作有哪些?

1. 查询

li[0]或者li[-1]  # 通过下标索引对象值,不能超出下标范围
li.index('a')  # 通过对象值查找对应下标,不存在的对象会导致此函数报错ValueError
li.count('a')  # 得到列表中'a'值出现的次数

2. 增加

li.append(20)  # 在列表末尾追加元素
li.insert(0, 20)  # 在列表下标0出插入20,这会导致元素右侧统一右移一个单位

3. 修改

li[0] = 20  # 直接修改第一个元素的值为20
li[:3] = [1,2,3]  # 将列表前三个元素批量修改

4. 删除

li.remove(23)  # 根据元素值删除元素,不存在会报错ValueError
li.pop(0)  # 根据元素下标删除元素,不给于参数则默认-1
del li[1]  # 使用通用删除关键字del
del li[1:5]  # 使用del批量删除

5. 切片

li = [1,2,3,4,5]
L = li[:3]  # L的值是[1,2,3],切片含首不含尾
L = li[:]  # 将li列表进行浅复制赋予L
L = li[::-1]  # 将li列表倒序后浅复制赋予L

6. 更新/扩展

list_a.extend(list_b)  # 将b的值更新至a
list_a = [1,2]
list_b = [2,3]
list_a.extend(list_b)  # a的值是[1,2,3],b保持不变

7. 拼接

list_a = [1,2]
list_b = [2,3]
list_c = list_a + list_c  # c的值是[1,2,3],a和b保持不变

8. 排序

li.sort()  # 在原始列表上修改,排序默认方式是升序
li.reverse()  # 让列表倒序

9. 循环

for i in li:
    print(i)

10. 清空/复制

li.clear()
list_b = list_a.copy()  # 等价于list_b = list_a[:]

深浅复制问题

因为列表是可变对象,即列表中的元素是可以改变的,所以在复制列表的时候,如果使用简单的复制(即浅复制),遇到嵌套的小列表时就会出现问题。为了规避可变对象在修改值时的安全问题,对于可变对象的复制,建议使用深复制。深复制的使用方式如下:

import copy
list1 = [1,2,3,[4,5],6]
list2 = copy.deepcopy(list1)
list1[-2][-1] = 'abc'
print(list1)
print(list2)

结果是:
[1, 2, 3, [4, 'abc'], 6]
[1, 2, 3, [4, 5], 6]

五、字符串

什么是字符串?

字符串就是一串字符序列,字符串在计算机中也是二进制数据,这些二进制数据通过编码格式显示成对应的字形,即字符串。字符串是不可变对象,一旦被创建就无法被修改。

字符串的常用方法

1.判断是否是数字字符串

str.isdigit()

s = '123'
s.isdigit()  # 值是True
s = '123b'
s.isdigit()  # 值是False

2. 替换字符

str2 = str1.replace(old, new)  # 返回一个新字符串

s1 = 'aaahello,world'
s2 = s1.replace('a', 'A')
print(s1)
print(s2)

结果:
aaahello,world
AAAhello,world

3. 定位字符

i = str.index(char)  # 不存在会报错
i = str.find(char)  # 不存在返回-1

s1 = 'aaahello world'
print(s1.index('a'))
print(s1.index('b'))
print(s1.find('a'))
print(s1.find('b'))

结果:
0
报错ValueError
0
-1

4. 字符计数

count = str.count(char)  # 查找字符串中char字符的个数

s1 = 'aaahello world'
print(s1.count('a'))

结果:
3

5. 处理首尾空格

str2 = str1.strip()  #  返回新字符串,删除首尾空格

s1 = '    hello world'
print(s1)
print(s1.strip())

结果:
    hello world   
hello world

6. 处理banner展示条

str2 = str1.center(len, char)  # 使用char填充字符串长度到len

s1 = 'student info'
print(s1.center(20, '*'))
print(s1.center(20, '-'))
print(s1.center(20, '+'))

结果:
****student info****
----student info----
++++student info++++

7. 分割字符串产生列表

list = str.split(char)  # 根据char字符分割字符串成列表,默认cahr是空格

s1 = 'student!info!xiaoming'
li = s1.split('!')
print(type(li))
print(li)

结果:
<class 'list'>
['student', 'info', 'xiaoming']

8. 拼接字符串列表成更长字符串

str = char.join(list)  # 根据char字符连接list里的所有字符串成为更长的字符串

li = ['student', 'info', 'xiaoming']
s1 = '+'.join(li)
print(type(s1))
print(s1)

结果:
<class 'str'>
student+info+xiaoming

9. 字符串格式化输出

str2 = str1.format(*args, **kw)  # 根据args和kw来对应赋值

result = 'name:{name}, age:{age}, a {0},a {1}'.format('apple', 'bananer', name='xiaoming', age=26)
print(result)

结果:
name:xiaoming, age:26, a apple,a bananer

10. 判断字符串开头和结尾

bool = str.startwith/endwith(char)  # 判断是否以char开头或结尾

s1 = 'hello'
s2 = 'world'
print(s1.startswith('h'))
print(s2.endswith('d'))

结果:
True
True

11. 改变字符串大小写

str2 = str1.upper/lower()  # 设置大小写

s1 = 'hello world'
print(s1.upper())

结果:
HELLO WORLD

使用r取消字符串转义

在字符串前面增加一个r可以取消此字符串在print过程中的转义功能,从而打印出字符串字面量。

s1 = '\ta\nbc'
print(s1)
s2 = r'\tabc\n'
print(s2)

结果:
    a
bc
\tabc\n

六、元组

元组和列表有什么区别?

元组可以认为是只读列表,元组的操作大部分和列表一致,只不过一旦定义完毕之后就不能修改元组元素的值。
但是,如果元组元素是一个可变对象比如列表,则可以修改此列表中的值,这并不违反元组的规则因为列表自身这个容器的内存地址并没有变化。

tu = (1,2,[3,4])
# tu[0] = 10   # 这条语句会报错
tu[-1].append(5)
print(tu)

结果是:
(1, 2, [3, 4, 5])

七、哈希

  1. 哈希函数用于将任意长度的输入通过算法计算得到固定长度的输出,这个输出也叫哈希值。
  2. 哈希算法是根据输入值的特征计算的,所以如果输入值不停变化,则输出也会不稳定,这就要求输入的是不可变对象。
  3. 因为哈希算法的原因,可能会出现不同的输入值有同样的输出值,这就发生了哈希冲突,所以想通过输出来反推唯一的输入是不合理的。
  4. 数据中保存的用户密码都是密文。
  5. 使用哈希的程序一定要有处理冲突的模块。

八、字典

什么是字典?

字典在某种角度上和列表很类似,都可以被认为是存储工具,保存各种数据类型对象。不过列表是通过下标索引,而字典是通过key来索引。另外,列表是有序的,而字典是无序的,因为字典的key涉及到哈希算法的计算。

数据类型 是否有序 索引值 作用 是否可变对象 使用方式
列表 有序 下标,从0开始 存储其他对象地址 li[0],li[-1]
字典 无序 key值,不可变对象 存储其他对象地址 dic[key]

字典常用操作

1. 增加

字典没有追加的函数,增加一个key-value的方式是直接:
dic['name'] = 'xiaoming'
如果dic中已经存在'name'的key,则会使用'xiaoming'来覆盖原有的value

2. 修改

字典的修改直接是:
dic['name'] = new_value
将新的值覆盖原有值

3. 查找

方法1:print(dic['name'])
如果dic没有'name'的key,则会报错

方法2:print(dic.get('name', 'not found!')
如果dic没有'name'的key,则会返回'not found!',默认第二参数是None,可以不填写

4. 删除

方法1:del dic['name']
如果dic没有'name'的key,则会报错

方法2:dic.pop('name', 'not found!')
如果dic没有'name'的key,则会返回'not found!',第二参数没有默认值,必须手工输入否则会报错

5. 清空/复制

dic.clear()
dic2 = dic1.copy()  # 浅复制

6. 设置默认

dic.setdefault('name', 'xiaoming')
首先,dic会查找自身是否有'name',如果有返回原有value值。如果没有,就新增这个key-value并返回新value值。

7. 更新

dic1.update(dic2)
使用dic2来更新dic1,有则覆盖,无则新增。

dic1 = {
    'a': 1,
    'b': 2,
    'x': 3,{'a': 1, 'b': 2, 'x': 100, 'y': 200}
}

dic2 = {
    'b': 2,
    'x': 100,
    'y': 200,
}

dic1.update(dic2)
print(dic1)

结果是:
{'a': 1, 
 'b': 2, 
 'x': 100, 
 'y': 200
}

8. fromkeys

为一系列key设置统一的value值,并返回此字典。比如要为3个房地产楼盘设置统一价格:

dic = dict.fromkeys(['万科', '阳光城', '融信'], 2000)
print(dic)

结果是:
{'万科': 2000, '阳光城': 2000, '融信': 2000}

9. 循环获取keys、values、items

for key in dic.keys()
for value in dic.values()
for item in dic.items()

九、集合

什么是集合?

集合一般用于分类计算。集合中的元素都是唯一的,不会有重复。集合是无序的,集合是可变对象,但是集合内的元素必须是不可变对象。因为集合一般用于分类计算,是站在集合维度的,所以集合并不支持s[0]这种方式来访问元素。

集合基本操作

s = {1,2,3,4,5}
print(s.pop())  # 随机删除一个元素
s.add(50)
s.add(60)
print(s)
s.discard(100)  # 删除元素,不存在不会报错
# s.remove(100)  # 100不存在就会报错
s2 = {100,200,300}
s.update(s2)  # 两个集合合并,其实就是并集
print(s)
s.clear()   # 清空集合元素
print(s)

结果是:
1
{2, 3, 4, 5, 50, 60}
{2, 3, 4, 5, 100, 200, 300, 50, 60}
set()   # 代表空集合

集合分类计算

s1 = {11,22,33}
s2 = {22,33,44}
print('交集是:', s1 & s2)  # 交集是: {33, 22}

print('s1 对于 s2 的差集是:', s1 - s2)  # s1 对于 s2 的差集是: {11}
print('s2 对于 s1 的差集是:', s2 - s1)  # s2 对于 s1 的差集是: {44}


print('并集是:', s1 | s2)  # 并集是: {33, 11, 44, 22}

print('对称差集是:', s1 ^ s2)  # 对称差集是: {11, 44}

print('s1是s2的超集吗?', s1.issuperset(s2), s1 > s2)  # s1是s2的超集吗? False False
print('s1是s2的子集吗?', s1.issubset(s2), s1 < s2)  # s1是s2的子集吗? False False

print('23在不在是s1中?', 23 in s1)  # 23在不在是s1中? False
print('s1 和 s2 是不是 不相交?', s1.isdisjoint(s2))  # s1 和 s2 是不是 不相交? False

十、文件编码--进阶

这里从:python3读取文件abc.txt,执行文件中的代码并在命令行展现内容的整个过程来理解各环节的编码问题。

文件准备
# abc.txt
str1 = '中国'
print(str1)
环境准备
python环境:python3.6.1
os环境:windows 10
abc.txt是使用pycharm编辑的,pycharm的编码设置为UTF-8

开始

  1. 我们登录windows操作系统,windows操作系统默认的编码格式是GBK,windows中有一个cmd命令行,此命令行的编码格式继承了操作系统的格式,也是GBK
  2. 打开pycharm,默认pycharm在安装完毕后的编码格式是GBK,可以通过设置修改为UTF-8。
  3. 在pycharm中创建文件abc.txt,在此文件中写入:

    str1 = '中国'
    print(str1)
  4. 注意,此时abc.txt文件中的str1的编码格式是UTF-8,即编辑器pycharm是什么格式,则编写的文件就是什么编码格式。
  5. 使用Crtl+s保存此文件到硬盘。
  6. 单独在cmd中打开python解释器,进入交互环境,在交互环境中,我们可以输入:

    s1 = 'hello world'
    s2 = '你好 世界'
    不论你输入的是什么字符串,不论是中文还是英文,此时在python3交互环境中的编码格式都是统一的unicode
  7. 退出python3交互环境,在cmd命令中通过命令启动python3解释器执行abc.txt文件:python abc.txt
    第7步其实有很多小环节:

  1. python3会使用默认的读写文件的编码格式对abc.txt文件进行解释。python3默认读写编码格式是UTF-8,而此文件恰好也是以UTF-8格式保存在硬盘上的,所以python3可以正确的对此文件进行编码。
  2. python3通过UTF-8读写文件编码对此文件进行解释,将UTF-8的文件内容转换成unicode格式的字符串保留在内存中。
  3. python3解释器执行unicode的保存在内存中的代码,设置一个str1变量,创建一个unicode格式的'中国'
  4. python3执行print函数,此时str1依然是unicode格式
  5. print函数被执行,str1'中国'会被打印到cmd中
  6. cmd命令行接收到一个以unicode编码的字符串,虽然cmd默认是GBK编码,但是cmd可以认识GBKunicode,所以可以根据unicode编码规则打印出此字符串的字形:'中国'

总之,有如下几个环节是需要编码的:

  1. python3解释器内部编码,也就是在内存中的编码格式,就是unicode
  2. python3解释器去读取文件时使用的编码格式,默认是UTF-8,会按照你在文件头部指定的# coding: xxx来设定,不设定的话就是UTF-8
  3. 文件自身也有一个编码格式,此编码格式来源于编辑此文件的编辑器
  4. 编辑器的编码格式继承了操作系统的编码格式,mac/Linux是UTF-8,windows是GBK,此外,cmd和shell也继承了操作系统的编码格式

十一、几个技术问题

if 条件语句判断

a = [1]
if a:
    print('true')
else:
    print('false')
这里的if条件是判断成立的,但是如下两者却是false:
a == True
a is True

为什么?

首先,a和True不能用is比较,因为是肯定不同的两个对象内存地址。也不能用==比较因为是不同的数据类型,值是肯定不同的。其次,if a这个语句等价于:if boo(a),bool(a)的结果是True,所以可以判断成立。

字典的两种循环方法对比

dic = {
    'a': 1,
    'b': 2,
    'c': 3,
}

for key in dic.keys():
    print(key, dic[key])

for key, value in dic.items():
    print(key, value)
    
哪一种循环的性能更好?

尝试过两种办法:

  1. 两种循环各循环100次,计算运行时间,发现是下面那种时间短。
  2. 扩大dic的数据量到100万个项目,两种循环各执行1次,发现上面那种时间短。
    目前还不知道确切分析的方法,后续补充。

while...else...

用处:需要判断while是否完全正常执行完毕时
i = 0
while i < 10:
    print(i)
    i += 1
else:
    print('循环正常执行完毕,没有被break或者return打断')