A篇,主要介绍Python基础中列表,元祖,字典,集合,字符串,I/O,条件与循环,异常处理的底层知识与使用的注意事项。
但愿你们对于Python会有新的收获,本篇不一样于以前的浅谈Python基础知识,会更加的深刻,可是在深刻的同时,涉及更多内容,须要自行看源码,着重点我会标明。html
目前全部的文章思想格式都是:知识+情感。 知识:对于全部的知识点的描述。力求不含任何的自我感情色彩。 情感:用我本身的方式,解读知识点。力求通俗易懂,完美透析知识。
因为本文不是基础篇,因此重要的名词与知识点,若是不懂,就必定要多google.python
使用过jupyter的人都知道,我简短作一下jupyter的介绍。git
Jupyter Notebook 官网: https://jupyter.org/github
** Jupyter 创始人: ** Fernando Pérez数据库
Jupyter 单词的解释: Ju (Julia)、Py (Python)和 R 三种科学运算语言的计算工具平台,因此将其命名为 Ju-Py-te-R。json
Jupyter能干什么: 把软件代码、计算输出、解释文档、多媒体资源整合在一块儿的多功能科学运算平台c#
建议: jupyter属于我定义的工具范畴,是加快学习的工具,因此须要掌握。
备注:关于jupyter在Python的安装与使用,我在IPython介绍里面已经写过。数组
列表内能够放置的内容: 任意数据类型。(有序)浏览器
特性: 列表是动态的,长度大小不固定,能够随意地增长、删减或者改变元素(mutable)。缓存
list操做: 多使用append(),enumerate(),切片(注意步长的正负),多使用[....]建立list(直接识别调用内置C函数),少使用list()建立(使用函数方法,会建立stack,并进行操做的检验,expensive!)
时间复杂度:
增长 / 删除的时间复杂度均为 O(1)。
列表进行排序,而后使用二分查找,时间复杂度 O(logn) .
列表排序须要时间: O(nlogn)
遍历列表的时间复杂度是: O(n)
两层列表循环,在最差状况下,须要 O(n^2) 的时间复杂度。
list使用场景: 存储的数据和数量是不变的
底层实现: 在cpython中,为了减少每次增长 / 删减操做时空间分配的开销,Python 每次分配空间给列表的时候,都会额外多分配一些,这样的机制(over-allocating)。
源码角度:
浏览器输入:
1)https://github.com/python/cpython/blob/949fe976d5c62ae63ed505ecf729f815d0baccfc/Include/listobject.h#L23
2)https://github.com/python/cpython/blob/3d75bd15ac82575967db367c517d7e6e703a6de3/Objects/listobject.c#L33
显示list源码关键内容以下:
#ifndef Py_LIMITED_API typedef struct { PyObject_VAR_HEAD /* Vector of pointers to list elements. list[0] is ob_item[0], etc. */ PyObject **ob_item; /* ob_item contains space for 'allocated' elements. The number * currently in use is ob_size. * Invariants: * 0 <= ob_size <= allocated * len(list) == ob_size * ob_item == NULL implies ob_size == allocated == 0 * list.sort() temporarily sets allocated to -1 to detect mutations. * * Items must normally not be NULL, except during construction when * the list is not yet visible outside the function that builds it. */ Py_ssize_t allocated; } PyListObject; #endif
源码解读:
列表(list)本质是 over-allocate 的数组(array)。(注意,数组就是数组,列表就是列表,数组不是列表,列表不是数组,不是一个概念。)
ob_item是一个指针列表,里面的每个指针都是指向列表的元素。(这就是为何列表可变)
allocated 表示的是当前列表确实被分配的空间大小。
ob_size 是列表的实际大小,实际大小是len(list) 得到结果,表示当前列表共存储了多少个元素。
为优化存储结构,避免每一次更改元素都从新分配内存,列表的分配空间allocated 会大于 ob_size。即: allocated >= len(list) == ob_size。
当分配给列表的空间满了的时候,就是allocated == len(list) 的状况,此时会向操做系统申请更大的内存空间,并将已经存在的元素所有拷贝到新的内存中。
每个向操做系统申请内存的大小变化是:0, 4, 8, 16, 25, 35, 46, 58, 72, 88....
元祖内能够放置的内容: 任意数据类型。(有序)
特性: 元祖是静态的,长度固定,没法增长删减或改变。
注意:
1) 只要对元祖修改,必然是开辟一块新的内存,将原有数据从新写入。
2)虽然属于不可变数据类型,可是内部嵌套可变数据类型,可变数据类型仍属于动态。
tuple使用场景: 存储的数据和数量是变化的。
底层实现: Python内部对于静态资源 ,存在资源缓存(resource caching)。
解释一下,资源缓存就是静态数据当不被使用而且占用内存空间不大的时候,Python会对这些 资源进行缓存处理,并不会因为垃圾回收机制而将数据返回给操做系统。当下次建立静态数据的时候,会直接使用已经缓存的内存空间,此时就节省了与操做系统交互的环节。
当tuple的大小不超过20,Python将其缓存子啊一个free list中,当下一次建立一样的tuple的时候,就直接去缓存中取出来,提升运行效率。
源码角度:
浏览器输入:
1)https://github.com/python/cpython/blob/3d75bd15ac82575967db367c517d7e6e703a6de3/Include/tupleobject.h#L25
2)https://github.com/python/cpython/blob/3d75bd15ac82575967db367c517d7e6e703a6de3/Objects/tupleobject.c#L16
显示tuple源码关键内容以下:
#ifndef Py_LIMITED_API typedef struct { PyObject_VAR_HEAD PyObject *ob_item[1]; /* ob_item contains space for 'ob_size' elements. * Items must normally not be NULL, except during construction when * the tuple is not yet visible outside the function that builds it. */ } PyTupleObject; #endif
源码解读:
tuple本质上也是一个数组(array),可是空间是固定的。而且tuple作了相应的优化(资源缓存),提高程序的效率。
字典 :是一系列由键(key)和值(value)配对组成的元素的集合。
key必须是不可变数据类型。(只有不可变数据类型才能够hash)
字典变成有序的啦!!!
在 Python3.7+,字典被肯定为有序。
在 3.6 中,字典有序是一个 implementation detail,在 3.7 才正式成为语言特性,所以 3.6 中没法 100% 确保其有序性。
而 3.6 以前是无序的,其长度大小可变,元素能够任意地删减和改变。
字典的操做:
多使用{....}建立,少使用dict() 建立。
多使用.get()获取值,少使用[...]获取值。(get不报错,并能够本身指定没找到的返回值)。
常使用pop(), items(), keys(), values()。
时间复杂度:
存储,删除,增长数据的时间复杂度是: O(1)
字典的存储结构:
此时,须要回忆数据库的表结构。
老版本 Python 的哈希表结构以下:(随着数据量的增长,表会变的稀疏,空间利用率低)
它是一个 over-allocate 的 array,根据元素键(key)的哈希值,来计算其应该被插入位置的索引。
--+-------------------------------+ | 哈希值 (hash) 键 (key) 值 (value) --+-------------------------------+ 0 | hash0 key0 value0 --+-------------------------------+ 1 | hash1 key1 value1 --+-------------------------------+ 2 | hash2 key2 value2 --+-------------------------------+ . | ... __+_______________________________+
新版本哈希表,除了字典自己的结构,会把索引和哈希值、键、值单独分开,看下面:(提升空间利用率)
它把存储结构分红了 Indices 和 Entries 这两个 array,而’None‘表明这个位置分配了内存但没有元素。
Indices ---------------------------------------------------- None | index | None | None | index | None | index ... ---------------------------------------------------- Entries -------------------- hash0 key0 value0 --------------------- hash1 key1 value1 --------------------- hash2 key2 value2 --------------------- ... ---------------------
字典的工做原理:
1)计算key的哈希值,hash(key)
2)mask = PyDicMinSize - 1 和哈希值作与操做,计算这个元素应该插入哈希表的位置 index = hash(key) & mask。
3)若是哈希表此处是空的,那么这个元素就会被插入。
4)若是这个元素不是空的,分类讨论:
a:比较两个元素的哈希值和键是否相等,若是相等,代表这是同一个元素,若是值不一样,增跟新字典的value,反之,啥都不错。
b:比较两个元素的哈希值和键是否相等,若是不相等,此时哈希冲突(hash collision 意思是两个元素的键不相等,可是哈希值相等。)此时,须要一种解决冲突的策略。(最简单是线性查找,即从这个位置开始,挨个日后寻找空位,找到就插进去。可是Python不使用这个,由于线性效率不高)
注意:哈希冲突状况的发生,会下降字典的查找等操做速度。可是,哈希冲突几率极小,可是不表明不发生。
因此哈希表,一般会保证其至少留有** 1/3 的剩余空间。随着元素的不停插入,当剩余空间小于 1/3 时,Python 会从新申请获得更大的内存空间,扩充哈希表。此时,表内全部的元素位置都会被从新排放**。
集合: 没有键和值的配对,是一系列无序的、惟一的元素组合。
集合本质: 是一个 哈希表。(不能索引)
集合操做:
不要使用pop()。(集合无序,pop谁???)
时间复杂度:
添加与查找的时间复杂度是 O(1)
列表的哈希表:彻底等同于字典的哈希表思路,只是没有value而已。
字符串: 是由独立字符组成的一个序列,一般包含在单引号('')双引号("")或者三引号之中(''' '''或""" """,二者同样)。(不可变数据类型)
字符串操做:
字符串的算数运算: + 、*
多用 .join() 函数,实现字符串拼接
多使用 strip() ,split()
时间复杂度:
更改字符串的时间复杂度是O(n)
字符串的+操做:
从 Python2.5 开始,每次处理字符串的拼接操做时(str1 += str2),Python 首先会检测 str1 还有没有其余的引用。
若是没有的话,就会尝试原地扩充字符串 buffer 的大小,而不是从新分配一块内存来建立新的字符串并拷贝。
时间复杂度为 O(n) ,因此就能够直接使用+ 了吗??
字符串的格式化
官方推荐使用: ''.format() ,是最新的字符串格式函数与规范
可使用以前的方法: % 进行格式化。%s 表示字符串型,%d 表示整型。
I/O表示的是输入输出,不知道你们会不会直接想到,input()与print(),文件操做。
input() 函数暂停程序运行,同时等待键盘输入;直到回车被按下,函数的参数即为提示语,输入的类型永远是字符串型(str)。
因此,针对输入的str通常须要进行强制转换为 int 用 int(),转为浮点数用 float()。
Python 对 int 类型没有最大限制,可是对 float 类型有精度限制。
注意:在生产环境中使用强制转换时,请记得加上 try .....except(即错误和异常处理)。
文件操做:
生产级别的 Python 代码,大部分 I/O 则来自于文件、网络、其余进程的消息等。
计算机内核(kernel)对文件的处理相对比较复杂,涉及到内核模式、虚拟文件系统、锁和指针等一系列概念。建议全部的 I/O 都应该进行错误处理。
文件操做概览:
1)open()函数
2)打开文件方式 r (默认),w, a .... (注意:只须要读取文件,就不要请求写入权限)
3)read() (注意:文件过大的处理方法,能够给 read 指定参数 size ,用来表示读取的最大长度。还能够经过 readline() 函数,每次读取一行)
4)write()
5)close()
with上下文管理:
自动判断关闭打开的文件,代码简洁,不须要close()的出现。
JSON序列化:
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它的设计意图是把全部事情都用设计的字符串来表示,这样既方便在互联网上传递信息,也方便人进行阅读(相比一些 binary 的协议)。
JSON操做
json.dumps() 这个函数,接受 Python 的基本数据类型,而后将其序列化为 string;
而 json.loads() 这个函数,接受一个合法字符串,而后将其反序列化为 Python 的基本数据类型。
注意: 进行异常捕获。
JSON业务场景: 当开发一个第三方应用程序时,能够经过 JSON 将用户的我的配置输出到文件,方便下次程序启动时自动读取。
扩展: 在 Google,有相似的JSON工具叫作 Protocol Buffer,已经开源。
它的优势是生成优化后的二进制文件,所以性能更好。但与此同时,生成的二进制序列,是不能直接阅读的。
条件: if 语句是能够单独使用的,但 elif、else 都必须和 if 成对使用
注意:除了 boolean 类型的数据,条件判断最好是显性的。
if num != 0: print(num)
循环:
分为while循环和for循环。而且可使用break与continue.
要尽可能避免多层嵌套的状况
range函数:直接由 C 语言写的,调用它速度很是快。而且特是一个迭代器。
>>> d = range(10) >>> d range(0, 10) >>> type(d) <class 'range'>
三元运算
使用三元运算能够简洁明了,配合列表与字典的生成式,此时达到最高境界~(一行搞定千言万语)
错误至少包括两种,一种是语法错误,另外一种则是异常。
语法错误: 就是各类Error.....
官方解释:https://docs.python.org/3/library/exceptions.html#bltin-exceptions
异常: try ... except ... finally.. 来解决异常。
注意:except block 只接受与它相匹配的异常类型并执行,若是程序抛出的异常并不匹配,那么程序照样会终止并退出。
万能异常爸爸,Exception。Exception 是其余全部非系统异常的基类,可以匹配任意非系统异常
在 finally 中,一般会放一些不管如何都要执行的语句。
自定义异常:
raise 主动抛出异常。
异常应用场景:
大型社交网站的后台,须要针对用户发送的请求返回相应记录。用户记录每每储存在 key-value 结构的数据库中,每次有请求过来后,咱们拿到用户的 ID,并用 ID 查询数据库中此人的记录,就能返回相应的结果。
而数据库返回的原始数据,每每是 json string 的形式,在 json.loads() 函数中,输入的字符串若是不符合其规范,那么便没法解码,就会抛出异常,所以须要加上异常处理。
注意: 正常的 flow-control 逻辑,不要使用异常处理,直接用条件语句解决就能够了。
通常来讲异常抛出,都会对其进行Log(通常每1000次log一次),输出到real time的table和dashboard里,这样有利于以后的分析和改进。
except以后的delete操做:
"When an exception has been assigned using as target, it is cleared at the end of the except clause." # 在异常处理的 except block 中,把异常赋予了一个变量,那么这个变量会在 except block 执行结束时被删除 # 在平时写代码时,必定要保证 except 中异常赋予的变量,在以后的语句中再也不被用到。 好比下面这个code block: except E as N: foo 就等于 except E as N: try: foo finally: del N
本文涵盖了我目前学习Python的基础知识的全部深度知识点。但愿你们学习的时候,能够区分出什么是深度与浅度,对深度加以理解。大不了就是github源码来一下.... 祝,你们阅读开心 C-: