Python基础

Python基础

    • Python2 与 3的区别
    • 进程 线程 协程
      • 进程
        • 进程通信
        • 进程说明
        • 进程池
      • 线程
        • 常用方法
          • join()方法
          • isAlive()方法
          • getName()方法
          • setDaemon()方法
        • 线程锁
        • 线程特性
        • 线程池
      • 进程和线程的区别
      • 协程
        • 协程优点
        • 协程缺点
        • 协程为何能处理并发
          • Greenlet
          • Gevent
        • 猴子补丁
        • 说明
    • GIL全局解释器锁
        • 有了GiL锁为何需要线程锁
        • GiL有什么影响
          • 排他锁
        • 避免GiL带来的影响
          • 方法一
          • 方法二
    • 迭代器
        • 迭代器的定义
        • 迭代器的2方法
        • 可迭代对象
        • 小结
    • 生成器
      • 工作原理
          • 生成器的定义
          • 生成器的作用
      • yield运行机制
      • 手写斐波那契
    • 装饰器
      • 装饰器作用
      • 手写三级装饰器
    • 闭包
        • 闭包特点
    • 四大高阶函数
        • MAP
        • Reduce
        • Filter
        • Sorted
        • Lambda(匿名函数)
    • 面向对象
      • 面向对象方法
        • 静态方法
        • 类方法
        • 属性方法
        • 特殊(魔术)方法
      • 单例模式
          • 优点
      • 面向对象属性
        • 公有属性
        • 普通属性
        • 私有属性
      • 封装 继承 多态
        • 封装
        • 继承
        • 多态
      • 新式类-经典类的区别
          • 写法上的区别
      • 反射
      • 三元运算
    • IO多路复用
        • select
        • Poll
        • Epoll
          • epoll为什么能实现高并发
    • 垃圾回收机制
        • 引用计数
          • 原理
          • 优 缺 点
        • 标记清楚
          • 原理
          • 说明
        • 分代回收
    • Tcp与Udp
        • 三次握手四次挥手
    • Websocket
    • 三种读操作
    • 数据类型
        • 列表
        • 元祖
          • *列表和元组常用函数*
        • 字符串
        • 字典
          • 将两个列表组合成字典
        • 集合
    • 数据结构
        • 栈的定义
        • 栈的特点
        • 栈的基本操作
        • 栈的应用场景
      • 队列
        • 队列的定义
        • 队列的使用方法
      • 链表
        • 单链表
          • 链表反转
        • 双链表
        • 单向循环链表
        • 数组
          • python中list与数组比较
        • 字典实现原理
          • 哈希表
    • Python常用模块
        • subprocess
            • **subprocess原理**
            • **subprocess.Popen()**
            • **subprocess.PIPE**
        • paramiko
            • **Paramiko模块作用**
        • re
            • 常用正则表达式符号
            • 匹配时忽略大小写
        • sys
            • **sys基本方法**
        • os
        • time
            • **time()模块中的重要函数**
            • **time()模块时间转换**
        • datetime

Python2 与 3的区别

print语句被python3废弃,改为print函数

Python3中字符串是Unicode (utf-8)编码、python2中是ASCII编码

异常处理 Python2中try:...except Exception, e:...,在Python3中改为了try:...except Exception as e:...

Python3中不再使用xrange方法,只有range方法。

range在Python2中返回列表,而在Python3中返回range可迭代对象。

python 2 中通过input输入的类型是int,只有通过raw_input()输入的类型才是str。

进程 线程 协程

进程

进程,是执行中的计算机程序。也就是说,每个代码在执行的时候,首先本身即是一个进程。

进程是是资源分配的最小单位 运行在内存中

每个进程最少有一个线程,进程不干活让线程干

进程可以通过派生新的进程来执行其它任务,不过每个进程还是都拥有自己的内存和数据栈等。

一个进程具有:就绪,运行,中断,僵死,结束等状态(不同操作系统不一样)。

进程支持并发和并行

进程通信

进程之间数据不共享  所以需要进程通信

通信的三种方式

​	1 进程队列queue

​	2 管道pipe

​	3 共享数据manage

进程说明

多个进程可以在不同的 CPU 上运行,互不干扰

同一个CPU上,可以运行多个进程,由操作系统来自动分配时间片

进程池

开多进程是为了并发,通常有几个cpu核心就开几个进程,但是进程开多了会影响效率,主要体现在切换的开销,所以引入进程池限制进程的数量。

进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。

线程

线程,是在进程中执行的代码。线程之间 数据共享

一个进程下可以运行多个线程,这些线程之间共享主进程内申请的操作系统资源。

线程是系统调度的最小单位  

常用方法

join()方法
​	实现所有线程都执行结束后再执行主线程

​	如果一个线程或者在函数执行的过程中调用另一个线程,并且希望待其完成操作后才能执行,   那么在	调用线程的时就可以使用被调线程的join方法join([timeout]) timeout:可选参数,线程运行的最长时间
isAlive()方法

​查看线程是否还在运行

getName()方法

​ 获得线程名

setDaemon()方法

​ 主线程退出时,需要子线程随主线程退出,则设置子线程的setDaemon() 也叫守护线程

线程锁

线程锁也叫用户锁 也叫互斥锁、当前线程还未操作完成前其他所有线程都无法对其操作,即使已经释放了GIL锁

线程特性

线程,必须在一个存在的进程中启动运行

线程使用进程获得的系统资源,不会像进程那样需要申请CPU等资源

线程无法给予公平执行时间,它可以被其他线程抢占,而进程按照操作系统的设定分配执行时间

线程池

系统启动一个新线程的成本是比较高的,因为它涉及与操作系统的交互。在这种情形下,使用线程池可以很好地提升性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池。

线程池在系统启动时即创建大量空闲的线程,程序只要将一个函数提交给线程池,线程池就会启动一个空闲的线程来执行它。当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,等待执行下一个函数。

进程和线程的区别

一个进程中的各个线程与主进程共享相同的资源,与进程间互相独立相比,线程之间信息共享和通信更加容易

线程一般以并发执行,正是由于这种并发和数据共享机制,使多任务间的协作成为可能。

进程一般以并行执行,这种并行能使得程序能同时在多个CPU上运行;

协程

协程能在单线程的条件下支持并发,能遇IO自动切换、将网络数据或磁盘数据写入到内存就是一个IO操作

协程的作用,是在执行函数A时,可以随时中断,去执行函数B,然后中断继续执行函数A(可以自由切换)。但这一过程并不是函数调用,且看似像多线程 实际就是单线程

协程由于由程序主动控制切换,没有线程切换的开销,所以执行效率极高。对于IO密集型任务非常适用,如果是cpu密集型,推荐多进程+协程的方式。

协程优点

1   无需线程上下文切换的开销   

2   不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突

3   单线程内就可以实现并发的效果,最大限度地利用cpu

协程缺点

无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上

线程阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

协程为何能处理并发

Greenlet
遇IO操作  手动切换 、是c语言开发的
Gevent
Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程

其实Gevent模块仅仅是对greenlet的再封装,将I/O间的手动切换变成自动切换

Gevent原理是只要遇到I/O操作就会自动切换到下一个协程

猴子补丁

用过gevent就会知道,会在最开头的地方gevent.monkey.patch_all();

作用是把标准库中的thread/socket等给替换掉.无需修改任何代码,把它变成了非阻塞

说明

协程间是协同调度的,这使得并发量数万以上的时候,协程的性能是远远高于线程。注意这里也是“并发”,不是“并行”。 协程还有gevent和greenlet 2个第三方库、greenlet是c语言开发的、是遇IO手动切换、 gevent内封装了greenlet  遇IO自动切换

greenlet框架封装了yield语句、挂起函数,直到稍后使用next()或send()操作进行恢复为止

GIL全局解释器锁

GIL 保证同一时间内只有一个线程在执行、GIL并不是Python的特性   

GIL只存在于使用C语言编写的解释器CPython中

有了GiL锁为何需要线程锁

因为CPU给当前线程分配了时间、时间结束后线程任务没完成所以需要线程锁

GiL有什么影响

GIL无疑就是一把全局排他锁。毫无疑问全局锁的存在会对多线程的效率有不小影响。甚至就几乎等于Python是个单线程的程序。
排他锁
若事务T对数据对象A加上X锁,则只允许T读取和修改A
其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。

避免GiL带来的影响

方法一
用进程+协程 代替 多线程的方式 在多进程中,由于每个进程都是独立的存在,所以每个进程内的线程都拥有独立的GIL锁,互不影响。但是,由于进程之间是独立的存在,所以进程间通信就需要通过队列的方式来实现。
方法二
更换解释器

迭代器

迭代器的定义

迭代器是访问集合内元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束。

凡是可作用于`next()`函数的对象都是迭代器(`Iterator)`类型,它们表示一个惰性计算的序列;

迭代器的2方法

next可以返回容器的下一个元素

a = iter([1,2,])              #生成一个迭代器
print(a.__next__())
print(a.__next__())
print(a.__next__())           #在这一步会引发  “StopIteration” 的异常

__ iter __返回迭代器本身

可迭代对象

​ 凡是可作用于for循环的对象都是可迭代的(Iterable)类型;

小结

一个实现了__iter__方法的对象是可迭代的,一个实现next方法的对象是迭代器

生成器

工作原理

生成器是这样一个函数,它记住上一次返回时在函数体中的位置。

对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。
生成器的定义
生成器,即生成一个容器。
在Python中,**一边循环,一边计算的机制,称为生成器。**
生成器可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他数据类型需要调用自己的内置iter()方法或__iter__()的内置函数),
所以,生成器就是一个可迭代对象。
生成器的作用
1. 通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的。

  2. 如: 创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

  3. 所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?

  4. 这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

  5. 创建一个生成器
	print( [i*2 for i in range(10)] )             #列表生成式: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
	print( (i*2 for i in range(10)) )             # at0x005A3690>
	
	#获取生成器内元素
	g = (i*2 for i in range(10))
	print( g.__next__() )               # 0
	print( g.__next__() )               # 2

yield运行机制

在Python中,yield就是一个生成器。  如果在函数内使用了yield关键字时,这个函数就是一个生成器

当你问生成器要一个数时,生成器会执行,直至出现 yield 语句,生成器把yield 的参数给你,之后生成器就不会往下继续运行。

当你问他要下一个数时,他会从上次的状态开始运行,直至出现yield语句,把参数给你,之后停下。如此反复

每当调用一次迭代器的next函数,生成器函数运行到yield之处,返回yield后面的值且在这个地方暂停,所有的状态都会被保持住,直到下次next函数被调用,或者碰到异常循环退出。

手写斐波那契

def fbnq(n):
    a,b=0,1
    while n>a:
        yield a
        a,b=b,a+b
if __name__ == '__main__':
    f=fbnq(9)
    print(next(f))    #打印结果 0
    print(next(f))    #打印结果 1

装饰器

装饰器作用

装饰器是在不修改源代码的情况下为其添加新的功能、 装饰器是基于闭包实现的 

装饰器原则:

​	不能修改被装饰函数的源代码
​	不能修改被装饰函数的调用方式

手写三级装饰器

import time
user,passwd = 'aaa','123'
def auth(auth_type):
    print("auth func:",auth_type)
    def outer_wrapper(func):
        def wrapper(*args, **kwargs):
            print("wrapper func args:", *args, **kwargs)
            if auth_type == "local":
                username = input("Username:").strip()
                password = input("Password:").strip()
                if user == username and passwd == password:
                    print("\033[32;1mUser has passed authentication\033[0m")
                    res = func(*args, **kwargs)  # from home
                    print("---after authenticaion ")
                    return res
                else:
                    exit("\033[31;1mInvalid username or password\033[0m")
            elif auth_type == "ldap":
                print("搞毛线ldap,不会。。。。")

        return wrapper
    return outer_wrapper

def index():
    print("welcome to index page")
@auth(auth_type="local") # home = wrapper()
def home():
    print("welcome to home  page")
    return "from home"

@auth(auth_type="ldap")
def bbs():
    print("welcome to bbs  page")

index()
print(home()) #wrapper()
bbs()

闭包

在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用,这样就构成了一个闭包

但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

闭包特点

1. 必须有一个内嵌函数

2. 内嵌函数必须引用外部函数中的变量

3. 外部函数的返回值必须是内嵌函数

四大高阶函数

MAP

map(func, iter)函数:Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回 

第一个参数接收一个函数名,第二个参数接收一个可迭代对象

功能:将传入的函数依次作用于序列中的每一个元素,并把结果作为一个新的迭代器返回.
注意:当传入map的序列只有一个的时候,传入的函数,有且只有一个参数.当map的序列有多个的情况下,得到的迭代长度与序列中最短的那个列表长度相同
def a(b):
    return b*b   #这里可以使用 算术运算符 任意一种
A=map(a,[1,2,3,4,5]) #map里2个参数 一个是函数名 一个是可迭代对象
print(list(A)) #把变量A 强转为列表  输出结果[1, 4, 9, 16, 25]
print(type(A)) #输出结果 

Reduce

reduce(func, iter)函数 :会对参数序列中元素进行累积
参数一:要传入的函数名 
参数二:序列 
功能:一个函数作用序列上,而且此函数必须接受两个参数, reduce把这次作用的结果,继续与序列中的下一个元素进行累计运算. 
#reduc 函数  使用reduce必须先导入模块
from functools import reduce
def e (f,g):
    return f+g
E=reduce(e,(1,2,3,4,5))
print(E) #这里不能用list强转   输出结果 15

Filter

filter(func, iter) 函数 :用于过滤序列 
参数一:函数名 
参数二:序列 
功能:将序列中的每一个元素作用于func,根据func返回True或者False决定是否保留该元素. 当func返回True,保留该元素,返回False的时候去除该元素. 
#一行代码过滤 奇偶数   面试高频题   
a = filter(lambda x:x%2 == 1,range(1,101))
print(list(a))    

Sorted

sorted(iterable,key,reverse)函数 :排序参数一:必须可迭代对象,[用于比较的序列]
参数二:用于比较函数的时候,比较什么由key来决定[key有默认值]
参数三:reverse = True[降序], reverse = False[升序,默认升序]返回值,返回的一个可迭代对象.
my_list=[2,5,1,-56,9,100,34,0,-101]
print(sorted(my_list))  #默认从小到大排序([-101, -56, 0, 1, 2, 5, 9, 34, 100])
print(sorted(my_list,reverse=True))  #从大到小排序([100, 34, 9, 5, 2, 1, 0, -56, -101])
print(sorted(my_list,reverse=False))  #从小到大排序([-101, -56, 0, 1, 2, 5, 9, 34, 100])
print(sorted(my_list,key=abs)) #使用key=系统函数abs(绝对值)从小到大排序([0, 1, 2, 5, 9, 34, -56, 100, -101])

Lambda(匿名函数)

lambda只是一个表达式,函数体比def简单很多。
lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
lambda表达式是起到一个函数速写的作用。允许在代码内嵌入一个函数的定义。
格式:lambda的一般形式是关键字lambda后面跟一个或多个参数,紧跟一个冒号,之后是一个表达式。
m=lambda i:i
lista=sorted([1,-2,3,-4,5,-6],key=abs)
print(lista)   #结果: [1, -2, 3, -4, 5, -6]


# 利用 filter、lambda表达式 获取列表中元素小于33的所有元素
l1= [11,22,33,44,55]
a = filter(lambda x: x<33, l1)
print(list(a))

面向对象

面向对象方法

静态方法

作用:静态方法可以更好的组织代码,防止代码变大后变得比较混乱。

特性: 静态方法只是名义上归类管理,实际上在静态方法里访问不了类或则实例中的任何属性

静态方法使用场景:
    经常有一些跟类有关系的功能但在运行时又不需要实例和类参与的情况下 需要用到静态方法.
    比如更改环境变量或者修改其他类的属性等能用到静态方法.
    这种情况可以直接用函数解决, 但这样同样会扩散类内部的代码,造成维护困难.

调用方式: 既可以被类直接调用,也可以通过实例调用

class Dog(object):
    def __init__(self,name):
        self.name = name
    @staticmethod
    def eat():
        print("I am a static method")
d = Dog("ChenRonghua")
d.eat()                       #方法1:使用实例调用
Dog.eat()                   #方法2:使用类直接调用

类方法

作用:无需实例化直接被类调用

特性: 类方法只能访问类变量,不能访问实例变量

类方法使用场景: 当我们还未创建实例,但是需要调用类中的方法

调用方式: 既可以被类直接调用,也可以通过实例调用

class Dog(object):
    name = '类变量' #在这里如果不定义类变量仅定义实例变量依然报错
    def __init__(self,name):
        self.name = '实例变量'
        self.name = name
    @classmethod
    def eat(self,food):
        print("%s is eating %s"%(self.name,food))
Dog.eat('baozi')                   #方法1:使用类直接调用
d = Dog("ChenRonghua")          
d.eat("包子")                      #方法2:使用实例d调用

属性方法

**作用:**属性方法把一个方法变成一个属性,隐藏了实现细节,调用时不必加括号直接d.eat即可调用self.eat()方法

class Dog(object):
    def __init__(self, name):
        self.name = name

    @property
    def eat(self):
        print(" %s is eating" % self.name)
d = Dog("ChenRonghua")
d.eat()
# 调用会出以下错误, 说NoneType is not callable, 因为eat此时已经变成一个静态属性了, 
# 不是方法了, 想调用已经不需要加()号了,直接d.eat就可以了

特殊(魔术)方法

__ doc __  表示类的描述信息

__ call __ 对象后面加括号,触发执行

__ str __ 如果一个类中定义了__ str __方法,在打印对象时,默认输出该方法的返回值

__ dict __ 查看类或对象中的所有成员

__new__和__init__的区别

1、 new__是一个静态方法,而__init__是一个实例方法.
    2、 new__方法会返回一个创建的实例,而__init__什么都不返回.
    3、 只有在__new__返回一个cls的实例时后面的__init__才能被调用.
    4、 当创建一个新实例时调用__new
,初始化一个实例时用__init
.

单例模式

  1. 单例模式:永远用一个对象得实例,避免新建太多实例浪费资源

    保证一个类只有一个实例,并提供一个访问他的全局访问点

  2. 实质:使用__new__方法新建类对象时先判断是否已经建立过,如果建过就使用已有的对象

  3. 使用场景:如果每个对象内部封装的值都相同就可以用单例模式

class Foo(object):
   instance = None
   def __init__(self):
      self.name = 'alex'

   def __new__(cls, *args, **kwargs):
      if Foo.instance:
         return Foo.instance
      else:
         Foo.instance = object.__new__(cls,*args,**kwargs)
         return Foo.instance

obj1 = Foo()       # obj1和obj2获取的就是__new__方法返回的内容
obj2 = Foo()
print(obj1,obj2)   
# 运行结果: <__main__.Foo object at 0x00D3B450>    <__main__.Foo object at 0x00D3B450>

# 运行结果说明:
# 这可以看到我们新建的两个Foo()对象内存地址相同,说明使用的•同一个类,没有重复建立类
优点

1、 对唯一实例的受控访问(比如写日志时的日志句柄)
2、 单例模式相当于全局变量,单例模式防止了命名空间被污染

面向对象属性

公有属性

公有属性:在内存中仅存一份

普通属性

普通属性:每个实例对象在内存存一份

私有属性

私有属性:实例在外部无法调用

封装 继承 多态

封装

封装(隐藏实现细节)

1.在类中对数据的赋值、内部调用对外部用户是透明的

  1. 这使类变成了一个胶囊或容器,里面包含着类的数据和方法

作用

1)防止数据被随意修改

2)使外部程序不需要关注对象内部的构造,只需要通过对外提供的接口进行直接访问

继承

继承(代码重用)

  1. 一个类可以派生出子类,在这个父类里定义的属性、方法自动被子 类继承

  2. 比如CS中的警察和恐怖分子,可以将两个角色的相同点写到一个父类中,然后同时去继承它

  3. 使用经典类: Person.init(self,name,age) 并重写写父类Person的构造方法,实现,先覆盖,再继承,再重构

多态

多态(接口重用)

1.多态是面向对象的重要特性,简单点说:“一个接口,多种实现”

  1. 指一个基类中派生出了不同的子类,且每个子类在继承同样的方法名的同时又对父类的方法做了不同的实现

  2. 这就是同一种事物表现出的多种形态

  3. 比如黄种人继承了人talk这个功能,但是他说的是中文,而美国人 的talk是英文,但是他们是同样的talk

作用:简单的讲就是允许父类调用子类的方法

新式类-经典类的区别

写法上的区别

​ ❶经典类 没有继承object , 新式类 继承了object

​ ❷多继承中,新式类采用广度优先搜索,而旧式类是采用深度优先搜索,python3中全是广度查询

​ ❸在继承中新式类和经典类写法区别

	SchoolMember.__init__(self,name,age,sex)                      #经典类写法
	super(Teacher,self).__init__(name,age,sex)                    #新式类写法

​ https://images2017.cnblogs.com/blog/1080958/201711/1080958-20171123111919977-1164656767.png

反射

通过字符串找到对应请求( get post delete 等…)

三元运算

  1. 三元运算格式: result=值1 if x

  2. **作用:**三元运算,又称三目运算,主要作用是减少代码量,是对简单的条件语句的缩写

#一个简单的三元运算
a=name='小草' if1=1 else '小花'
print(a)  # 小草

#三元运算 + lambda
f = lambda x:x if x % 2 != 0 else x + 100
print(f(10))                    # 110

IO多路复用

无论是sellect、poll、epoll他们三个都是在I/O多路复用中检测多个socket链接,与数据从内核态到数据态没有什么关系

Windows下只支持select 如果是Linux最好使用epoll

https://www.cnblogs.com/xiaonq/p/7907871.html#i3

select

select 能监控数量有限,不能告诉用户程序具体哪个连接有数据

1. select目前几乎在所有的平台上支持,其良好**跨平台**支持也是它的一个优点,

   事实上从现在看来,这也是它所剩不多的优点之一

2. select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,

   在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制

3. select监控socket连接时不能准确告诉用户是哪个,比如:现在用socket监控10000链接,如果其中有一个

 链接有数据了,select就会告诉用户程序,你有socket来数据了,那样就只能自己循环10000次判断哪个活跃

Poll

poll和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制

Epoll

epoll被认为是linux下性能最好的多路io就绪通知方法

epoll直到Linux2.6(centos6以后)才出现了由内核直接支持

epoll最重要的优点是他可以直接监听哪些是活跃数据,并将其放在链表中返回
epoll为什么能实现高并发
1. epoll() 中内核则维护一个链表,epoll_wait 直接检查链表是不是空就知道是否有文件描述符准备好了。

2. 在内核实现中 epoll 是根据每个 sockfd 上面的与设备驱动程序建立起来的回调函数实现的。

3. 某个 sockfd 上的事件发生时,与它对应的回调函数就会被调用,来把这个 sockfd 加入链表,其他处于“空      闲的”状态的则不会。*

4. epoll上面链表中获取文件描述,这里使用内存映射(mmap)技术, 避免了复制大量文件描述符带来的开销

5. 内存映射(mmap):内存映射文件,是由一个文件到一块内存的映射,将不必再对文件执行I/O操作

垃圾回收机制

引用计数

原理
1. 当一个对象的引用被创建或者复制时,对象的引用计数加1;当一个对象的引用被销毁时,对象的引用计数减1
2. 当对象的引用计数减少为0时,就意味着对象已经再没有被使用了,可以将其内存释放掉。
优 缺 点
优点

引用计数有一个很大的优点,即实时性,任何内存,一旦没有指向它的引用,就会被立即回收,而其他的垃圾收集技术必须在某种特殊条件下才能进行无效内存的回收。

缺点

1. 引用计数机制所带来的维护引用计数的额外操作与Python运行中所进行的内存分配和释放,引用赋值的次数是成正比的,

2. 这显然比其它那些垃圾收集技术所带来的额外操作只是与待回收的内存数量有关的效率要低。
3.  同时,因为对象之间相互引用,每个对象的引用都不会为0,所以这些对象所占用的内存始终都不会被释放掉。

标记清楚

原理
1. 基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点

2. 以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放
说明
1. 标记-清除: 只关注那些可能会产生循环引用的对象
2. Python中的循环引用总是发生在container(容器) 对象之间,也就是能够在内部持有其它对象的对象,比如list、dict、class等等。
3. 这也使得该方法带来的开销只依赖于container对象的的数量.

标记和清除的过程效率不高

分代回收

1. 将系统中的所有内存块根据其存活时间划分为不同的集合,每一个集合就成为一个“代”,垃圾收集的频率随着“代”的存活时间的增大而减小。
2. 活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。
3. 如何来衡量这个存活时间:通常是利用几次垃圾收集动作来衡量, 如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。

Tcp与Udp

TCP与UDP比较
            1. TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
            2. CP提供可靠的服务,也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;
                  UDP尽最大努力交付,即不保证可靠交付
            3. Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。
            4. UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
            5. 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
                  TCP对系统资源要求较多,UDP对系统资源要求较少。

注:UDP一般用于即时通信(QQ聊天 对数据准确性和丢包要求比较低,但速度必须快),在线视频等

三次握手四次挥手

TCP三次握手
第一次握手:建立连接时,客户端发送SYN包到服务端,并进入SYN_SENT状态,等待服务器确认。
SYN:同步序列编号
第二次握手:服务器收到SYN包,必须确认客户的SYN包,同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN_RECV状态 
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入,ESTABLISHED(TCP连接成功)状态,完成三次握手。

UTP四次挥手
第一次挥手:首先客户端向服务端发送断开请求
(客户端发送一个FIN,用来关闭客户端到服务器的数据传送,然后等待服务器的确认。其中终止标志位FIN=1,序列号seq=u)
第二次挥手:服务端向客户端进行回复
(服务器收到这个FIN,它发送一个ACK,确认ack为收到的序号加一)
第三次挥手:服务端向客户端断开请求
(关闭服务器到客户端的连接,发送一个FIN给客户端)
第四次挥手:客户端收到消息后进行回复
(客户端收到FIN后,并发回一个ACK报文确认,并将确认序号seq设置为收到序号加一。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭)

Websocket

1. 是一种在单个TCP连接上进行 全双工通信(又称为双向同时通信,即通信的双方可以同时发送和接收信息的信息交互方式。)的协议。
2.   WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

三种读操作

三种读操作比较
readline() 每次读一行,读完一行移至下一行(迭代着读){注: 适用于读大型文件}
readline速度是fileinput的3倍左右,每秒3-4万行,**好处是 一行行读 ,不占内存**,适合处理比较大的文件,比如超过内存大小的文件

readlines() 读取整个文件所有行,保存在一个列表变量中,每行作为一个元素
readlines会把文件都读入内存,**速度大大增加**,但是木有这么大内存,那就只能乖乖的用readline

read(size)从文件当前位置起读取size个字节,如果不加size会默认一次性读取整个文件(适用于读取小文件)

数据类型

列表

append 用于在列表末尾追加新的对象

count 方法统计某个元素在列表中出现的次数

a = ['aa','bb','cc','aa','aa']
print(a.count('aa'))   #the result : 3

extend方法可以在列表的末尾一次性追加另一个序列中的多个值

a = [1,2,3]
b = [4,5,6]
a.extend(b)   #the result :[1, 2, 3, 4, 5, 6]
index  函数用于从列表中找出某个值第一个匹配项的索引位置

 insert  方法用于将对象插入到列表中   结合下标使用

pop   方法会移除列表中的一个元素(默认是最后一个),并且返回该元素的值

remove  方法用于移除列表中某个值的第一个匹配项
a = ['aa','bb','cc','aa']
a.remove('aa')     #the result : ['bb', 'cc', 'aa']
reverse  方法将列表中的元素反向存放

sort  方法用于对列表进行排序

enumrate  和for循环差不多  只是能获取下标的同时还能获取item
li = [11,22,33]
for k,v in enumerate(li, 1):
    print(k,v)
#打印结果如下:
1 11
2 22
3 33

元祖

元祖和列表一样、只不过元祖是只读列表

列表和元组常用函数
  com(x,y)        比较两个值

  len(seq)        返回序列的长度

  list(seq)       把序列转换成列表

  max(args)       返回序列或者参数集合中得最大值

  min(args)       返回序列或者参数集合中的最小值

  reversed(seq)   对序列进行反向迭代

  sorted(seq)     返回已经排列的包含seq 所有元素的列表

  tuple(seq)      把序列转换成元组

字符串

使用百分号(%)字符串格式化

使用format字符串格式化

find方法可以在一个较长的字符串中查找子串,他返回子串所在位置的最左端索引,如果没有找到则返回-1
a = 'abcdefghijk'
print(a.find('abc'))                 #the result : 0
print(a.find('abc',10,100))          #the result : 11  指定查找的起始和结束查找位置

join方法 是非常重要的字符串方法,连接序列中的元素,并且需要被连接的元素都必须是字符串。

a = ['1','2','3']
print('+'.join(a))        # 1+2+3

split方法 是非常重要的字符串,用来将字符串分割成序列

print('1+2+3+4'.split('+'))       # ['1', '2', '3', '4']

strip 方法返回去除首位空格(不包括内部)的字符串

print("  test   test   ".strip())       #“test   test”

replace方法 返回某字符串所有匹配项均被替换之后得到字符串

print("This is a test".replace('is','is_test'))        # This_test is_test a test

字典

clear方法清除字典中所有的项,这是一个原地操作,所以无返回值(或则说返回None)

d = {
     }
d['Tom']=8777
d['Jack']=9999
print(d)                                # {'Jack': 9999, 'Tom': 8777}
d.clear()
print(d)                                # {}

copy方法返回一个具有相同 ”键-值” 对的新字典,而不是副本

d = {
     'Tom':8777,'Fly':6666}
a = d.copy()
a['Tom'] = '改变后的值'
print(d)                        #{'Fly': 6666, 'Tom': 8777}
print(a)                        #{'Fly': 6666, 'Tom': '改变后的值'}

# 查看内存地址
d={
     'tom':122,'fly':221}
id(d)
1932459885696

a=d.copy()
a
{
     'tom': 122, 'fly': 221}
id(a)
1932488640048

fromkeys方法使用给定的键建立新的字典,每个键都对应一个默认的值None。

get方法是一个访问字典项的方法

for循环字典的三种方法

d = {
     'Tom':8777,'Jack':8888,'Fly':6666}
for k,v in d.items():
    print(k,v)
for k in d.values():
    print(k)
for k in d.keys():
    print(k)

pop方法 用于获得对应与给定键的值,然后将这个”键-值”对从字典中移除

update方法可以利用一个字典项更新另一个字典,提供的字典中的项会被添加到旧的字典中

>>> a
{
     'tom': 122, 'fly': 221}
>>> b={
     'age':20}
>>> a.update(b)
>>> a
{
     'tom': 122, 'fly': 221, 'age': 20}
将两个列表组合成字典
keys = ['a', 'b']
values = [1, 2]

#1、zip生成字典
print(dict(zip(keys,values)))                        # {'a': 1, 'b': 2}
#2、for循环推倒字典
print({
     keys[i]: values[i] for i in range(len(keys))})       # {'a': 1, 'b': 2}

集合

list_1 = [1,2,3,4,5,1,2]
#1、去重(去除list_1中重复元素1,2)
list_1 = set(list_1)                                    #去重: {1, 2, 3, 4, 5}
print(list_1)
list_2 = set([4,5,6,7,8])

#2、交集(在list_1和list_2中都有的元素4,5)
print(list_1.intersection(list_2))                      #交集: {4, 5}

#3、并集(在list_1和list_2中的元素全部打印出来,重复元素仅打印一次)
print(list_1.union(list_2))                             #并集: {1, 2, 3, 4, 5, 6, 7, 8}

#4、差集
print(list_1.difference(list_2))                        #差集:在list_1中有在list_2中没有:   {1, 2, 3}
print(list_2.difference(list_1))                        #差集:在list_1中有在list_2中没有:   {8, 6, 7}

#5、子集
print(list_1.issubset(list_2))                          #子集:    False    List_1中的元素是否全部在list2中
#6、父集
print(list_1.issuperset(list_2))                        #父集:    False    List_1中是否包含list_2中的所有元素

#7、交集
print(list_1 & list_2)                                  #交集    {4, 5}

#8、union并集
print(list_1 | list_2)                                  #并集:  {1, 2, 3, 4, 5, 6, 7, 8}

#9、difference差集
print(list_1 - list_2)                                  #差集:    {1, 2, 3}

#10、在集合中添加一个元素999
list_1.add(999)
print(list_1)                                           #Add()方法:          {1, 2, 3, 4, 5, 999}

#11、删除集合中任意一个元素不会打印删除的值
list_1.pop()                                            #Pop()方法:     无返回值

#12、discard删除集合中的指定元素,如过没有则返回None
print(list_1.discard("ddd"))                            #Discard()方法:   删除指定的值,没有返回None

数据结构

栈的定义

栈是一种数据集合,可以理解为只能在一端进行插入或删除操作的列表

栈的特点

后进先出 栈有栈顶和栈底

栈的基本操作

进栈(压栈):push

出栈:pop

取栈顶:gettop

栈的应用场景

匹配括号是否成对出现

def check_kuohao(s):
   stack = []
   for char in s:
      if char in ['(','[','{']:
         stack.append(char)
      elif char == ')':
         if len(stack)>0 and stack[-1] == '(':
            stack.pop()
         else:
            return False
      elif char == ']':
         if len(stack) > 0 and stack[-1] == '[':
            stack.pop()
         else:
            return False
      elif char == '}':
         if len(stack) > 0 and stack[-1] == '{':
            stack.pop()
         else:
            return False
   if len(stack) == 0:
      return True
   else:
      return False
print(check_kuohao('(){}{}[]'))  #True

队列

队列的定义

  1. 队列是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除
  2. 插入的一端称为队尾(rear),插入动作叫进队或入队
  3. 进行删除的一端称为对头(front),删除动作称为出队
  4. 队列性质:先进先出(First-in, First-out)
  5. 双向队列:队列的两端都允许进行进队和出队操作

队列的使用方法

  1. 导入: from collectios import deque
  2. 创建队列:queue = deque(li)
  3. 进队: append
  4. 出队: popleft
  5. 双向队列队首进队:appendleft
  6. 双向队列队尾出队:pop

链表

单链表

链表中每个元素都是一个对象,每个对象称为一个节点,包含有数据域key和指向下一节点的指针next,通过各个节点间的相互连接,最终串联成一个链表

img

单链表第一个节点没有前驱,最后一个节点没有后继。

链表反转
class Node(object):
    def __init__(self, val):
        self.val = val
        self.next = None

def list_reverse(head):
    if head == None:
        return None
    L, R, cur = None, None, head  # 左指针、有指针、游标
    while cur.next != None:
        L = R             # 左侧指针指向以前右侧指针位置
        R = cur           # 右侧指针前进一位指向当前游标位置
        cur = cur.next    # 游标每次向前进一位
        R.next = L        # 右侧指针指向左侧实现反转
    cur.next = R          # 当跳出 while 循环时 cur(原链表最后一个元素) R(原链表倒数第二个元素)
    return cur

if __name__ == '__main__':
    '''
    原始链表:1 -> 2 -> 3 -> 4
    反转链表:4 -> 3 -> 2 -> 1
    '''
    l1 = Node(1)
    l1.next = Node(2)
    l1.next.next = Node(3)
    l1.next.next.next = Node(4)
    l = list_reverse(l1)
    print l.val         # 4  反转后链表第一个值4
    print l.next.val    # 3  第二个值3

双链表

双链表中每个节点有两个指针:一个指针指向后面节点、一个指向前面节点

Python基础_第1张图片

单向循环链表

本质和单向联链表一样,但循环链表的最后一个结点的指针是指向该循环链表的第一个结点或者表头结点,从而构成一个环形的链。

数组

数组的定义

  1. 所谓数组,就是相同数据类型的元素按一定顺序排列的集合

  2. 在Java等其他语言中并不是所有的数据都能存储到数组中,只有相同类型的数据才可以一起存储到数组中。

  3. 因为数组在存储数据时是按顺序存储的,存储数据的内存也是连续的,所以他的特点就是寻址读取数据比较容易,插入和删除比较困难。

python中list与数组比较

1. python中的list是python的内置数据类型,list中的数据类不必相同的,而array的中的类型必须全部相同。

2. 在list中的数据类型保存的是数据的存放的地址,简单的说就是指针,并非数据

3. 这样保存一个list就太麻烦了,例如list1=[1,2,3,‘a’]需要4个指针和四个数据,增加了存储和消耗cpu。

字典实现原理

哈希表
  1. 哈希表(也叫散列表),根据关键值对(Key-value)而直接进行访问的数据结构。

它通过把key和value映射到表中一个位置来访问记录,这种查询速度非常快,更新也快。

  1. 而这个映射函数叫做哈希函数,存放值的数组叫做哈希表

Python常用模块

https://www.cnblogs.com/xiaonq/p/7866925.html

subprocess

subprocess原理
  1. 运行python的时候,我们都是在创建并运行一个进程。像Linux进程那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序

  2. 在Python中,我们通过标准库中的subprocess包来fork一个子进程,并运行一个外部的程序。

  3. subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用

  4. 另外subprocess还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。

  5. 可以在Windows中运行linux命令

    #1、返回执行状态:0 执行成功
    retcode = subprocess.call(['ping', 'www.baidu.com', '-c5'])
    
    #2、返回执行状态:0 执行成功,否则抛异常
    subprocess.check_call(["ls", "-l"])
    
    #3、执行结果为元组:第1个元素是执行状态,第2个是命令结果
    >>> ret = subprocess.getstatusoutput('pwd')
    >>> ret
    (0, '/test01')
    
subprocess.Popen()

​ Popen对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block)

subprocess.PIPE

将多个子进程的输入和输出连接在一起

subprocess.PIPE实际上为文本流提供一个缓存区。child1的stdout将文本输出到缓存区,随后child2的stdin从该PIPE中将文本读取走

paramiko

Paramiko模块作用
  1. 如果需要使用SSH从一个平台连接到另外一个平台,进行一系列的操作时,

​ 比如:批量执行命令,批量上传文件等操作,paramiko是最佳工具之一。

  1. paramiko是用python语言写的一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接
  2. 支持多平台 如 Linux, Solaris, BSD,MacOS X, Windows等

re

常用正则表达式符号

通配符 ●

**作用:**● 可以匹配除换行符以外的任意一个字符串

转义字符 ╲

**作用:**可以将其他有特殊意义的字符串以原本意思表示

**注:**如果想对反斜线(\)自身转义可以使用双反斜线(\\)这样就表示’\’

字符集

**作用:**使用中括号来括住字符串来创建字符集,字符集可匹配他包括的任意字串

‘[pj]ython’ 只能够匹配‘python’ ‘jython’

[a-z] 能够(按字母顺序)匹配a-z任意一个字符

[a-zA-Z0-9] 能匹配任意一个大小写字母和数字

[ ^abc ] 可以匹配任意除a,b和c 之外的字符串

管道符

**作用:**一次性匹配多个字符串

例如:’python | perl’ 可以匹配字符串‘python’ 和 ‘perl’

最常用的匹配方法

\d     匹配任何十进制数;它相当于类 [0-9]。
\D     匹配任何非数字字符;它相当于类 [^0-9]。
\s     匹配任何空白字符。
\S     匹配任何非空白字符 [^\s]。
\w     匹配任何字母数字字符;它相当于类 [a-zA-Z0-9_]。
\W     匹配任何非字母数字字符;它相当于类 [^a-zA-Z0-9_]。
\w*    匹配所有字母字符
\w+    至少匹配一个字符
匹配时忽略大小写
import re
#匹配时忽略大小写
print(re.search("[a-z]+","abcdA").group())                #abcd
print(re.search("[a-z]+","abcdA",flags=re.I).group())     #abcdA

sys

sys基本方法
sys.argv          返回执行脚本传入的参数
sys.exit(n)                       退出程序,正常退出时exit(0)
sys.version                           获取Python解释程序的版本信息
sys.maxint                            最大的Int值
sys.path                                返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform                           返回操作系统平台名称

os

os模块就是对操作系统进行操作

import os
# 当前工作目录,即当前python脚本工作的目录路径
print(os.getcwd())    # C:\Users\admin\PycharmProjects\s14\Day5\test4

# 当前脚本工作目录;相当于shell下cd
os.chdir("C:\\Users\\admin\\PycharmProjects\\s14")
os.chdir(r"C:\Users\admin\PycharmProjects\s14")
print(os.getcwd())    # C:\Users\admin\PycharmProjects\s14

# 返回当前目录: ('.')
print(os.curdir)        # ('.')

#4 获取当前目录的父目录字符串名:('..')
print(os.pardir)        # ('..')

# 可生成多层递归目录
os.makedirs(r'C:\aaa\bbb')         # 可以发现在C盘创建了文件夹/aaa/bbb

# 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.removedirs(r'C:\aaa\bbb')    # 删除所有空目录


# 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.rmdir(r'C:\aaa')        # 仅删除指定的一个空目录

# 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
print(os.listdir(r"C:\Users\admin\PycharmProjects\s14"))

# 删除一个文件
os.remove(r'C:\bbb\test.txt')        # 指定删除test.txt文件

# 重命名文件/目录
os.rename(r'C:\bbb\test.txt',r'C:\bbb\test00.bak')

# 获取文件/目录信息
print(os.stat(r'C:\bbb\test.txt'))

# 运行shell命令,直接显示
os.system("bash command")

# 获取系统环境变量
print(os.environ)                # environ({'OS': 'Windows_NT', 'PUBLIC': ………….

# 返回path规范化的绝对路径
print(os.path.abspath(r'C:/bbb/test.txt'))    # C:\bbb\test.txt

# 将path分割成目录和文件名二元组返回
print(os.path.split(r'C:/bbb/ccc'))    # ('C:/bbb', 'ccc')

# 无论linux还是windows,拼接出文件路径
put_filename = '%s%s%s'%(self.home,os. path.sep, filename)
#C:\Users\admin\PycharmProjects\s14\day10select版FTP\home

time

time()模块中的重要函数
函数 描述
asctime([tuple]) 将时间元组转换为字符串
localtime([secs]) 将秒数转换为日期元组(转换成本国时区
mktime(tuple) 将时间元组转换为本地时间
time() 获取当前时间戳
time()模块时间转换

点击这里


datetime

import datetime
#1、datetime.datetime获取当前时间
print(datetime.datetime.now())
#2、获取三天后的时间
print(datetime.datetime.now()+datetime.timedelta(+3))
#3、获取三天前的时间
print(datetime.datetime.now()+datetime.timedelta(-3))
#4、获取三个小时后的时间
print(datetime.datetime.now()+datetime.timedelta(hours=3))
#5、获取三分钟以前的时间
print(datetime.datetime.now()+datetime.timedelta(minutes = -3))

import datetime
print(datetime.datetime.now())                                   #2017-08-18 11:25:52.618873
print(datetime.datetime.now().date())                            #2017-08-18
print(datetime.datetime.now().strftime("%Y-%m-%d %H-%M-%S"))    #2017-08-18 11-25-52

时间戳转换成datetime对象
# datetime.datetime.fromtimestamp(1520561646.8906238)
datetime.datetime(2018, 3, 9, 10, 14, 6, 890624)





博客导读目录

点击这里

你可能感兴趣的:(Python,python)