python基础
Posted zxs117
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python基础相关的知识,希望对你有一定的参考价值。
python基础字符编码python2和python3的区别python2和python3中编码转换深拷贝、浅拷贝select、poll 、epoll(同步io)进程(资源分配的单位)、线程(操作系统调度的最小单位)、协程进程: 一个在运行的程序 系统给他分配资源 (运行在内存) 提资源线程: 操作系统最小的调度单位,状态保存在cpu的栈中协程: 微线程,纤程。 类似于单线程,不同于单线程,因为它可以实现并发效果,因为有greenlet 遇io自动切换(greenlet可以检测io),把这个io请求交给epollGIL全局解释器锁1、什么是GIL2、GIL的作用3、GIL的影响4、如何避免GIL的影响5 为什么有时候加了GIL全局解释器锁 ,还要在加线程锁?线程锁装饰器、迭代器、生成器迭代器 含有iter和next方法 (包含next方法的可迭代对象就是迭代器)迭代器的定义:迭代器和可迭代对象关系:迭代器仅是一容器对象,它实现了迭代器协议。它有两个基本方法iter方法会返回一个迭代器(iterator),所谓的迭代器就是具有next方法的对象。在调用next方法时,迭代器会返回它的下一个值,如果next方法被调用,但迭代器中没有值可以返就会引发一个StopIteration异常生成器装饰器装饰器必须准寻得原则:闭包os和sys模块的作用?面向对象面向对象优点:易维护,易扩展,效率高面向对象深度优先和广度优先是什么?三大特性:封装,继承,多态Encapsulation 封装(隐藏实现细节)Inheritance 继承(代码重用)Polymorphism 多态(接口重用)类方法属性方法:反射垃圾回收机制1.引用计数原理优点缺点2.标记-清除原理3.分代回收三种读文件方式1.readline()2.readlines()3.read(size)
python基础
字符编码
-
ASCII
ASCII不支持中文字符
-
GBK
是中国的中文字符(中国人自己研发的),其包含了简体中文和繁体中文的字符
-
Unicode : 万国编码(Unicode 包含GBK)
-
Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码
-
规定虽有的字符和符号最少由 16 位来表示(2个字节),即:2 **16 = 65536
-
这里还有个问题:使用的字节增加了,那么造成的直接影响就是使用的空间就直接翻倍了
-
-
Utf-8 : 可变长码, 是Unicode 的扩展集
-
UTF-8编码:是对Unicode编码的压缩和优化,他不再使用最少使用2个字节,而是将所有的字符和符号进行分类
-
ASCII码中的内容用1个字节保存、欧洲的字符用2个字节保存,东亚的字符用3个字节保存
-
python2和python3的区别
-
从编码的角度来说
python2 默认编码方式ASCII码(不能识别中文,要在文件头部加上 #-- encoding:utf-8 -- 指定编码方式)
python3 默认编码方式UTF-8(能识别中文,是Unicode 的扩展集)
(可识别中文)
-
从输出(print)的角度来说
python2中加不加括号都可以打印
python3中必须加括号
-
从输入(input)的角度来说
python2 raw_input()
python3 input()
-
从range()函数的角度来说
python2 range()/xrange()
python3 range()
-
从类的角度来说
python3 中都是新式类python2 中经典类和新式类混合
新式类中使用广度优先,经典类中使用深度优先
python3 可以使用superpython2 不能使用super
-
从字符串的角度来说
python2中字符串有str和unicode两种类型
python3 中字符串有str和字节(bytes) 两种类型
-
从语法格式角度来说
python3中不再支持u中文的语法格式
python2中支持
python2和python3中编码转换
-
在python3中字符串默认是unicode所以不需要decode(),直接encode(编码)成想要转换的编码如gb2312
-
在python2中默认是ASCII编码,必须先转换成Unicode,Unicode 可以作为各种编码的转换的中转站
深拷贝、浅拷贝
-
深拷贝就是将一个对象拷贝到另一个对象中,这意味着如果你对一个对象的拷贝做出改变时,不会影响原对象。在Python中,我们使用函数deepcopy()执行深拷贝
-
而浅拷贝则是将一个对象的引用拷贝到另一个对象上,所以如果我们在拷贝中改动,会影响到原对象。我们使用函数copy()执行浅拷贝
-
浅拷贝只是增加了一个指针指向一个存在的地址,
-
而深拷贝是增加一个指针并且开辟了新的内存,这个增加的指针指向这个新的内存。
select、poll 、epoll(同步io)
-
select,poll,epoll都是IO多路复用中的模型
-
内核空间 操作系统才能访问 操作系统 可以调用cpu 挂起进程
用户空间 如qq 微信 运行在用户空间
IO多路复用(重点)
特点: 用户还是要等待数据从内核拷贝到用户进程
大白话: 你们去干活 我不管你 让操作系统等你
Windows下只支持select
Epoll有回调 会把数据加到链表中
Select 不管你有没数据 都放到链表中
协程利用epoll greelit监听请求
协程又称微线程,协程最主要的作用是在类似单线程的条件下实现并发的效果,但实际上还是串行的(像yield一样),为什么能处理并发,因为遇IO自动切换,协程内封装Greenlet(遇到IO手动切换) 和Gevent(遇到IO自动切换),如果是耗时任务就会IO切换任务,会把任务交给操作系统,然后利用epoll,epoll有回调 会把数据加到链表中,告诉协程结果
协程一般用于网络请求 socket 链接
请求本身是从内核copy到用户空间
select (能监控数量有限(最大1024),不告诉用户程序具体哪个连接有数据)
poll(和select一样,仅仅去除了最大监控数量,算是epoll过度)
epoll (不仅没有最大监控数量限制,还能告诉用户程序哪个连接有活跃(数据))通常使用epoll
epoll() 中内核则维护一个链表,epoll_wait 直接检查链表是不是空就知道是否有文件描述符准备好了
在内核实现中 epoll 是根据每个 sockfd 上面的与设备驱动程序建立起来的回调函数实现的。
-
-
在高并发情况下, 连接活跃度不是很高, epoll比select好
-
在并发不高情况下, 连接活跃度高, select比epoll好
进程(资源分配的最小单位)、线程(操作系统调度的最小单位)、协程
线程是指进程内的一个执行单元,
# 进程
进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。
# 线程
线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度
# 协程和线程
协程避免了无意义的调度,由此可以提高性能;但同时协程也失去了线程使用多CPU的能力
?
进程与线程的区别
(1)地址空间:线程是进程内的一个执行单位,进程内至少有一个线程,他们共享进程的地址空间,而进程有自己独立的地址空间
(2)资源拥有:进程是资源分配和拥有的单位,同一个进程内线程共享进程的资源
(3)线程是处理器调度的基本单位,但进程不是
(4)二者均可并发执行
(5)每个独立的线程有一个程序运行的入口
?
协程与线程
(1)一个线程可以有多个协程,一个进程也可以单独拥有多个协程,这样Python中则能使用多核CPU
(2)线程进程都是同步机制,而协程是异步
(3)协程能保留上一次调用时的状态
进程: 一个在运行的程序 系统给他分配资源 (运行在内存) 提资源
线程: 操作系统最小的调度单位,状态保存在cpu的栈中
协程: 微线程,纤程。 类似于单线程,不同于单线程,因为它可以实现并发效果,因为有
greenlet 遇io自动切换(greenlet可以检测io),把这个io请求交给epoll
-
并行 :是多个程序运行在多个cpu上
-
并发 :是一个时间段内,有几个程序在同一个cpu上运行,但是任意时刻只有一个程序在cpu上运行
一般多线程可以称为并发 ,因为一个进程可以有多个线程,强调的是一个进程
-
详细介绍 https://v3u.cn/book/p4.html 1.5
GIL全局解释器锁
1、什么是GIL
-
GIL全称Global Interpreter Lock,即全局解释器锁。 作用就是,限制多线程同时执行,保证同一时间内只有一个线程在执行。
-
GIL并不是Python的特性,Python完全可以不依赖于GIL。
2、GIL的作用
-
为了更有效的利用多核处理器的性能,就出现了多线程的编程方式 ,同一个进程里线程资源是共享的,当各个线程访问数据资源时会出现竞状态 ,会出现数据混乱,解决多线程之间数据完整性和状态同步最简单的方式就是加锁。GIL能限制多线程同时执行,保证同一时间内只有一个线程在执行。
3、GIL的影响
-
GIL无疑就是一把全局排他锁。毫无疑问全局锁的存在会对多线程的效率有不小影响。甚至就几乎等于Python是个单线程的程序。
4、如何避免GIL的影响
-
方法一:用进程+协程 代替 多线程的方式 在多进程中,由于每个进程都是独立的存在,所以每个进程内的线程都拥有独立的GIL锁,互不影响。但是,由于进程之间是独立的存在,所以进程间通信就需要通过队列的方式来实现。
-
方法二 : 更换解释器
像JPython和IronPython这样的解析器由于实现语言的特性,他们不需要GIL的帮助。然而由于用了Java/C#用于解析器实现,他们也失去了利用社区众多C语言模块有用特性的机会。所以这些解析器也因此一直都比较小众。
5 为什么有时候加了GIL全局解释器锁 ,还要在加线程锁?
因为cpu是分片 ,分时的 ,如果GIL处理的任务特别繁琐,到一定时间会自动切换其他线程 造成混乱 ,所以要加一个线程锁。
线程锁
-
GIL(全局解释器锁)
GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,是为了实现不同线程对共享资源访问的互斥,才引入了GIL
在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势
-
互斥锁(同步锁)
互斥锁是用来解决上述的io密集型场景产生的计算错误,即目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据。
-
死锁
保护不同的数据就应该加不同的锁。所以当有多个互斥锁存在的时候,可能会导致死锁(有可能是一个线程拿到锁,并没有释放)
-
递归锁(重要)
解决死锁
-
Semaphore(信号量)
实际上也是一种锁,该锁用于限制线程的并发量
装饰器、迭代器、生成器
迭代器 含有iter和next方法 (包含next方法的可迭代对象就是迭代器)
迭代器的定义:
迭代器是访问集合内元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束。
迭代器和可迭代对象关系:
? 一个实现了iter方法的对象是可迭代的,一个实现next方法的对象是迭代器
迭代器仅是一容器对象,它实现了迭代器协议。它有两个基本方法
㈠ next**方法**
? 返回容器的下一个元素
㈡ iter方法
? 返回迭代器自身
iter方法会返回一个迭代器(iterator),所谓的迭代器就是具有next方法的对象。
在调用next方法时,迭代器会返回它的下一个值,如果next方法被调用,但迭代器中没有值可以返就会引发一个StopIteration异常
生成器
包括含有yield这个关键字,生成器也是迭代器,调动next把函数变成迭代器
生成器工作原理
-
生成器是这样一个函数,它记住上一次返回时在函数体中的位置。
-
对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。
yield生成器运行机制
1)在Python中,yield就是这样的一个生成器。
2)当你问生成器要一个数时,生成器会执行,直至出现 yield 语句,生成器把yield 的参数给你,之后生成器就不会往下继续运行。
3)当你问他要下一个数时,他会从上次的状态开始运行,直至出现yield语句,把参数给你,之后停下。如此反复
4)在python中,当你定义一个函数,使用了yield关键字时,这个函数就是一个生成器
5)它的执行会和其他普通的函数有很多不同,函数返回的是一个对象,而不是你平常所用return语句那样,能得到结果值。如果想取得值,那得调用next()函数
6)每当调用一次迭代器的next函数,生成器函数运行到yield之处,返回yield后面的值且在这个地方暂停,所有的状态都会被保持住,直到下次next函数被调用,或者碰到异常循环退出。
装饰器
装饰器作用:本质是函数(装饰其他函数)就是为其他函数添加其他功能
装饰器必须准寻得原则:
-
不能修改被装饰函数的源代码
-
不能修改被装饰函数的调用方式
闭包
在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
os和sys模块的作用?
OS模块是Python标准库中的一个用于访问操作系统功能的模块,使用OS模块中提供的接口,可以实现跨平台访问sys模块主要是用于提供对python解释器相关的操作
面向对象
面向对象优点:易维护,易扩展,效率高
class Role(object): #1、在定义类时继承object就是新式类,没有就是就是旧类式
public_object = "public" #2、在类例定义一个公有属性:所有实例都能访问的属性叫“公有属性”
def __init__(self,name,role,weapon,life_value=100,money=15000): #构造函数==初始化方法:模板
self.name = name #3、普通属性
self.__heart= "Normal" #4、私有属性在外面无法访问,在名字前加__(双下划线) 外部无法访问
def shot(self): #5、类的方法
print("%s is shooting..."%self.name)
面向对象深度优先和广度优先是什么?
1、区别
-
1) 二叉树的深度优先遍历的非递归的通用做法是采用栈,广度优先遍历的非递归的通用做法是采用队列。
-
2) 深度优先遍历:对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。要特别注意的是,二叉树的深度优先遍历比较特殊,可以细分为先序遍历、中序遍历、后序遍历。具体说明如下:
-
广度优先遍历:又叫层次遍历,从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止。
三大特性:封装,继承,多态
Encapsulation 封装(隐藏实现细节)
在面向对象思想中,对一个类,将数据和过程封包围起来,对外隐藏具体的实现细节 并且数据(成员变量和成员函数)在内部是完全自由使用的,而外部对数据的访问是加访问限定的,某些对操作只能根据由内部提供接口。
Inheritance 继承(代码重用)
? 继承可以说是一种代码复用的手段,我们在一个现有类上想扩展出一些东西的时候,不需要再次重复编写上面的代码,而是采用一种继承的思想。在派生出的子类里添加一些我们想要的数据或方法,也可以理解为从一般到特殊的过程。
Polymorphism 多态(接口重用)
这里先说运行时多态,其实是指在继承体系中父类的一个接口(必须为虚函数),在子类中有多种不同的实现,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。即通过父类指针或者引用可以访问到子类的接口(虚函数),看上去就像是一个相同的动作,会出现多种不同的结果。
类方法
class Num:
# 普通方法:能用Num调用而不能用实例化对象调用
def one():
print (‘1‘)
# 实例方法:能用实例化对象调用而不能用Num调用
def two(self):
print (‘2‘)
# 静态方法:能用Num和实例化对象调用
@staticmethod
def three():
print (‘3‘)
# 类方法:第一个参数cls长什么样不重要,都是指Num类本身,调用时将Num类作为对象隐式地传入方法
@classmethod
def go(cls):
cls.three()
Num.one() #1
#Num.two() #TypeError: two() missing 1 required positional argument: ‘self‘
Num.three() #3
Num.go() #3
i=Num()
#i.one() #TypeError: one() takes 0 positional arguments but 1 was given
i.two() #2
i.three() #3
i.go() #3
属性方法:
-
属性方法可以不加括号进行调用,但是不能传递参数。可以使用setter装饰器使用私有属性使用赋值方法间接传递参数。属性方法不支持直接删除,需要使用deleter装饰器来装饰删除方法,删除方法内需要删除setter装饰器装饰方法内用于传递删除的私有属性。
@property
def test2(self):
name1 = self.__name1
print(‘%s age is %d‘%(name1, self.age) )
@test2.setter
def test2(self, name1):
self.__name1 = name1
@test2.deleter #类属性的删除方法,默认类是不能删除的;先调用该方法,然后再删除属性方法
def test2(self):
del self.__name1
print(‘删除方法‘)
反射
-
反射的核心本质就是以字符串的形式去导入个模块,利用字符串的形式去执行函数。
Django中的 CBV就是基于反射实现的。
垃圾回收机制
1.引用计数
原理
-
当一个对象的引用被创建或者复制时,对象的引用计数加1;当一个对象的引用被销毁时,对象的引用计数减1
-
当对象的引用计数减少为0时,就意味着对象已经再没有被使用了,可以将其内存释放掉。
优点
-
引用计数有一个很大的优点,即实时性,任何内存,一旦没有指向它的引用,就会被立即回收,而其他的垃圾收集技术必须在某种特殊条件下才能进行无效内存的回收。
缺点
-
引用计数机制所带来的维护引用计数的额外操作与Python运行中所进行的内存分配和释放,引用赋值的次数是成正比的,
-
这显然比其它那些垃圾收集技术所带来的额外操作只是与待回收的内存数量有关的效率要低。
-
同时,因为对象之间相互引用,每个对象的引用都不会为0,所以这些对象所占用的内存始终都不会被释放掉。
为了解决以上问题:
2.标记-清除
-
标记-清除: 只关注那些可能会产生循环引用的对象,
-
把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放
缺点:标记和清除的过程效率不高
原理
-
基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点
-
以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放
3.分代回收
-
将系统中的所有内存块根据其存活时间划分为不同的集合,每一个集合就成为一个“代”,垃圾收集的频率随着“代”的存活时间的增大而减小。
-
也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。
-
那么如何来衡量这个存活时间:通常是利用几次垃圾收集动作来衡量,如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。
三种读文件方式
1.readline()
readline()每次读取一行,当前位置移到下一行readline()作用:readline 的用法,速度是fileinput的3倍左右,每秒3-4万行,好处是 一行行读 ,不占内存,适合处理比较大的文件,比如超过内存大小的文件
#readline读取大文件#
f1 = open(‘test02.py‘,‘r‘)
f2 = open(‘test.txt‘,‘w‘)
while True:
line = f1.readline()
if not line:
break
f2.write(line)
f1.close()
f2.close()
2.readlines()
readlines()读取整个文件所有行,保存在一个列表(list)变量中,每行作为一个元素readlines()作用:readlines会把文件都读入内存,速度大大增加,但是木有这么大内存,那就只能乖乖的用readline
#readlines读文件#
f1=open("readline.txt","r")
for line in f1.readlines():
print(line)
3.read(size)
read(size)从文件当前位置起读取size个字节,如果不加size会默认一次性读取整个文件(适用于读取小文件)read(n)读取指定长度的文件
f = open(r"somefile.txt")
print(f.read(7)) # Welcome 先读出 7 个字符
print(f.read(4)) #‘ to ‘ 接着上次读出 4 个字符
f.close()
seek(offset[, whence]) 随机访问 :从文件指定位置读取或写入
?
f = open(r"somefile.txt", "w")
f.write("01234567890123456789")
f.seek(5)
f.write("Hello, World!")
f.close()
f = open(r"somefile.txt")
print(f.read()) # 01234Hello, World!89
tell 返回当前读取到文件的位置下标
以上是关于python基础的主要内容,如果未能解决你的问题,请参考以下文章