目录
1.1基础入门
1.1.1Python简介
- 胶着型语言:可以和C语言,java语言融合使用.(GIL程序通常使用其他语言编写,而使用Python调用)
- 解释型语言:开发过程中无编译环节,运行速度比较慢.
- 交互式语言:意味着在一个Python提示符下直接互动执行程序.
- 面向对象语言:代码封装在对象的编程技术.
Python应用是否广泛,做网站,做游戏,做爬虫,做数据分析和挖掘,以及人工智能等
1.1.2环境搭建
1.1.2.1基础认知
- dll文件是一种动态链接库文件
- .exe文件是一种可执行文件
- ipconfig展示ip地址
- ping ip:检查本机是否与该ip地址连通
1.1.2.2Python安装
1.1.2.3PiP工具
- pip3 install 包名==版本号 包的安装
- pip3 uninstall 包名 包的卸载
- pip3 install -U?包名 包的更新
- pip list 当前环境中安装的包
- pip freeze>requestments.txt 将当前环境中的包名存放在文件中
- pip3 install requestments 安装该文件中的所有包
1.1.2.4PyCharm
1.1.2.5注意
- 系统PATH和用户PATH
- 配置环境变量
- 项目管理用虚拟环境
- 版本控制用Pyenv
1.2数据存储
1.2.1变量和常量
1.2.1.1变量
声明:
- 格式:变量名=值
- 规则:变量名符合小驼峰规则,类名符合大驼峰规则
- 起名规则:字母数字下划线可以有,数字特殊字符开头不能有
1.2.1.2常量
python中没有常量,python程序一般通过约定俗成的变量名全大写的形式表示这是一个常量。然而这种方式并没有真正实现常量,其对应的值仍然可以被改变。
MAX=100
print(MAX)
MAX=4
print(MAX)
1.2.2数据类型
1.2.2.1基本数据类型
int、float、str(原生字符串:print(r"\n"),保留格式字符串:三引号)、bool、bytes:a=b‘hello’
1.2.2.2高级数据类型
list、tuple、set、dict
1.2.2.3小整数对象池
范围:
- -5到256
含义:
- 当一个整数小于等于256大于等于-5的时候就将这个整数存放在小整数对象池中,当第二个变量想要和第一个变量存放的值相同的时候,就会直接接将第一个数的地址存放在第二个变量中,也就是说第一个变量和第二个变量所指的数是同一个值,然而当这个数超出了-5到256的范围的时候,即使第一个值和第二个值相同,但是他们都会存储在不同的位置.
检验方式:id(变量名)
- is和==的区别:==要求两个变量指向的内容的值相同;is两个变量指向同一个地址.
为什么PyCharm和Console中同一段代码输出的结果却不相同?
- 这是因为pycharm是将a=1000和b=1000当做一个块执行的,但是在控制台却是一句一句执行的,这样a和b的值即使相同,也会重新分配空间,因为一次执行完之后缓存清除。
1.2.2.4字符串驻留区
原理:
- 对于短字符串,将其赋值给多个不同的对象时,内存中只有一个副本,多个对象共享该副本。长字符串不遵守驻留机制.
优缺点:
- 驻留机制尽管可以提升时间和空间上的性能,但是对驻留对象也会有所消耗
Join和+号的区别:
- Python标识符的不可变性导致字符串的改动不是replace替换,而是重新创建对象.因此为了节省空间,在涉及到字符串的改动的时候通常使用的是Join函数而不是+,因为+会多次创建对象浪费空间,但是Join只会创建一次对象.
1.2.3数值运算
1.2.3.1算术运算符
符号:
- +、-、*、/、**、//
注意:
- 数值型的时候进行的是加法运算,字符串类型的时候进行的是字符串的连接操作
- 整型的时候表示乘法运算;如果是字符型的时候,表示的是字符串的n词重复, print(s1*3)
- 当数字和bool运算符运算的时候,False表示0;True表示1
1.2.3.2赋值运算符
=、+=、-=、*=、/=、==、!=
1.2.3.3关系运算符
==、 !=、 >、 < 、 >=、 <=、
当数字和bool运算符进行关系运算的时候False表示0;True表示1
1.2.3.4逻辑运算符
and or not
是假的情况:0、’ ’、None;
注意:‘0‘表示的不是假,因为这时候它表示字符串,不是空的。
1.2.3.5位运算
print(21>>3)
print(21//(2**3))
print(2<<3)
print(2*2**3)
print(0>>3) # 临界值问题
print(-8>>2)
按位与或非
a=2 # 0010
b=3 # 0011
print(a&b) # 0010=2
print(a|b) # 0011=3
print(a^b) # 异或:相同为0,不同为1
print(~a) # 0010--》1101--》1010--》1011--》-3
# a的按位非,首先将a转成二进制数0010,按位取反1101,由首位可知为负数,因此需要求它的补码,也就是反码加1最后得1011,因此结果是-3,注意在求补码的时候符号位是不变的
1.2.3.6身份运算符
is 、is not
is 和 is not 比较的是地址,可以通过id(变量)获取地址,返回值是True或者False
is是对身份的比较,地址的比较,在终端中是false,因为是单行解释,一行一结束,而在pycharm中是将整个文件全部加载在解释器中才去运行的,地址相同,因此结果为True,一个文件作为开始和结束。==比较的是内容
1.2.3.7成员运算符
in; not in
代码:
names=['zp','lw','wn','sy']
print("sy" in names)
print("sy" not in names)
1.2.3.8三目运算符
格式:
- 结果1(true) if 条件 else 结果2(false)
代码:
print(2 if 2>3 else 3)
1.2.3.9运算符的优先级
指数-》单目运算符-》双目运算符-》 位移运算符 -》&-》^ |-》关系运算符-》等于运算符-》赋值运算符-》身份运算符-》成员运算符-》逻辑运算符
1.2.4注释
- 单行注释其实是一种标记,计算机会认为#号之后的字符都不会被加载
- 多行注释其实是一个匿名变量,也就是如果一个变量没有被引用,那么系统就会把它当垃圾处理,因此执行的时候就不会被加载.
1.2.5原码反码和补码
"负数的补码等于原码的反码加一."对于计算机来说,所有的运算都是通过加法实现的,原码反码和补码的产生就是为了实现计算机的减法问题.
- 原码:是最简单的机器数表示法.最高位表示符号位,'1'表示负数,'0'表示正数.其他存放该数的二进制的绝对值.
- 反码:正数的反码等于原码,负数的反码就是他的原码按位取反(除符号位)
- 补码:正数的补码等于原码,负数的补码等于反码+1
为什么要存在补码?
- 让一个数加上这个补码形式表现的负数时,由于溢出自动丢失的效果,结果与减去这个负数的绝对值是相同的,这就是补码存放负数的实质
1.2.6进制转换
- 二进制转八进制:从低位到高位,每三位算出一位即可得到一个八进制数。(oct)
- 二进制转十进制:1010=2**3+2**1(int)
- 二进制转十六进制:从低位到高位,每四位算出一位即可得到一个十六进制数(hex)
- 八进制转十进制:567=5*8^2+6*8+7
- 八进制转十六进制:先转十进制再转十六进制
- 十进制转其他进制:使用这个十进制除以想要转化的进制,取余,用商继续除以相应的进制,以此类推,直到商为零为止,然后余数逆序,即可得到对应的进制转化。(bin)
二进制表示:0b01110;八进制表示
1.2.7字符串格式化
通过占位符格式化:
a=10
print('%d'%a)
- %d:表示整数,格式化的是浮点型,它会自动保留整数部分,此外报错
- %s:将左右格式化的东西转成字符串
- %.2f:格式化浮点数,小数点后保留两位小数
通过函数格式化:
for i in range(10):
print("10点{}分".format(i))
- format()
1.2.8注意
- 查看关键字:import keyword print(keyword.kwlist)
- 程序的执行顺序:检索自己-->检索外部函数-->检索内置函数:因此一般情况下我们不会让变量名和函数内置函数名重名,否则新建的变量名就会将原来的内置函数名覆盖掉.
- print()函数用来打印和测试
- type()函数用来测试变量数据类型
- builtins该模块会自动加载,主要是存放Python的内置函数.
- 原码反码补码和符号位有关,但是进制之间的转换是通过特殊字母表示符号位的.
1.3语句结构
1.3.1顺序结构
;略
1.3.2条件结构
格式:
if 条件1:
pass
elif 条件2:
pass
else:
pass
1.3.3循环结构
while循环
while 条件1:
pass
if 条件2:
break
else:
pass # 如果执行了break就不会执行该语句
for循环
for i in range(10):
print(i)
1.3.4break和continue
- break表示跳出最近一层的循环
- continux表示跳出本次循环
1.4数据类型
1.4.1字符串
1.4.1.1切片
格式
- s[start:stop:step]
- s[start:]---->从头到尾
- s[:stop]---->从0到stop
- s[start:stop]---->从start到stop-1
- s[start:stop:step]---->step步长
- 控制方向:step是负数 从右到左
- s[start:stop:-step]
代码
a='abcdefg'
# 取bd
print(a[1:4:2])
print(a[-6:-3:2])
# 取db
print(a[-4:-7:-2])
print(a[3:0:-2])
1.4.1.2相关函数
查找
- index:没找到报错
- find:没找到返回-1
- rindex:从右往左查找
- rfind:从左往右查找
替换:
- replace('需要替换的字符串','替换成的字符串',[替换的个数])
切割:
- split(分割符,[最大分割次数])
转换:
- upper()转大写
- lower()转小写
- title()每个单词的首字母转成大写
- capitalize()字符串的第一个字符转成大写
判断
- isupper()是否全是大写
- islower()是否全是小写
- isalpha()是否字符串中只有字母
- isdigit()是否字符串是纯数字
- startswith('指定字符串')是否以指定的内容开头
- endswith('指定字符串')是否以指定的内容结尾
获取长度
- len()获取字符串的长度
对齐方式:
- center(width):居中对其
- rjust(width):右对齐
- ljust(width):左对齐
字符串格式化:
- format()
祛除空格:
- strip()祛除字符串左右两边空格
- lstrip()祛除左边空格
- rstrip()祛除右边空格
1.4.2列表
1.4.2.1列表基础
列表的声明
- cars=[]
- cars=list('abcd')
列表的特点
- 没有长度的限制
- 可以存放任何数据类型
- 允许存放重复的元素
列表的切片
- 与字符串的切片方式是一样的:cars[start:end:step]
1.4.2.2内置函数
增加
- append(elem):列表末尾追加元素
- extend(object):将一个可迭代对象添加到末尾
- insert(index,object):将object对象插入到index位置
删除
- remove(elem):从列表中移除elem元素,值移除第一个找到的,如果没有找到就会报错
- pop([index]):默认删除最后一个元素,可以删除指定index位置的元素,并且返回删除的元素,如果超出范围则会报错
- clear():清空列表中的所有元素,保留地址
- del 列表:删除该列表,地址也没了
修改
- list[index]=新值:将旧值替换成新值
查询
- index(elem,[start],[end]):默认是从该列表中查找指定的elem元素的位置,现在是查找指定位置从start到end中元素elem的位置
注意:
isinstance(elem,iterable):判断指定元素elem是否是可迭代的
1.4.2.3其他相关
聚合函数
- list.max()
- list.min()
- list.sum()
- list.count(value):统计列表中指定值的个数
排序函数
- list.sort(reverse=True):对原列表进行排序(默认为升序,如果参数为True就是降序)
- list.reverse():对原列表进行翻转
- sorted(list):对原列表进行排序产生新的列表
- sorted(list,key,reverse=True):对原列表进行排序(降序)
import operator
tup=((7,4,6),(2,6,2),(9,4,7))
print(tup)
tup0=sorted(tup,key=operator.itemgetter(0))
print(tup0)
tup1=sorted(tup,key=operator.itemgetter(1))
print(tup1)
tup2=sorted(tup,key=operator.itemgetter(2))
print(tup2)
列表转字符串
- 分隔符.join(可迭代对象):将可迭代对象合并成字符串并且通过分隔符进行分隔
- list.copy() 拷贝的是变量中的内存地址,存放在一个新的位置
- str(list):是将整个列表转成字符串
- list(iterable):内部参数必须是可迭代的
符号问题
- +:相当于extend
- *:类似于字符串中的*的使用
- print('a' in ('a','b'))
1.4.3元组
1.4.3.1元组基础
元组的声明
- tuple=()
- tuple=tuple()
- tuple=(1,)
元组的特点
- 可以存放多个元素
- 类似于列表,但是不支持添加,删除,修改
元组的切片
- tuple[-1]
- tuple[::-2]
1.4.3.2增删改查
查找
- index(elem):没有找到的时候就会报错
增删改:
- 不支持
1.4.3.3其他相关
聚合函数
- tup.count('a'):统计元组中指定值的个数
- tup.max()
- tup.min()
- tup.sum()
排序函数
sorted(tuple):对元组进行排序
支持的符号
+、*、[]、in、is
1.4.4集合
1.4.4.1集合基础
集合的声明
- set1=set()
- set2={1,2,3,4}
集合的特点
- 无序
- 不重复
- 底层原理是哈希表
1.4.4.2增删改查
添加
- add(elem):添加单个元素
- update(iterable):一次添加多个元素,类似于列表中的extend
删除
- discard(elem):删除指定的元素,不存在不报错
- remove(elem):删除指定元素,不存在报错
- pop():随机删除其中一个元素
- clear():清空集合中的元素
- del set1:删除集合
查询和修改
- 不支持(原因:不支持切片)
1.4.4.3集合运算
交集intersection和&
set1={1,2,3,4,5}
set2={3,4,5,6,7}
print(set1.intersection(set2))
print(set1&set2)
并集union和|
a={1,2,3,4}
b={3,4,5,6}
c=a.union(b)
print(c)
差集differnce和-
a={1,2,3,4}
b={3,4,5,6}
c=a.difference(b)
print(c)
对称差集symetric_difference
a={1,2,3,4}
b={3,4,5,6}
c=(a|b)-(a&b)
# c=a.symmetric_difference(b)
print(c)
1.4.5字典
1.4.5.1字典基础
字典的声明
- dict=dict()
- dict={'name':'张三'}
1.4.5.2增删改查
添加
- dict={}
- dict['张三']=18
删除
- pop(key):根据key删除值,返回key
- popitem():随机删除键和值,返回键和值构成的元组
修改
- dict1[key]=value
查找
- get(key):获取对应的值
- keys():获取字典的键列表
- values():获取字典的值列表
- items():获取字典键值对组成的元组
1.4.6其他类型相关
1.4.6.1枚举类型
a=[1,2,3,4]
b=list('abcd')
for i in enumerate(b):
print(i)
for i in zip(a,b):
print(i)
1.4.6.2类型的转换
列表转字典fromkey
a=['jack','mary','Tom']
b=dict.fromkeys(a,'未知')
print(b)
列表转字符串
a=[1,2,3,4]
b=[str(i) for i in a]
c=''.join(b)
print(c)
字符串的切分
s='abc'
print(list(s))
s='1#2#3#'
print(s.split('#'))
list1=list('1234550')
print(list1)
其他相关的转换比较简单,在这里就不在赘述了.
1.5内存管理
1.5.1可变与不可变
- 可变数据类型:主要有:字典,集合,列表等.内存空间中值发生改变,如果对变量进行append.,加法操作后,改变的是变量的值,而不会重新创建对象,而是在原来的容器中添加,变量引用对象的地址不会发生改变,不过相同的值不会产生同一个对象,每个对象都有自己的内存地址,相当于内存对于同值对象保存了多份
- 不可变数据类型:主要有,数值型\字符型,元组,布尔类型等.每个变量的内存地址是固定的,如果变量的值发生了改变,原来变量的内存地址也会发生改变,创建的是一个新的变量,而不是在原来变量基础上的改变.
- 距离:x=3,x+=1之后x是一个进行的变量,此时x存储的变量地址和原来x存储的变量地址不是同一个变量,因此是不可变类,如果x=[1,2],x.append(3),此时的x和原来的x是同一个容器.
- 小结:只要改变变量的值,地址就会发生变化,认为此类型就是不可变的,也就是说,该变量所指的内容在内存中是固定的,一旦我们改变了变量的值,他就会在内存中重新找一个.这里的可变与不可变指的是某个容器中存储的数据在内存中是不是固定了,比如3这个整型在内存中的位置是固定的,因此不可变,列表中的数据你可以任意改,但是改完之后,该列表的地址没有改变,因此不可变.
1.5.2深拷贝与浅拷贝
深拷贝:拷贝的非常彻底,递归拷贝完所有的内存和地址,拷贝完成之后的数据和没有拷贝的数据之间除了数值相同之外,其他都各不相干.
import copy
a=[1,2,3,[5,6]]
b=copy.deepcopy(a)
print(id(a),id(b))
浅拷贝:拷贝的不彻底,只拷贝了第一层的值,其他各层没有拷贝值,只拷贝了地址.Python会分配一块新的内存用于创建新的拷贝对象,但拷贝对象中的元素依旧是原对象(被拷贝对象)中元素,即拷贝对象与原对象的内存地址不同,但两者中的元素具有相同的内存地址.存储内存地址的地方是分开的,但是内存地址指向的变量指的是同一个位置
import copy
a=[1,2,3,[5,6]]
b=copy.copy(a)
print(id(a),id(b))
1.5.3值传递与引用传递
- 值传递:传递的是存储变量的内容
- 引用传递:传递的是变量的内存的地址
1.5.4垃圾回收机制(内存管理机制)
引用计数为主,标记清除和分代回收为辅:
1.5.4.1引用计数
typedef struct_object {
int ob_refcnt; # 引用计数
struct_typeobject *ob_type;
}PyObject;
计数器加1的情况:
- 对象的创建
- 对象被引用
- 对象作为参数传递
- 对象作为元素存储在容器中
计数器减1的情况
- 对象被显示销毁
- 引用该对象的别名被赋予新的对象
- 对象离开它的作用域,也就是函数执行完毕
- 对象从容器中被删除
当指向该对象的内存引用计数器为0的时候,该内存将会被Python虚拟机销毁
引用计数的优点:
- 高效
- 实时性回收
- 对象有确定的生命周期
- 易于实现
引用计数的缺点:
- 维护引用计数比较消耗资源
- 无法解决循环引用的问题
为了解决循环引用的问题,我们使用标记清除和分带回收的方式:
1.5.4.2标记清除
Python的引用计数算法不能够处理互相指向自己的对象。你的代码也许会在不经意间包含循环引用并且你并未意识到。
标记清除
- 第一阶段:标记阶段,把所有活动对象打上标记
- 第二阶段:对没有进行标记的对象进行回收
活动对象和非活动对象的判别:
- 对象之间通过引用指针连在一起,构成有向图,对象构成有向图的节点,而引用关系构成有向图的边,从指向的对象出发,沿着有向边遍历对象,可达对象标记为活跃对象,不可达对象就是要清除非活动对象
标记清除算法作为Python的辅助垃圾收集技术主要处理的是一些容器对象,因为对于字符串、数值对象是不可能造成循环引用问题。Python使用一个双向链表将这些容器对象组织起来。不过,这种简单粗暴的标记清除算法也有明显的缺点:
- 清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象。
“标记-清除”法是为了解决循环引用问题。可以包含其他对象引用的容器对象(如list, dict, set,甚至class)都可能产生循环引用,为此,在申请内存时,所有容器对象的头部又加上了PyGC_Head来实现“标记-清除”机制。任何一个python对象都分为两部分: PyObject_HEAD + 对象本身数据
// objimpl.h
typedef union _gc_head {
struct {
union _gc_head *gc_next;
union _gc_head *gc_prev;
Py_ssize_t gc_refs;
} gc;
long double dummy; /* force worst-case alignment */
} PyGC_Head;
在为对象申请内存的时候,可以明显看到,实际申请的内存数量已经加上了PyGC_Head的大小
检测循环引用
随后,Python会循环遍历零代列表上的每个对象,检查列表中每个互相引用的对象,根据规则减掉其引用计数。在这个过程中,Python会一个接一个的统计内部引用的数量以防过早地释放对象。
没有被标记的就会被清除,标记清除需要对所有节点进行扫描,效率低,因此有了分代回收
1.5.4.3分代回收
- 分代回收是一种以空间换时间的操作方式,Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。同时,分代回收是建立在标记清除技术基础之上。分代回收同样作为Python的辅助垃圾收集技术处理那些容器对象.