python进阶,常见的Python问题

python 是如何进行内存管理的?

一 对象的引用计数机制

Python内部使用引用计数,来保持追踪内存中的对象,所有对象都有引用计数

引用计数增加的情况?

1一个对象分配一个新的名称

2将其放到一个容器中(如列表,元组或字典)

引用计数减少的情况?

1 使用del语句对对象别名显示的销毁

2 引用超出作用域或被重新赋值

sys.getrfcount()函数可以获得对象的当前引用计数

多数情况下,引用计数比你猜测的要大得多,对于不可变数据(如数字和字符串),解释器会在程序的不同部分共享内存,以便节约内存。

二垃圾回收

1 当一个对象的引用计数归零时,它将被垃圾回收机制处理掉

2 当两个对象a,b互相引用时,del语句可以减少a,b的引用计数,并销毁用于引用底层对象的名称,然而由于每个对象都包含一个对其他对象的应用,因此引用计数不会归零,对象也不会被销毁(从而导致内存泄漏)为解决这一问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除他们。

三内存池机制

Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。

1 pymalloc机制,为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放

2Python中所有小于256个字节的对象都使用pymalloc实现的分配器,而大的对象则使用系统的malloc

3对于Python对象,如整数,浮点数和List,都有独立的私有内存池,对象间不共享他们的内存池,就是说如果你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。

Python里面如何拷贝一个对象?

1 赋值(=),就是创建了对象的一个新的引用,修改其中任何一个变量都会影响另一个

2浅拷贝,创建一个新的对象,但是它包含的是对原始对象中包含项的引用(即复制了引用,内存没变,一个对象被改变另一个也会被改变,如完全切片,copy模块的copy()函数)

3深拷贝:创建一个对象,并且递归的复制它所包含的对象(修改其中一个,另外一个不会改变copy模块中的deep.deepcopy()函数)

Python里面的match()和search()的区别?

re.match(pattern,string),检查string的开头是否与pattern匹配

re.seach(pattern,string),在string中搜索与pattern的第一个匹配值

Python里如何生成随机数

random模块

随机整数:random.randint(a,b),返回随机整数x,a<=x<=b

​ random.randrange(start,stop,[step])返回一个范围在(start,stop)之间的随机数step控制间 隔,不包括结束值。

随机实数:random.random(),返回0到1之间的浮点数。random.uniform(a,b),返回指定范围的浮点数

Python中单引号,双引号,三引号的区别

单引号和双引号是等效的,如果要换行,需要符号(\),三引号则可以直接换行,并且可以包含注释。

Python函数参数的传递

a = 1
def fun(a):
    a = 2
fun(a)
print(a)#1

b = []
def fun(b):
    b.append(1)
fun(b)
print(b)#[1]

所有的变量都可以理解为内存中一个对象的“引用”。而类型是属于对象的,而不是变量。

在python中对象有两种,可变对象(list,dict),不可变对象(number,string,tuple)

当一个引用传递给函数的时候,函数自动复制一份引用,在第一个例子中函数把引用指向了一个不可变对象,也就是在函数内分配了一块新的内存,这块内存的引用也叫a,但不会函数内引用a的改变不会影响函数外引用a。而第二个例子中把引用指向了可变对象,既内存可变,函数内外的引用b都指向同一块内存。

Python中@staticmethod和@classmethod

class A(object):
    def foo(self,x):
        print("executing foo%s%s"%(self,x))

    @staticmethod
    def static_foo(x):
        print("executing static_foo%s"%x)

    @classmethod
    def class_foo(cls,x):
        print("executing class_foo%s%s"%(cls,x))

a = A()
a.foo(1)#executing foo<__main__.A object at 0x03BB6210>1
a.static_foo(1)#executing static_foo1
a.class_foo(1)#executing class_foo<class '__main__.A'>1
A.static_foo(1)#executing static_foo1
A.class_foo(1)#executing class_foo<class '__main__.A'>1

先解释一下函数参数里面的self,cls,这个self和cls是对类或者实例的绑定,对于一般函数来说我们可以直接foo(x),这个函数就是最常用的,它的工作跟任何东西(类,实例)无关,对于实例方法,我们知道在类里每次定义方法的时候都需要绑定这个实例,就是foo(self,x)。因为实例方法的调用离不开实例,我们需要把实例自己传给函数,调用的时候是这样的a.foo(x)其实就是foo(a,x),类方法一样,只不过它传递的是类而不是实例。A.class_foo(x)注意这里的self,cls可以替换成别的参数,但python约定是这俩,最好不要改。有时候,在多个类中都会使用到相同的一些工具函数,或者在一个类中,有一个在别的类中使用到的通用函数,那么这时候,如果每次使用都要实例化后再调用就太冗余浪费了,类中静态方法和类方法就是为了解决这一问题。

Python类变量和实例变量

class Person:
    name="aaa"

p1=Person()
p2=Person()
p1.name="bbb"
print p1.name  # bbb
print p2.name  # aaa
print Person.name  # aaa

class Person:
    name=[]
    
p1=Person()
p2=Person()
p1.name.append(1)
print p1.name  # [1]
print p2.name  # [1]
print Person.name  # [1]

p1.name="bbb"是实例调用了类变量,这其实和函数的参数传递是一样的,p1.name一开始是指向的类变量name=‘aaa’,但是在实例的作用域里把类变量的引用改变了,就变成了一个实例变量p1.name就不再引用Person的类变量name了。

Python的自省

自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型,简单一句就是运行时能够获得对象的类型,比如type(),dir(),getattr(),hasattr(),isinstance()

dir([obj]):

dir()函数是python内置函数,可能是Python自省机制中最著名的部分了,它返回传递给它的任何对象的经过排序的属性名称列表(会有一些特殊的属性不包含在内),如果不指定对象,则dir()返回当前作用域中的名称(obj的默认值是当前的模块对象)

类似__ XXX __的属性和方法在python中都是有特殊用途的比如

__ len __方法返回长度,在Python中如果你调用len()函数试图获取一个对象长度,实际上在len()函数内部,它会自动调用该对象的

__ len __()方法,下面的代码是等价的

print(len('abc'))#3
print('abc'.__len__())#3

配合getattr(),setattr(),hasattr(),我们可以直接操作一个对象的状态

>>> class MyObject(object):
...     def __init__(self):
...         self.x = 9
...     def power(self):
...         return self.x * self.x
>>> obj = MyObject()

>>> hasattr(obj, 'x') # 有属性'x'吗?
True
>>> obj.x
9
>>> hasattr(obj, 'y') # 有属性'y'吗?
False
>>> setattr(obj, 'y', 19) # 设置一个属性'y'
>>> hasattr(obj, 'y') # 有属性'y'吗?
True
>>> getattr(obj, 'y') # 获取属性'y'
19
>>> obj.y # 获取属性'y'
19
#如果试图获取不存在的属性,会抛出AttributeError的错误
>>> getattr(obj, 'z') # 获取属性'z'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyObject' object has no attribute 'z'
# 可以传入一个default参数,如果属性不存在,就返回默认值
>>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404
404
>>> hasattr(obj, 'power') # 有属性'power'吗?
True
>>> getattr(obj, 'power') # 获取属性'power'
<bound method MyObject.power of <__main__.MyObject object at 0x108ca35d0>>
>>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
>>> fn # fn指向obj.power
<bound method MyObject.power of <__main__.MyObject object at 0x108ca35d0>>
>>> fn() # 调用fn()与调用obj.power()是一样的
81

Python字典推导式,列表推导式,集合推导式

推导式又称解析式,是Python一种独有的特性,推导式是可以从一个数据序列构建另一个新的数据序列的结构体,共有三种推导式。

1列表推导式

使用[]生成list

variable = [out_exp_res for out_exp in input_list if out_exp == 2]

out_exp_res:列表生成元素表达式,可以是有返回值的函数

for out_exp in input_list:迭代input_list 将 out_exp传入out_exp_res表达式中

if out_exp == 2:根据条件过滤那些值是可以的

lis = [i for i in range(30) if i % 3 is 0]
print(lis)#[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

def fun(x):
    return x*x
lis = [fun(i) for i in range(30) if i % 3 is 0]
print(lis)#[0, 9, 36, 81, 144, 225, 324, 441, 576, 729]

使用()生成generator

将上面俩列表推导式的[]改为()即可得到生成器,generator保存的是算法,每次调用next(),就计算出下一个元素的值,知道计算出最后一个元素的值,没有更多元素时,则抛出StopIteration错误

def fun(x):
    return x*x
lis = (fun(i) for i in range(10) if i % 3 is 0)
print(lis)
print('aa',next(lis))
for i in lis:
    print(i)
<generator object <genexpr> at 0x00323F90>
aa 0
9
36
81

2字典推导式

字典推导式和列表推导式使用的方法时类似的,只不过是中括号改为大括号

d = {'a':1,'b':2}
for k ,v in enumerate(d):
    print(k,v)
new_d = {k:v for v,k in d.items()}
print(new_d)
0 a
1 b
{1: 'a', 2: 'b'}

3集合推导式

跟列表推导式类似,区别在于它使用大括号{}

s = {i*i for i in [1,1,3]}
print(s)
{1, 9}

python中的单下划线和双下划线

__ foo __,一种约定,python内部的名字,用来区别其他用户自定义的命名,以防冲突

__ foo,双下划线开头,有实际意义,表示为私有成员,只允许类本身访问,子类也不行,在文本上被替换 为 _class __foo

class_ ,以单下划线结尾仅仅是为了区别该名称与关键词

_foo,以单下划线开头,表示这是一个保护成员,只有类对象和子类对象自己能访问到这些变量,以单下划线开头的变量和函数被默认当做是内部函数,使用 from module import * 时不会被获取,但是使用 import module 可以获取

python中字符串格式化:%和.format的区别

1 %的方式无法同时传递一个变量和元组

name = 'zhangsan'
s = 'my name is %s'%name
print(s)#my name is zhangsan
#假如name变量现在正好是元组(1,2,3),则会报Typeerror异常,为保证他正常必须这样做
name = (1,2,3)
s = 'my name is %s'%(name,)#提供一个数组而不是一个参数
print(s)#my name is (1, 2, 3)

2 .format作为一个参数他可以这样使用

lis = [1,2,3,4]
print('{3},{2},{1},{0}'.format(*lis))
#.format作为一个函数,可以用作其他函数的参数
print(list(map('number is {}'.format,lis)))

python中的*args和**kwargs

使用*args和**kwargs只是为了方便并没有强制使用它们

*args用来将参数打包成tuple()给函数体调用,输出结果将以元组的形式展示

def func(*args):
    print(args, type(args))
func(1,2,3)
(1, 2, 3) <class 'tuple'>

def func(x,y,*args):
    print(x, y, args, type(args))
func(1,2,3,4,5)
1 2 (3, 4, 5) <class 'tuple'>

**kwargs打包关键字参数成dict给函数体调用

def func(**kwargs):
    print(kwargs, type(kwargs))
func(a='1',b='2')
{'a': '1', 'b': '2'} <class 'dict'>

注意arg,*args,**kwargs三个参数的位置必须是一定的,必须是(arg,*args,**kwargs)

命名关键字参数:参数中用*,隔开的参数(a,*,b)b就是命名关键词参数,赋值时这个参数必须有且名称不能变(a,*args,b)此时b也是命名关键字参数
def func(arg,*args,**kwargs):
    print(arg,args,kwargs)
func(1,2,3,a=1,b=2)
1 (2, 3) {'a': 1, 'b': 2}

面向切面编程AOP和装饰器

装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志,性能测试,事务处理,装饰器是解决这类问题的绝佳设计,有了装饰器,我们可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用,概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

鸭子类型

“当看到一只鸟走起来像鸭子,游泳起来像鸭子,叫起来也像鸭子,那么这只鸟就可以被称为鸭子”

我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。

比如list.extend()方法中,我们并不关心它的参数是不是list,只要它是可迭代的,所以它的参数可以是list/tuple/dict/字符串/生成器

Python中的重载

函数重载是一个同名函数完成不同的功能,编译系统在编译阶段通过函数参数个数,参数类型不同,函数的返回值来区分该调用哪一个函数,即实现的是静态的多态性

函数重载主要是为了解决两个问题

可变参数类型

可变参数个数

一个设计的基本原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才是用函数重载,如果两个函数功能其实不同,那么不应当使用重载,而应当使用一个名字不同的参数。

如果是函数功能相同,参数类型不同,python该如何处理?

答:不需要处理,因为Python可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在Python中很可能是相同的代码,没有必要做成两个不同的函数

如果是函数功能相同,但是参数个数不同,该如何处理?

答,缺省参数,对那些缺少的参数设定为缺省参数即可解决问题,因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。

Python新式类和旧式类

1 新式类都是从object继承,经典类不需要

2 新式类的mro(基类搜索顺序)算法采用c3算法,而旧式类的mro采用深度优先搜索

3 新式类相同父类只执行一次构造函数,经典类重复执行多次

4 在python3.x中默认都是新式类,经典类被移除,不必显示的继承object

#在python2.x中的经典类
class A:
	pass
class B:
	pass
class C(B):
	pass 
class D(C,A):
	pass
#深度优先执行顺序为D->C->B->A
#在python3.x中的新式类并不是按照c3算法
#mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2),  [ Base1, Base2] )
#(其中Child继承自Base1, Base2)
class G:
    var = "Class G"
class E(G):
    pass
class F(G):
    pass
class B(E):
    pass
class C(F):
    pass
class D(G):
    pass
class A(B,C,D):
    pass
a = A()
print(a.var)
print(A.__mro__)
#Class G
#(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>)

Python单例模式

单例模式既一个类有且只有一个实例

class B:
    pass

b1 = B()
print(id(b1))
b2 = B()
print(id(b2))
#通过打印实例id可以发现,B默认创建了两个实例
#12315664
#12315440
#我们需要让一个类创建完一个实例后,再次创建时返回上一次创建的引用,在python中一个类创建对象实例是通过调用父类object的__new__(cls)方法来创建对象的,我们可以重写__new(cls)方法实现单例模式
class A(object):
    _instance = None#定义一个类属性做判断
    def __new__(cls, *args, **kwargs):
        if cls._instance == None:#为None则证明是第一次创建实例
            cls._instance = object.__new__(cls)#创建实例
            return cls._instance
        else:
            return cls._instance

a = A()
print(id(a))
b = A()
print(id(b))
#11821616
#11821616

Python GIL线程全局锁

线程全局锁(GIL)既python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程

解决方法就是多进程和协程

python 中__new__ 和__init__

__new__ 方法接受的参数虽然也是和__init__ 一样,但是__init__ 是在类实例创建之后调用,而__ new__ 方法正是创建这个类实例的方法。所以说__ new __ 方法的调用是发生在__ init __ 之前的,并且__ new __ 方法还决定是否使用该类的__init__ 方法

#1 首先执行 a = A('a')
#2 使用name这个参数来执行A类的__new__方法,这个方法会返回A类的一个实例
#3 然后利用这个实例来调用类的__init__方法,上一步__new__产生的实例也就是__init__里面的self
class A(object):

    def __new__(cls, name):#控制生成一个类实例,是类级别的
        print('__new__')
        #return  super(A,cls).__new__(cls, name)这样写会报错,TypeError: object() takes no parameters。object不接受参数
        return super(A,cls).__new__(cls)

    def __init__(self,name):#用于初始化一个新实例,比如添加一些属性,是实例级别的
        print('__init__')
        self.name = name

    def __str__(self):
        return '%s is A'%(self.name)

a = A('a')
print(a)
#通过打印结果可以看出__new__ 在__init__之前调用
__new__
__init__
a is A

#__new__方法主要是当你继承了一些不可变的class时(int,str)提供给你一个自定义这些类的实例化过程的途径,比如我们需要一个永远都是正数的整数类型。
class Integer(int):

    def __new__(cls, value):
        return super(Integer, cls).__new__(cls, abs(value))

i = Integer(-3)
print(i)

3

python中的闭包

如果在一个函数的内部定义了另一个函数,外部的我们叫他外函数,内部的我们叫他内函数

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

一般情况下,在我们认知中,如果一个函数结束,函数的内部所有的东西都会释放掉,还给内存,局部变量都会消失,但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将会在内部函数中用到,就会把这个临时变量绑定给内部函数,然后再自己结束。

#闭包函数的实例
# outer是外部函数 a和b都是外函数的临时变量
def outer( a ):
    b = 10
    # inner是内函数
    def inner():
        #在内函数中 用到了外函数的临时变量
        print(a+b)
    # 外函数的返回值是内函数的引用
    return inner

if __name__ == '__main__':
    # 在这里我们调用外函数传入参数5
    #此时外函数两个临时变量 a是5 b是10 ,并创建了内函数,然后把内函数的引用返回存给了demo
    # 外函数结束的时候发现内部函数将会用到自己的临时变量,这两个临时变量就不会释放,会绑定给这个内部函数
    demo = outer(5)
    # 我们调用内部函数,看一看内部函数是不是能使用外部函数的临时变量
    # demo存了外函数的返回值,也就是inner函数的引用,这里相当于执行inner函数
    demo() # 15
    demo2 = outer(7)
    demo2()#17

在Python语法中,一个函数可以随意读取全局数据,但是要修改全局数据的时候有两种方法

1 global声明全局变量

2 全局变量是可变类型数据的时候可以修改

在闭包内函数也是类似的情况,在函数中想修改闭包变量的时候

1 在python3中,可以用nonlocal关键字声明一个变量,表示这个变量不是局部变量空间,需要向上一层变量空间找这个变量

2 在python2中,没有nonlocal这个关键字,我们可以把闭包变量改成可变数据类型进行修改,比如列表

def outer( a ):
    b = 10  # a和b都是闭包变量
    c = [a] #这里对应修改闭包变量的方法2
    # inner是内函数
    def inner():
        #内函数中想修改闭包变量
        # 方法1 nonlocal关键字声明
        nonlocal  b
        b+=1
        # 方法二,把闭包变量修改成可变数据类型 比如列表
        c[0] += 1
        print(c[0])
        print(b)
    # 外函数的返回值是内函数的引用
    return inner

if __name__ == '__main__':
    demo = outer(5)
    demo() # 6  11

Python中read(),readline(),readlines()的用法

read(): 每次读取整个文件,将内容整个放到一个字符串中,不适合连续的行处理,如果文件大于可用内存,那么不可以使用read()

readlines():自动将文件内容分析成一个行的列表,可以用for …in…结构进行处理

readline(): readline每次读取一行,比readlines()慢多了,有点像生成器,仅当没有足够内存可以一次读取整个文件时,才使用readline()

python 中的生成器和迭代器

迭代器:对于list,string,tuple,dict等这些容器对象,使用for循环遍历是很方便的,在后台for语句对容器对象调用iter()函数。iter()函数是python的内置函数

iter()函数会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内的对象,next()也是python的内置函数,在没有后续元素时,next()会抛出一个Stopiteration异常,通知for语句循环结束

迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一位置的数据,实际上在使用next()函数的时候,调用的就是迭代器对象的__next__ 方法(在python3中)。所以如果我们想要构造一个迭代器实现__ next__ 方法,但是这还不够,python要求迭代器本身也是可迭代的,所以我们还需要为迭代器实现__ iter__ 方法,而__ iter__ 方法需要返回一个迭代器,迭代器本身就是一个迭代器,所以迭代器__ iter__ 方法返回自身self即可

1 迭代器协议:对象需要提供next()方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,直到迭代终止

2 可迭代对象:实现了迭代器协议对象。list,tuple,dict都是可迭代对象,但不是迭代器对象。但是可以使用内建函数iter(),把这些变成迭代器对象

3 for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象的迭代器。然后对获取到的迭代器不断调用next()方法来获取下一个值,并将其赋值给item.遇到stop…异常时迭代停止

l = [1,2]
iterl = iter(l)
print(iterl)		#<list_iterator object at 0x006CB3F0>可迭代器
print(next(iterl))	#1
print(next(iterl))	#2
print(next(iterl))	#StopIteration

#自定义迭代器类,并且实现斐波那契数列
class Fib(object):

    def __init__(self,max):
        super(Fib,self).__init__()
        self.max = max

    def __iter__(self):
        self.a = 0
        self.b = 1
        return self

    def __next__(self):
        fib = self.a
        if fib > self.max:
            raise StopIteration
        self.a, self.b = self.b, self.a + self.b
        return fib

def main():
    fib = Fib(20)
    for i in fib:
        print(i)

if __name__ == '__main__':
    main()
'''
在循环遍历自定义容器对象时,会使用python内置函数iter()调用遍历对象的__iter__(self)获得一个迭代器,之后再循环对这个迭代器使用next()调用迭代器对象的__next__(self)
注意:__iter__(self)只会被调用一次,而__next__(self)会被调用n次直到出现异常
'''

生成器:

作用:延迟操作,也就是在需要的时候才产生结果,不是立即产生结果

生成器是只能遍历一次的

生成器是一类特殊的迭代器

'''
第一类生成器函数,使用def定义函数,但是,使用yield而不是return语句返回结果,yield语句一次返回一个结果,在每个结果中间挂起函数的状态,以便下次从它离开的地方继续执行
'''
def fib(max):
    n, a, b = max, 0, 1
    while a < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return "no num"

f = fib(10)
while True:
    try:
        x = next(f)
        print('f',x)
    except StopIteration as e:
        print(e.value)
        break
'''
第二类生成器表达式,类似于列表推导式,只不过是把[]换成(),但是生成器表达式是按需产生一个生成器结果对象,想要拿到每一个元素,就需要循环遍历
'''
l = [1,2,3,4]
gen = (i for i in l)
print(gen)
for j in gen:
    print(j)
#<generator object <genexpr> at 0x02B83F90>
#1
#2
#3
#4
'''为什么要使用生成器,因为效率,使用生成器表达式取代列表推导式可以同时节省cpu和内存'''

python2和python3的区别

2:print

3:prin()函数

2:有ascll字符串类型,unicode()是单独的

3:有了Unicode(utf-8)字符串,默认使用utf-8编码

2:/整数和整数相除,结果是一个整数,会把小数部分完全忽略1/2=0 1.0/2.0=0.5

3:/整数之间相除结果也会是浮点数 1/2=0.5

2:异常except exc , var

3:异常except exc as var

2:map,filter他们输出的结果都是列表

3:map,filter 输出结果变成了一个可迭代对象

调度算法

先来先服务

短作业优先

时间片轮转

乐观锁和悲观锁

悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据的完整性

数据库引擎MyISAM,InnoDB

MyISAM:适合于一些需要大量查询的应用,但对于有大量写操作并不是很好,可能一个更新操作都会导致表被锁起来。

InnoDB:支持“行锁”,在写操作比较多的时候会比较优秀,支持事务

三次握手

1 客户端向服务器发送一个SYN来创建一个主动打开,客户端把这段连接的序号设为随机数A

2 服务端收到合法的SYN回送一个SYN/ACk。ACK的确认码为A+1,(SYN/ACK)包里又包含一个随机码B

3 客户端再发送一个ACK,当服务端收到ACK的时候,就完成了三次握手,进入连接创建状态, 此时包序号被设定为收到确认号为A+1,响应为B+1

四次挥手

简单描述A客户端B服务端

1 A 向B说我要关闭链接了(第一次挥手)

2 B 向 A说我收到你的信息了,知道你要关闭链接了(第二次挥手)

3 B向A说我也要关闭链接了(第三次挥手)

4 A向B说 收到你要关闭链接的信息(第四次挥手)

ARP协议:根据IP地址获取物理地址的一个TCP/IP协议

冒泡排序和插入排序

import requests
import random
#冒泡排序,时间复杂度O(n^2)几次循环就是n的几次方的时间复杂度
def bubbling():
    lis = [1,4,34,76,12,45,90,456,23,0,43]
    lis_len = len(lis)
    for i in range(0,lis_len):
        for j in range(i+1,lis_len):
            if lis[i] > lis[j]:
                lis[i],lis[j] = lis[j],lis[i]
    print(lis)

#直接插入排序,时间复杂度最好情况为O(n),最差为O(n^2)
def insert():
    lis = [0,2,4,1,7,5,3,9,8]
    count = len(lis)
    for i in range(1,count):
        if lis[i]<lis[i-1]:
            temp = lis[i]
            j = i-1
            while lis[j]>temp and j >= 0:
                lis[j+1] = lis[j]
                j -= 1
            lis[j+1] = temp
    print(lis)

Python网络编程

"""
如果使用tcp协议来传递数据,客户端和服务器端需要分别经过以下步骤
server:创建socket,bind(绑定socket到指定地址),listen(在端口监听来自客户端的链接),accept(接受客户端的链接请求),send(向客户端发送数据),close(关闭连接)
client:创建socket,connect(发出链接请求),receive(接受服务器端发送来的数据),close(关闭此次链接)
"""
import socket
import time

def server():
    sock = socket.socket()
    sock.bind(('localhost',8080))
    sock.listen(1)
    while True:
        connection, address = sock.accept()
        connection.settimeout(1)
        connection.recv(1024)#控制接收
        connection.send('ok')#发送数据
        connection.close()#关闭连接

if __name__ == '__main__':
    server()

def client():
    sock = socket.socket()
    sock.connect(('localhost',8080))
    time.sleep(2)
    sock.send('1')
    print(sock.recv(1024))
    sock.close()

if __name__ == '__main__':
    client()

scrapy的架构以及爬取流程

架构:

scrapy引擎:整个scrapy架构中核心是scrapy引擎,它控制着整个数据处理流程,并且进行事务处理的触发

调度器:它的作用是从引擎接收请求并列入队列,在引擎发出请求后把符合调度的url返还给引擎。

下载器:主要作用是抓取网页,并将网页的内容通过引擎返还给spider。

spider(爬虫):定义特定网站的抓取和解析规则。

item:负责处理抓取到的数据。

三个中间件:downloader middlewares,spider middlewares,schedule middlewres,自定义代码的方式来扩展scrapy功能

抓取流程:

1 引擎打开一个域名,spider处理这个域名,并且抓取第一个url。

2 引擎获取spider传来的request,并且作为请求列入调度器队列

3 引擎从调度器获得下一个要抓取的页面

4 引擎获取url之后,通过中间件发送给下载器

5 网页被下载完成后,下载器将response返回给引擎

6 引擎将收到的网页内容发送给spider处理,spider将处理后的数据返回给引擎

7 引擎将需要的数据发送到item存储,将新的请求发送给调度器

8 系统重复上面操作,知道调度中没有请求,然后断开引擎与各个域之间的联系

常用正则

[abc]			#匹配a或者b或者c
[a-zA-Z]		#用来匹配任意一个字母
[0-9]			#用来匹配任意一个数字	\d
[a-zA-Z0-9_]	#用来匹配任意一个数字字母下划线	\w
[^a-zA-Z0-9_]	#匹配非数字字母下划线		\W
^				#以什么开头或者取反
^a				#以字母a作为开头
^[0-9]			#匹配以数字作为开头的
[^0-9]			#匹配一个非数字字符	\D
$				#以什么作为结尾
{m}				#表示数量控制位m个,不能自己单独使用
[a]{3}			#表示匹配3个连续的字母a
{m,}   			#表示前面的数量最少为m个
[0-9]{4,}		#表示最少4位数字
{m,n}			#表示前面的数量为m到n个
[1][3-8][0-9]{9}#手机号
.				#匹配换行符以外的任意字符
* 				#匹配任意字符任意次
.*				#匹配除换行符以外的任意字符任意次,默认为贪婪模式
.*?				#匹配除换行符以外的任意字符任意次,为拒绝贪婪模式
+				#表示匹配数量最少为1次{1,}
.+				#表示匹配除换行符以外的任意字符最少一次
()				#作为一个单元,作为一个子存储
|				#或

Linux常用命令

1 cd , ls, cp, mv, rm, mkdir

​ cp/mv/rm : -i 询问 -f 强制 -n 不覆盖

2 pwd 显示当前完整路径

3 ln -s [src_file] [dst_file] 建立软链接

4 touch 创建,没有则创建,有则忽略

5 history 打印使用过的命令

6 ps -aux 查看所有进程

7 ps -ef | grep “python” 查看python进程

8 top 动态监控进程

9 free -m 查看内存

10 kill -9 进程号 杀死进程

11 su - 切换用户身份

12 chmod -R 修改权限

13 cat 文件名 查看文件

14 find DIR -name ’*.xxx’ 找到目录下所有名字匹配的文件

15 ifconfig 查看网卡状态

16 netstat -natp 查看网络连接状态

17 tar -czf 压缩

18 tar -xzf 解压

19 du -hs 以友好方式显示文件大小

20 df -h 查看磁盘分区占用情况

HTTP中常用的请求方法有哪些?哪些请求方法是安全的?为什么?

常用的请求方法有GET,POST,HEAD,PUT,DELETE。其中GET,HEAD是安全的,因为这两个方法只是请求数据,而其他三个方法都会对服务器产生动作。

GET,POST方法的区别?

1 get请求在页面后退的时候是无害的(既不会再次发送请求),而post会再次发送请求

2 get请求的参数是放在url中的,而post是放在请求体中的

3 get请求在url中传递参数是有长度限制的,而post无限制(不是绝对而是相对来说)

4 get请求会被浏览器主动缓存,而post不会

5 get请求的参数会保存在浏览器中,而post的参数不会保存在浏览器中

6 get产生一个tcp数据包,post产生两个tcp数据包,get请求时浏览器会把http的header和data一并发出去,服务器响应200(返回数据),post浏览器先发送header,服务器响应100,浏览器再发data,服务器响应200(返回数据)

在浏览器中输入url到页面进行渲染的过程中发生了什么?

1 浏览器解析主机名

2 DNS进行域名解析,即将语义化的主机名转换成ip地址

3 浏览器获得端口号

4 浏览器根据得到的ip地址和端口号发起tcp链接

5 浏览器发起http请求

6 浏览器读取服务器返回的响应报文

7 浏览器对返回的html进行渲染

8 浏览器断开连接

TCP两次握手可以吗?

不可以,三次握手主要是为了防止已经过期的请求再次链接到服务器端而占用资源而造成浪费。如果是两次握手的话,假设主机A在发送第一次请求时,由于网络滞留的问题卡住了,很久没有收到服务器端发来的确认信息,于是又发送了第二次请求,过了一段时间第一次请求到达了服务器,但是服务器会认为这是一次新的请求,就返回确认信息,但是没有第三次握手,只要服务器发出确认信息,就会链接,这时候服务器就会一直等待主机A发送信息,就会造成资源浪费。

TCP和UDP的区别是什么?

在连接方面:tcp是面向连接的,而udp是无连接的,既udp在传输数据之前不需要想tcp那样3次握手建立连接

可靠性方面:tcp比udp更可靠,tcp可以保证不丢包,会按照顺序传递数据

资源消耗方面:tcp对系统资源要求比较高,并且消耗资源比较大,udp要求不高,但在网络情况不好的情况下比 较容易丢包,消耗资源相对较小

适用场景:tcp适合于http,ftp以及邮件传输等等,而udp比较适合语音,视频

速度方面:tcp传输速度比较慢,效率低,在握手挥手过程中会占用较多时间,udp传输速度比较快,由于是无连接的,只有传输数据的过程

安全性方面:安全性和可靠性是不同概念,由于tcp的机制比较多,更容易受到攻击,而udp就相对安全些

连接形式:tcp只能是一对一发送,而udp可以是一对一,一对多,多对多

HTTP有什么缺点

1 一条连接只能发送一个请求

2 请求只能从客户端开始,客户端不能接受除响应以外的指令

3 请求/响应首部未经压缩就发送,首部信息越多造成的延迟越大

4 发送冗长的首部,每次互发相同的首部造成很大的浪费

5 通信使用明文,内容可能会被窃听

6 不验证通讯方的身份,有可能遭遇伪装

7 无法验证报文的完整性,有可能遭遇篡改

AJAX实现原理

ajax的实现核心在于XMLHttpRequest这个api,主要分为以下几个步骤

1 创建XMLHttpRequest对象

2 初始化传递进来的参数

3 使用open方法发送请求

4 接收请求并调用onreadystatechange方法对返回成功及失败的情况进行定义

什么是http持久化和管道化

http持久化:是针对http无连接的特点进行改进的,使用keep-alive首部字段保持长连接

http管道化:是针对http每次只能是请求一次回答一次的模式进行改进,使得可以连续发送多次请求

HTTP和HTTPS的关系?

https是在http的基础上加上了ssl协议,使得http通讯更加安全,针对http无法验证通讯方的身份,无法验证报文完整性以及容易被窃听等安全方面的缺陷,https添加了加密认证机制,使得http通信更加安全

https的加密机制:

1 客户端发送请求

2 服务端接受请求并返回数字证书

3 客户端使用内置的CA解密证书,拿到公钥

4 如果证书没有问题,就将自己的对称密钥使用公钥加密发送给服务端

5 服务端使用私钥解密

6 客户端服务端使用对称密钥进行通信

webSocket是什么?主要作用是什么?

webSocket也是一种协议,用来解决要实时更新时,只能通过ajax轮询等方式的问题,使用这个协议就改变了只能客户端发送请求,服务端接收请求的模式,在该协议中客户端服务端都能发送请求和接收请求,从而实现实时更新(服务端有数据更新后就发送数据)的功能,基于该协议可以大大减少通信量

SSL有几次握手,具体过程是怎样的?

4次

1 客户端请求SSL连接

2 服务端发送包含公钥的证书

3 客户端使用公钥加密对称密钥并发送给服务端

4 服务端使用私钥解密对称密钥

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值