python杂七杂八

常见数据结构

  • 线性结构:list/tuple,array/collections.namedtuple

  • 链式结构:-----------,collections.deque(双端队列)-

  • 字典结构:dict,collections.Counter/OrderedDict

  • 集合结构:set/frozenset,-----------

  • 排序算法:sorted,----------

  • 二分算法:-----------,bisect

  • 堆算法:--------,heapq

  • 缓存算法:-----------,functools.lru_cache(左边代表内置,右边代表第三方)

 

内置结构常用方法

 

python内置数据类型注意点:

  • 元组不可变指的是保存的数据引用不可变,比如列表套元组,列表可以改变

  • 字典底层是hash表实现,复杂度是O(1)

 

python内存管理

  • 引用计数:引用数为0时,清除

  • 标记清除,是解决循环引用,从根对象可以到达的每个对象,不可达到的就清除

  • 分代回收:对象生命周期分为0,1,2代 ,每隔一段时间对从第代开始进行标记清除

 

python常用标准库?

  • 综合:re,json,pickle,math,hmac,hashlib,random,logging,pytest

  • 异步编程:threading,multiprocessing,gevent,asyncio

  • 网络:urllib,socket,http,aiohttp,request

  • 文件,系统:os,sys,io

  • 函数式编程:itertools,functools

 

第三方库?

  • flask,django,sqlalchemy,celery,pymysql,pymongo,redis,request,xlrd,xlwt,lxml,appium,selenium,scrapy。。

 

GIL(全局解释器锁)

  • 是由于cpython解释器造成的

  • 每个线程在执行过程中都需要先获取GIL,保证同一时刻只有一个线程执行代码

  • 什么情况会释放锁:在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL

 

深浅拷贝

  • 深拷贝(copy.deepcopy):对于一个对象递归拷贝,只有所有嵌套层全为不可变数据类型才指向,否则一律拷贝

  • 浅拷贝:对于可变数据类型,顶层拷贝,内部指向;对于不可变数据类型,全部指向;切片和赋值工厂函数都属于浅拷贝

 

with打开文件的时候帮我们做了什么?

  • with是一种上下文管理协议,帮我们实现了try,except,finally和资源释放的代码,简化处理流程

  • 上下文管理器就是实现来enterexit两个魔法方法的对象都可以叫上下文管理器,还可以用contextlib中的contextmanager来实现

  • from contextlib import contextmanager
    ​
    @contextmanager
    def my_open(path, mode):
        f = open(path, mode)
        yield f
        f.close()

 

os模块常用方法?

 

sys模块常用方法?

 

python解释器种类以及相关特点?

  • cpython(默认),ipython(基于cpython,交互式),jpython(将python代码解释称java字节码),ironpython(将python代码解释为.net)

 

什么是PEP8?

  • 命名

    • 常量:大写加下划线 USER_CONSTANT。

    • 私有变量或函数名 : 小写和一个前导下划线 _private_value

    • 内置变量 : 小写,两个前导下划线和两个后置下划线 class

  • 不要乱用*args和**kwargs

  • 一行不要太长,一个函数不要超过30行,一个类不要超过200行,一个模块不要超过500行

 

python之禅

 
优美胜于丑陋(Python 以编写优美的代码为目标)
明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)
简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)
复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)
扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套)
间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)
可读性很重要(优美的代码是可读的)。。。

 

了解docstring吗?

  • DocStrings 文档字符串是一个重要工具,用于解释文档程序,帮助你的程序文档更加简单易懂。

    我们可以在函数体的第一行使用一对三个单引号 ''' 或者一对三个双引号 """ 来定义文档字符串。

    你可以使用 doc(注意双下划线)调用函数中的文档字符串属性。(https://www.runoob.com/w3cnote/python-docstrings.html

 

了解类型注解吗?

 

python命名规范?更多规范可以参考(https://www.cnblogs.com/kilometerwine/p/9650154.html

  • 由数字字母下划线组成

  • 不能以数字开头,最好别以下划线开头(因为有可能和关键字重复)

 

python代码规范工具:

 

简述 any()和 all()方法

  • any():只要迭代器中有一个元素为真就为真

  • all():迭代器中所有的判断项返回都是真,结果才为真

 

当退出 Python 时是否释放所有内存分配?

 

pathlib 的用法举例

 

dir()是干什么用的?

  • dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法dir(),该方法将被调用。如果参数不包含dir(),该方法将最大限度地收集参数信息。(https://www.runoob.com/python/python-func-dir.html

 

断言什么时候用?

  • 防御性的编程

  • 运行时对程序逻辑的检测

  • 合约性检查(比如前置条件,后置条件)

  • 程序中的常量

  • 检查文档

 

私有化

  • xx: 公有变量

  • _x: 单前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问

  • __xx:双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到)

  • xx:双前后下划线,用户名字空间的魔法对象或属性。例如:__init__ , __ 不要自己发明这样的名字

  • xx_:单后置下划线,用于避免与Python关键词的冲突

  • 通过name mangling(名字重整(目的就是以防子类意外重写基类的方法或者属性)如:_Class__object)机制就可以访问private了

 

怎样一次声明多个变量并赋值

#给多个变量赋值
data=['alex',84,[1900,3,38]]
name,age,birth=data
print(name)
print(age)
print(birth)
==========
msg='hello'
a,b,c,d,e=msg
print(a,b,c,d,e)
==========
msg='hello'
a,_,_,_,b=msg
print(a)
print(b)
#只给第一个和最后一个变量赋值
a,*_,b=msg
print(a,b)

 

sorted和sort区别?

  • sorted对所有可迭代对象排序,sort知识list特有方法

  • 前者会返回新列表,后者对原来列表操作

 

python中逻辑运算符,位运算符,赋值运算符?

 

python中使用多进制数字?

 

定义函数参数顺序?

  • 位置参数,可变参数,默认参数,关键词参数

 

匿名函数?

  • 定义 lambda 形参:返回值

  • 定义并调用:(lambda 形参:返回值)(实参)

  • 应用场景:比如用于map,reduce函数中

 

python函数怎么传参?

  • 共享传参(看起来是可变参数引用传递,不可变参数值传递):实际上是共享传参,就是函数形参获得实参各个副本的引用

  • 默认参数为可变参数时,只计算一次

 

函数递归调用停止条件?

  • 默认为1000,可以调整

  • 或者设置递归条件停止

 

map,reduce,filter函数

 

functools模块

 

函数装饰器有什么作用?请列举说明?

可以无侵入式的为程序添加功能

import time


def time_fun(fun):
    def wrapper():
        start = time.time()
        fun()
        end = time.time()
        take = end - start
        return take

    return wrapper


@time_fun
def fun1():
    for i in range(10000000):
        pass

 

魔法方法

 

metaclass 作用?以及应用场景?

python中的组合和继承

  • 组合是通过对现有对象进行拼装即组合产生新的具有更复杂的功能,通俗理解为把一个类的实例放到另一个类叫组合,一般优先使用组合,组合不关心局部类的实现,只关心局部类有没有什么功能或者属性,拿来用,属于黑盒式复用
  • 继承属于白盒式复用,优点在于可以很方便的修改被复用的代码
#组合
class
A: def __init__(self, a, b): self.a = a self.b = b class B(A): def __init__(self, a, b): self.c = A(a, b) b1 = B(1, 2) print(b1.c) == > A的对象

 

多继承及MRO(非重点)

  1. super().init相对于类名.init,在单继承上用法基本无差

  2. 但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,具体看前面的输出结果

  3. 多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错

  4. 单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错

  5. 多继承时,相对于使用类名.init方法,要把每个父类全部写一遍, 而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因

 

property

class Goods:
    @property
    def price(self):
        print('@property')

    @price.setter
    def price(self, value):
        print('@price.setter')

    @price.deleter
    def price(self):
        print('@price.deleter')

obj = Goods()
obj.price          # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
obj.price = 123    # 自动执行 @price.setter 修饰的 price 方法,并将  123 赋值给方法的参数
del obj.price      # 自动执行 @price.deleter 修饰的 price 方法

 

鸭子类型或者多态

  • 同一父类同一属性或者方法的表现不同,比如黑人白人都是人,都有皮肤,但是皮肤颜色不同,他们都会唱歌,但可能黑人唱歌天赋好一点

 

pymysql的基本使用

from pymysql import *


def main():
    find_name = input("请输入物品名称:")

    # 创建Connection连接
    conn = connect(host='localhost', port=3306, user='root', password='mysql', database='jing_dong', charset='utf8')
    # 获得Cursor对象
    cs1 = conn.cursor()

    # 非安全的方式
    # 输入 " or 1=1 or "   (双引号也要输入)
    # sql = 'select * from goods where name="" or 1=1 or ""' % find_name

    # 安全的方式,构造参数列表
    params = [find_name]
    count = cs1.execute('select * from goods where name=%s', params)
    # 注意:
    # 如果要是有多个参数,需要进行参数化
    # 那么params = [数值1, 数值2....],此时sql语句中有多个%s即可

    # 打印受影响的行数
    print(count)
    # 获取查询的结果
    # result = cs1.fetchone()
    result = cs1.fetchall()
    # 打印查询的结果
    print(result)
    # 关闭Cursor对象
    cs1.close()
    # 关闭Connection对象
    conn.close()


if __name__ == '__main__':
    main()

 

pymongo的基本使用

# 导入pymongo并选择要操作的集合 数据库和集合乜有会自动创建
from pymongo import MongoClient

client = MongoClient(host, port)
collection = client[db名][集合名]

添加一条数据
ret = collection.insert_one({"name": "test10010", "age": 33})

添加多条数据
t = collection.insert_many(item_list)

查找一条数据
t = collection.find_one({"name": "test10005"})

查找全部数据

结果是一个Cursor游标对象,是一个可迭代对象,可以类似读文件的指针,但是只能够进行一次读取
# find返回所有满足条件的结果,如果条件为空,则返回数据库的所有
t = collection.find({"name": "test10005"})
# 结果是一个Cursor游标对象,是一个可迭代对象,可以类似读文件的指针,
for i in t:
    print(i)
for i in t:  # 此时t中没有内容
    print(i)

 

redis的基本使用

from redis import *

sr = StrictRedis(host='localhost', port=6379, db=0)
# 简写
# sr=StrictRedis()
# 添加键name,值为ycg
result = sr.set('name', 'ycg')
# 输出响应结果,如果添加成功则返回True,否则返回False
print(result)

 

zip函数的用法

  • zip()函数在运算时,会以一个或多个序列(可迭代对象)做为参数,返回一个元组的列表。同时将这些序列中并排的元素配对。

 

format函数用法

 

python中except用法?

#第一种
try:
    xxx
except:
    xxx
​
#第二种
try:
    xxx
except xxError:
    xxx
​
#第三种
try:
    xxx
except xxError1,xxError2,xxError3:
    xxx
​
#第四种
try:
    xxx
except xxError,args(参数):
    xxx(可以用到args)
​
#第五种
try:
    xxx
except xxError:
    xxx
except xxError:
    xxx
except xxError:
    xxx
#完整格式是
try:             
    可能引发异常现象的代码          
except:
    出现异常现象的处理代码
else:
    未出现异常现象的处理代码
finally:
    try代码块结束后运行的代码(一定执行)

 

random

  • random.random():生成一个 0-1 之间的随机浮点数;

  • random.uniform(a, b):生成[a,b]之间的浮点数;

  • random.randint(a, b):生成[a,b]之间的整数;

  • random.randrange(a, b, step):比如(1,10,2)就是从1,3,5,7,9取随机;

  • random.choice(sequence):从特定序列中随机取一个元素,这里的序列可以是字符串,列表, 元组等 

  • random.shuffle(序列):打乱一个序列

 

time模块:

 

datetime模块:

 

python logging模块详解:

 

python中metaclass使用举例

 

猴子补丁

  • 即在运行时对方法 / 类 / 属性 / 功能进行修改,把新的代码作为解决方案代替原有的程序,也就是为其打上补丁。

  • 使用猴子补丁的方式,gevent能够修改标准库里面大部分的阻塞式系统调用,包括socket、ssl、threading和 select等模块,而变为协作式运行。也就是通过猴子补丁的monkey.patch_xxx()来将python标准库中模块或函数改成gevent中的响应的具有协程的协作式对象。这样在不改变原有代码的情况下,将应用的阻塞式方法,变成协程式的。

 

python中常见异常

 

python中的接口如何实现

  • 接口只是定义了一些方法,而没有去实现,多用于程序设计时,只是设计需要有什么样的功能,但是并没有实现任何功能,这些功能需要被另一个类(B)继承后,由 类B去实现其中的某个功能或全部功能。

    在python中接口由抽象类和抽象方法去实现,接口是不能被实例化的,只能被别的类继承去实现相应的功能。

    个人觉得接口在python中并没有那么重要,因为如果要继承接口,需要把其中的每个方法全部实现,否则会报编译错误,还不如直接定义一个class,其中的方法实现全部为pass,让子类重写这些函数。

    当然如果有强制要求,必须所有的实现类都必须按照接口中的定义写的话,就必须要用接口。更多参考:https://blog.csdn.net/kobeyan/article/details/44344087

     

python中的反射(又叫自省)

  • 反射,reflection,指的是运行时获取类型定义信息。 一个对象能够在运行时,像照镜子一样,反射出其类型信息。简单说,在Python中,能够通过一个对象,找出其type、class、attribute或method的能力,称为反射或自省。 具有反射能力的函数有type(),isinstance(),callable(),dir(),getattr()等

     

怎么理解restful风格

  • restful风格是一种接口开发的风格,中文翻译就是表现层状态转换,表现层要表现的就是资源,比如html,xml,json,http请求中主要体现在accept和content-type两个请求头字段,状态转换就是客户端和服务端交互的过程中数据和状态肯定会发生改变

  • 总结就是,每一个url代表一种资源;客户端和服务端之间,传递着这种资源的表现层;客户端通过http四个动词对服务端资源操作,来实现资源的状态转换 

 

pycharm中的Mark as root 和sys.path.append(xxx)效果一样,都是讲非python包(就是没有init文件)加入python解释器搜寻路径

 

str和repr

 

python几种数据结构底层实现:

 

python有了GIL为什么还要线程锁

  • GIL是限制同一个进程中只有一个线程进入Python解释器。而线程锁是由于在线程进行数据操作时保证数据操作的安全性(同一个进程中线程之间可以共用信息,如果同时对数据进版行操作,则会出现公共数据错误)其实线程锁完全可以替代GIL,但是Python的后续功能模块都是加在GIL基础上的,所以无法更改或去掉GIL,这就是Python语言最大的bug…只能用多进程或协程改善,或者直接用其他语言写这部分

 

python单例应用场景:

 

如何判断一个对象是函数还是方法

  • types模块里面有Functiontype和Methodtype,用instance判断

 

python常用模块总结:

 

python操作excel:

 

pyechart在django中显示:

 

python中的nolocal:

  • nolocal用在闭包中,提升外部函数变量,使闭包函数可以修改外部函数中的变量,当知识读取变量,或者修改外部可变数据类型变量的时候nolocal可以省略 

 

python pickle cpickle:

 

python内存管理:

 

python闭包及陷阱:

 

单例及其实际应用场景:

 

sys.path维护的是python解释器的导包路径列表,加入其中的路径,都可以直接直接引用(就好像全局变量一样)

 

python Merry(异常处理模块,业务逻辑和异常处理分离):

 

argparse模块的使用:

 

python中的exec()、eval()以及complie():

 

re.compile:

 

占位符详解:

 

format详解:

 

python实用编程技巧:

 

python程序提高性能的20 种方法:

 

python获取命令行输出结果:

 

python中 r'', b'', u'', f'' 的含义:

 

python定义函数时,指定要传入参数的数据类型:

 

str(),repr(),eval():

 

python基础大整合:

 

Unicode与utf-8:

 

匹配中文的正则:

 

程序中出现中文导致, SyntaxError: Non-UTF-8 code starting with '\xc1',在第一行加上#coding=gbk,详情可见:

 

python3 Unicode编码字符串 转成汉字(\u 开头 ): 

a = "\u5317\u4eac\u5e02"
print(a.encode().decode()) 

 

pip的使用

 

python列表推导式的两种形式

a = [i if i % 2 is 0 else 0 for i in range(5)]#if写在前面一定要加else,可以理解为没有加else的话,相当于没有拿着一个变量去列表推导式找值
b = [j for j in range(5) if j % 2 is 0]

 

python进制转换

# 其他进制转换为十进制
# 二进制转换为十进制
a = int('10', 2) ==>2
# 八进制转换为十进制
b = int('10', 8) ==>8

# 十进制转换为其他进制
c = bin(10) ==>0b1010
d = oct(10) ==>0o12
e = hex(10) ==>0xa

 

json序列化时,可以处理的数据类型有哪些?如何定制支持datetime类型

复制代码
自定义时间序列化转换器
import json
from json import JSONEncoder
from datetime import datetime
class ComplexEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        else:
            return super(ComplexEncoder,self).default(obj)
d = { 'name':'alex','data':datetime.now()}
print(json.dumps(d,cls=ComplexEncoder))
# {"name": "alex", "data": "2018-05-18 19:52:05"}
复制代码

 

json序列化时遇到中文会默认转换成unicode  ,如何让他保留中文形式

import json
a=json.dumps({"ddf":"你好"},ensure_ascii=False)
print(a) #{"ddf": "你好"}

 asyncio的基本使用

 

yield和yield from的区别:

def fun1(x):
    yield x


def fun2(x):
    yield from x


a = [1, 2, 3, 4, 5]
for i in fun1(a):
    print(i)  # 结果:[1, 2, 3, 4, 5]

for j in fun2(a):
    print(j)  # 结果:1,2,3,4,5

# 区别:yield和yield from后边加上可迭代对象的时候,yield from是将可迭代对象中的元素一个一个yield出来,而yield是直接yield的是可迭代对象

 

functools.partial

functools模块用于高阶函数:作用于或返回其他函数的函数。一般而言,任何可调用对象都可以作为本模块用途的函数来处理。

functools.partial返回的是一个可调用的partial对象,使用方法是partial(func,*args,**kw),func是必须要传入的,而且至少需要一个args或是kw参数。

from functools import partial


def add(a, b, c):
    return a + b + c


p = partial(add, 12)
x = p(2, 3)
print(x)  # 17

 

asyncio基本使用

python 装饰器

#最基本的装饰器
def
hint(func): def wrapper(*args, **kwargs): print('{} is running'.format(func.__name__)) return func(*args, **kwargs) return wrapper @hint def hello(): print("Hello!") >>> hello.__name__ 'wrapper' ##函数名字已改变 #想要函数名不变: from functools import wraps def hint(func): @wraps(func) def wrapper(*args, **kwargs): print('{} is running'.format(func.__name__)) return func(*args, **kwargs) return wrapper @hint def hello(): print("Hello!") #装饰器带参数 from functools import wraps def hint(coder): def wrapper(func): @wraps(func) def inner_wrapper(*args, **kwargs): print('{} is running'.format(func.__name__)) print('Coder: {}'.format(coder)) return func(*args, **kwargs) return inner_wrapper return wrapper @hint(coder="John") def hello(): print("Hello!")

 更多装饰器可见

 

python什么时候用类方法,什么时候用静态方法,什么时候用实例方法

  • 静态方法就是没有用到类里面的属性和方法的时候,可以用静态方法,主要是为了保证类的完备性
  • 类方法是定义在类中,不属于某个具体对象的行为,被所有对象共同使用的行为,类方法里面不能使用实例变量和方法

 

python元类

  

python sort和sorted

  • python sort 和python sorted的区别?
    • python sort是在原对象上排序,而sorted会生成新的排序对象
  • sorted和的实现原理都是基于timesort
    • Timsort是结合了合并排序(merge sort)和插入排序(insertion sort)而得出的排序算法排序的输入的单位不是一个个单独的数字,而是一个个的块-分区。其中每一个分区叫一个run。针对这些 run 序列,每次拿一个 run 出来按规则进行合并。每次合并会将两个 run合并成一个 run。合并的结果保存到栈中。合并直到消耗掉所有的 run,这时将栈上剩余的 run合并到只剩一个 run 为止。这时这个仅剩的 run 便是排好序的结果。

    • 综上述过程,Timsort算法的过程包括

      (0)如何数组长度小于某个值,直接用二分插入排序算法

      (1)找到最小run;优化run的大小;通过归并排序合并run

    • 更多可见:https://blog.csdn.net/yangzhongblog/article/details/8184707
  • sort的实现原理
    • python内部的sort采用的是混合(hybrid)排序,规模小的时候采用 binary insertion,规模大的时候采用 sample sort 

 

python互斥锁(lock),可重入锁(rlock,同一线程可以多次获取锁),死锁:

  • 常规锁和Python中的Rlock之间的一个区别是,常规锁可以由不同的线程释放,而重入锁必须由获取它的同一个线程释放,同时要求解锁次数应与加锁次数相同,才能用于另一个线程。
  • 产生死锁的条件:“迭代调用”死锁,互相调用死锁,更多可见:https://www.jb51.net/article/74426.htm

进程和线程之间的通信方式

进程

  • 管道(匿名管道):半双工的通信方式,数据只能单向流动,且只能在有亲缘关系(父子进程或兄弟进程)的进程间使用;
  • 命名管道:FIFO,半双工的通信方式,但允许在无亲缘关系的进程间通信;
  • 消息队列:消息的链表,存放在内核中,并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点;
  • 共享内存:映射一段能被其他进程访问的内存,这段内存由一个进程创建,但多个进程都可以访问;
  • 信号:信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生.
  • 信号量:是一个计数器,用于控制多个进程间对共享资源的访问;
  • socket

线程

  • 锁机制:包括互斥锁、条件变量、读写锁

    • 互斥锁提供了以排他方式防止数据结构被并发修改的方法。
      读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
      条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。  
  • 信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
  • 信号机制(Signal):类似进程间的信号处理

线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。

__getattr____getattribute__魔法函数

属性描述符和属性查找过程

import numbers
​
class IntField:
    #数据描述符
    def __get__(self, instance, owner):
        return self.value
    def __set__(self, instance, value):
        if not isinstance(value, numbers.Integral):
            raise ValueError("int value need")
        if value < 0:
            raise ValueError("positive value need")
        self.value = value
    def __delete__(self, instance):
        pass
​
​
class NonDataIntField:
    #非数据属性描述符
    def __get__(self, instance, owner):
        return self.value
​
class User:
    age = IntField()
    # age = NonDataIntField()
'''
如果user是某个类的实例,那么user.age(以及等价的getattr(user,’age’))
首先调用__getattribute__。如果类定义了__getattr__方法,
那么在__getattribute__抛出 AttributeError 的时候就会调用到__getattr__,
而对于描述符(__get__)的调用,则是发生在__getattribute__内部的。
user = User(), 那么user.age 顺序如下:
​
(1)如果“age”是出现在User或其基类的__dict__中, 且age是data descriptor, 那么调用其__get__方法, 否则
​
(2)如果“age”出现在user的__dict__中, 那么直接返回 obj.__dict__[‘age’], 否则
​
(3)如果“age”出现在User或其基类的__dict__中
​
(3.1)如果age是non-data descriptor,那么调用其__get__方法, 否则
​
(3.2)返回 __dict__[‘age’]
​
(4)如果User有__getattr__方法,调用__getattr__方法,否则
​
(5)抛出AttributeError
​
'''if __name__ == "__main__":
    user = User()
    user.__dict__["age"] = "abc"
    print (user.__dict__)
    print (user.age) 

元类编程

基本用法

class MetaClass(type):
    def __new__(cls, *args, **kwargs):
        return super().__new__(cls, *args, **kwargs)
​
​
class User(metaclass=MetaClass):
    def __init__(self, name):
        self.name = name
​
    def __str__(self):
        return "user"
​
​
if __name__ == "__main__":
    # User = type("User":类名, (BaseClass, ):父类, {"name":"user", "say":say}:属性)
    my_obj = User(name="bobby")
    print(my_obj)

实现orm

# 需求
import numbers
​
​
class Field:
    pass
​
​
class IntField(Field):
    # 数据描述符
    def __init__(self, db_column, min_value=None, max_value=None):
        self._value = None
        self.min_value = min_value
        self.max_value = max_value
        self.db_column = db_column
        if min_value is not None:
            if not isinstance(min_value, numbers.Integral):
                raise ValueError("min_value must be int")
            elif min_value < 0:
                raise ValueError("min_value must be positive int")
        if max_value is not None:
            if not isinstance(max_value, numbers.Integral):
                raise ValueError("max_value must be int")
            elif max_value < 0:
                raise ValueError("max_value must be positive int")
        if min_value is not None and max_value is not None:
            if min_value > max_value:
                raise ValueError("min_value must be smaller than max_value")
​
    def __get__(self, instance, owner):
        return self._value
​
    def __set__(self, instance, value):
        if not isinstance(value, numbers.Integral):
            raise ValueError("int value need")
        if value < self.min_value or value > self.max_value:
            raise ValueError("value must between min_value and max_value")
        self._value = value
​
​
class CharField(Field):
    def __init__(self, db_column, max_length=None):
        self._value = None
        self.db_column = db_column
        if max_length is None:
            raise ValueError("you must spcify max_lenth for charfiled")
        self.max_length = max_length
​
    def __get__(self, instance, owner):
        return self._value
​
    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise ValueError("string value need")
        if len(value) > self.max_length:
            raise ValueError("value len excess len of max_length")
        self._value = value
​
​
class ModelMetaClass(type):
    def __new__(cls, name, bases, attrs, **kwargs):
        if name == "BaseModel":
            return super().__new__(cls, name, bases, attrs, **kwargs)
        fields = {}
        for key, value in attrs.items():
            if isinstance(value, Field):
                fields[key] = value
        attrs_meta = attrs.get("Meta", None)
        _meta = {}
        db_table = name.lower()
        if attrs_meta is not None:
            table = getattr(attrs_meta, "db_table", None)
            if table is not None:
                db_table = table
        _meta["db_table"] = db_table
        attrs["_meta"] = _meta
        attrs["fields"] = fields
        del attrs["Meta"]
        return super().__new__(cls, name, bases, attrs, **kwargs)
​
​
class BaseModel(metaclass=ModelMetaClass):
    def __init__(self, *args, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
        super().__init__()
​
    def save(self):
        fields = []
        values = []
        for key, value in self.fields.items():
            db_column = value.db_column
            if db_column is None:
                db_column = key.lower()
            fields.append(db_column)
            value = getattr(self, key)
            values.append(str(value))
​
        sql = "insert {db_table}({fields}) value({values})".format(db_table=self._meta["db_table"],
                                                                   fields=",".join(fields), values=",".join(values))
        pass
​
​
class User(BaseModel):
    name = CharField(db_column="name", max_length=10)
    age = IntField(db_column="age", min_value=1, max_value=100)
​
    class Meta:
        db_table = "user"
​
​
if __name__ == "__main__":
    user = User(name="bobby", age=28)
    # user.name = "bobby"
    # user.age = 28
    user.save()

 

线程

线程之前的通信方式:共享变量和queue

# 通过queue的方式进行线程间同步
from queue import Queue
​
import time
import threading
​
​
def get_detail_html(queue):
    # 爬取文章详情页
    while True:
        url = queue.get()
        # for url in detail_url_list:
        print("get detail html started")
        time.sleep(2)
        print("get detail html end")
​
​
def get_detail_url(queue):
    # 爬取文章列表页
    while True:
        print("get detail url started")
        time.sleep(4)
        for i in range(20):
            queue.put("http://projectsedu.com/{id}".format(id=i))
        print("get detail url end")
​
​
# 1. 线程通信方式- 共享变量
if __name__ == "__main__":
    detail_url_queue = Queue(maxsize=1000)
​
    thread_detail_url = threading.Thread(target=get_detail_url, args=(detail_url_queue,))
    for i in range(10):
        html_thread = threading.Thread(target=get_detail_html, args=(detail_url_queue,))
        html_thread.start()
    start_time = time.time()
    detail_url_queue.task_done()
    detail_url_queue.join()
​
    # 当主线程退出的时候, 子线程kill掉
    print("last time: {}".format(time.time() - start_time))

线程间的同步方式

加锁:互斥锁和可重入锁

import time
import threading
​
detail_url_list = []
​
​
def get_detail_html(lock):
    # 爬取文章详情页
    while True:
​
        if len(detail_url_list):
            lock.acquire()
            if len(detail_url_list):
                detail_url_list.pop()
                lock.release()
                # for url in detail_url_list:
                print("get detail html started")
                time.sleep(2)
                print("get detail html end")
            else:
                lock.release()
                time.sleep(1)
​
​
def get_detail_url(lock):
    # 爬取文章列表页
    while True:
        print("get detail url started")
        time.sleep(4)
        for i in range(20):
            lock.acquire()
            if len(detail_url_list) >= 10:
                lock.release()
                time.sleep(1)
            else:
                detail_url_list.append("http://projectsedu.com/{id}".format(id=i))
                lock.release()
        print("get detail url end")
​
​
if __name__ == "__main__":
    lock = threading.RLock()
    thread_detail_url = threading.Thread(target=get_detail_url, args=(lock,))
    for i in range(10):
        html_thread = threading.Thread(target=get_detail_html, args=(lock,))
        html_thread.start()
    start_time = time.time()
    print("last time: {}".format(time.time() - start_time))
​
​
# Lock和Rlock的区别:
# 常规锁和Python中的Rlock之间的一个区别是,常规锁可以由不同的线程释放,而重入锁必须由获取它的同一个线程释放,同时要求解锁次数应与加锁次数相同,才能用于另一个线程。
# 产生死锁的条件:“迭代调用”死锁,互相调用死锁,更多可见:https://www.jb51.net/article/74426.htm

条件变量condition

import threading
​
​
# 条件变量, 用于复杂的线程间同步
class XiaoAi(threading.Thread):
    def __init__(self, cond):
        super().__init__(name="小爱")
        self.cond = cond
​
    def run(self):
        with self.cond:
            self.cond.wait()
            print("{} : 在 ".format(self.name))
            self.cond.notify()
​
            self.cond.wait()
            print("{} : 好啊 ".format(self.name))
            self.cond.notify()
​
            self.cond.wait()
            print("{} : 君住长江尾 ".format(self.name))
            self.cond.notify()
​
            self.cond.wait()
            print("{} : 共饮长江水 ".format(self.name))
            self.cond.notify()
​
            self.cond.wait()
            print("{} : 此恨何时已 ".format(self.name))
            self.cond.notify()
​
            self.cond.wait()
            print("{} : 定不负相思意 ".format(self.name))
            self.cond.notify()
​
​
class TianMao(threading.Thread):
    def __init__(self, cond):
        super().__init__(name="天猫精灵")
        self.cond = cond
​
    def run(self):
        with self.cond:
            print("{} : 小爱同学 ".format(self.name))
            self.cond.notify()
            self.cond.wait()
​
            print("{} : 我们来对古诗吧 ".format(self.name))
            self.cond.notify()
            self.cond.wait()
​
            print("{} : 我住长江头 ".format(self.name))
            self.cond.notify()
            self.cond.wait()
​
            print("{} : 日日思君不见君 ".format(self.name))
            self.cond.notify()
            self.cond.wait()
​
            print("{} : 此水几时休 ".format(self.name))
            self.cond.notify()
            self.cond.wait()
​
            print("{} : 只愿君心似我心 ".format(self.name))
            self.cond.notify()
            self.cond.wait()
​
​
if __name__ == "__main__":
    cond = threading.Condition()
    xiaoai = XiaoAi(cond)
    tianmao = TianMao(cond)
​
    # 启动顺序很重要
    # 在调用with cond之后才能调用wait或者notify方法
    # condition有两层锁, 一把底层锁会在线程调用了wait方法的时候释放, 上面的锁会在每次调用wait的时候分配一把并放入到cond的等待队列中,等到notify方法的唤醒
    xiaoai.start()
    tianmao.start()
    
# 结果:
# 天猫精灵 : 小爱同学 
# 小爱 : 在 
# 天猫精灵 : 我们来对古诗吧 
# 小爱 : 好啊 
# 天猫精灵 : 我住长江头 
# 小爱 : 君住长江尾 
# 天猫精灵 : 日日思君不见君 
# 小爱 : 共饮长江水 
# 天猫精灵 : 此水几时休 
# 小爱 : 此恨何时已 
# 天猫精灵 : 只愿君心似我心 
# 小爱 : 定不负相思意 

Semaphore 是用于控制进入数量的锁

# Semaphore 是用于控制进入数量的锁
# 文件, 读、写, 写一般只是用于一个线程写,读可以允许有多个
import threading
import time
​
​
class HtmlSpider(threading.Thread):
    def __init__(self, url, sem):
        super().__init__()
        self.url = url
        self.sem = sem
​
    def run(self):
        time.sleep(2)
        print("got html text success")
        self.sem.release()
​
​
class UrlProducer(threading.Thread):
    def __init__(self, sem):
        super().__init__()
        self.sem = sem
​
    def run(self):
        for i in range(20):
            self.sem.acquire()
            html_thread = HtmlSpider("https://baidu.com/{}".format(i), self.sem)
            html_thread.start()
​
​
if __name__ == "__main__":
    sem = threading.Semaphore(3)
    url_producer = UrlProducer(sem)
    url_producer.start()

线程池编程

from concurrent.futures import ThreadPoolExecutor, as_completed, wait, FIRST_COMPLETED
from concurrent.futures import Future
from multiprocessing import Pool
​
# 未来对象,task的返回容器
​
​
# 线程池, 为什么要线程池
# 主线程中可以获取某一个线程的状态或者某一个任务的状态,以及返回值
# 当一个线程完成的时候我们主线程能立即知道
# futures可以让多线程和多进程编码接口一致
import time
​
​
def get_html(times):
    time.sleep(times)
    print("get page {} success".format(times))
    return times
​
​
executor = ThreadPoolExecutor(max_workers=2)
# 通过submit函数提交执行的函数到线程池中, submit 是立即返回
# task1 = executor.submit(get_html, (3))
# task2 = executor.submit(get_html, (2))
​
​
# 要获取已经成功的task的返回
urls = [3, 2, 4]
all_task = [executor.submit(get_html, (url,)) for url in urls]
wait(all_task, return_when=FIRST_COMPLETED)
print("main")
# for future in as_completed(all_task):
#     data = future.result()
#     print("get {} page".format(data))
# 通过executor的map获取已经完成的task的值
# for data in executor.map(get_html, urls):
#     print("get {} page".format(data))
​
​
# #done方法用于判定某个任务是否完成
# print(task1.done())
# print(task2.cancel())
# time.sleep(3)
# print(task1.done())
#
# #result方法可以获取task的执行结果
# print(task1.result())

进程

基本使用

# import os
# #fork只能用于linux/unix中
# pid = os.fork()
# print("bobby")
# if pid == 0:
#   print('子进程 {} ,父进程是: {}.' .format(os.getpid(), os.getppid()))
# else:
#   print('我是父进程:{}.'.format(pid))
​
​
import multiprocessing
​
# 多进程编程
import time
​
​
def get_html(n):
    time.sleep(n)
    print("sub_progress success")
    return n
​
​
if __name__ == "__main__":
    # progress = multiprocessing.Process(target=get_html, args=(2,))
    # print(progress.pid)
    # progress.start()
    # print(progress.pid)
    # progress.join()
    # print("main progress end")
# 使用线程池
    pool = multiprocessing.Pool(multiprocessing.cpu_count())
    # result = pool.apply_async(get_html, args=(3,))
    #
    # #等待所有任务完成
    # pool.close()
    # pool.join()
    #
    # print(result.get())
# imap
    # for result in pool.imap(get_html, [1,5,3]):
    #     print("{} sleep success".format(result))
for result in pool.imap_unordered(get_html, [1, 5, 3]):
        print("{} sleep success".format(result))

进程之间的通信方式

import time
from multiprocessing import Process, Queue, Pool, Manager, Pipe
​
​
# def producer(queue):
#     queue.put("a")
#     time.sleep(2)
#
# def consumer(queue):
#     time.sleep(2)
#     data = queue.get()
#     print(data)
#
# if __name__ == "__main__":
#     queue = Queue(10)
#     my_producer = Process(target=producer, args=(queue,))
#     my_consumer = Process(target=consumer, args=(queue,))
#     my_producer.start()
#     my_consumer.start()
#     my_producer.join()
#     my_consumer.join()
# 共享全局变量通信
# 共享全局变量不能适用于多进程编程,可以适用于多线程
​
​
# def producer(a):
#     a += 100
#     time.sleep(2)
#
# def consumer(a):
#     time.sleep(2)
#     print(a)
#
# if __name__ == "__main__":
#     a = 1
#     my_producer = Process(target=producer, args=(a,))
#     my_consumer = Process(target=consumer, args=(a,))
#     my_producer.start()
#     my_consumer.start()
#     my_producer.join()
#     my_consumer.join()
# multiprocessing中的queue不能用于pool进程池
# pool中的进程间通信需要使用manager中的queue
# def producer(queue):
#     queue.put("a")
#     time.sleep(2)
#
# def consumer(queue):
#     time.sleep(2)
#     data = queue.get()
#     print(data)
#
# if __name__ == "__main__":
#     queue = Manager().Queue(10)
#     pool = Pool(2)
#
#     pool.apply_async(producer, args=(queue,))
#     pool.apply_async(consumer, args=(queue,))
#
#     pool.close()
#     pool.join()
# 通过pipe实现进程间通信
# pipe的性能高于queue
# def producer(pipe):
#     pipe.send("bobby")
#
# def consumer(pipe):
#     print(pipe.recv())
#
# if __name__ == "__main__":
#     recevie_pipe, send_pipe = Pipe()
#     #pipe只能适用于两个进程
#     my_producer= Process(target=producer, args=(send_pipe, ))
#     my_consumer = Process(target=consumer, args=(recevie_pipe,))
#
#     my_producer.start()
#     my_consumer.start()
#     my_producer.join()
#     my_consumer.join()
def add_data(p_dict, key, value):
    p_dict[key] = value
​
​
if __name__ == "__main__":
    progress_dict = Manager().dict()
    from queue import PriorityQueue
​
    first_progress = Process(target=add_data, args=(progress_dict, "bobby1", 22))
    second_progress = Process(target=add_data, args=(progress_dict, "bobby2", 23))
​
    first_progress.start()
    second_progress.start()
    first_progress.join()
    second_progress.join()
​
    print(progress_dict)
​

 

 

 

 

posted @ 2020-03-31 23:14  鱼虫光  阅读(328)  评论(0编辑  收藏  举报