- python可以做三种推导式:列表,字典,集合,还可以做生成器
a = {i:i*3 for i in range(10)}
print(a)
b = {i:j*3 for i,j in zip(range(10), range(10,20)) if i>5}
- zip返回值是一迭代器,有别于可迭代对象,并以最短作为迭代的次数
- 集合是无序的,用for进行遍历取出来也不是固定顺序的
for i in {3,4,6,7,8,43,24,242,3453}:
print(i)
- 可迭代对象可以做成迭代器等着被迭代
- 驼峰是两边小写,python的类要首字母大写,推荐用_
- *d 和**d的用法
- 字符串可以和数字相乘
- import 时带下划线的私有属性不会被直接导入进来,需要单独导入,__all__也是跟导入时有关
- 类属性需要类名+属性进行调用
- *_的用法
- 在ipython中可以用? ??进行函数的查看
- 列表可以相乘,可以扩充列表
- python timeit的用法
- \用来转义 还有windows下的路径
赋值和引用
迭代器和生成器
def foo():
for i in range(10):
yield i
res = foo()
print(res)
print(next(res))
- 这里的生成器函数不会马上执行,会在调用next的时候将yield里面的值传递给接受的变量x
def foo():
print(111)
yield 222
print(333)
yield 444
print(555)
res = foo()
x = next(res)
print(x)
x = next(res)
print(x)
next(res)
- 生成器为什么可以被迭代,被调用?
- 生成器
- 可迭代对象:str list tuple dict set bytes 内部都实现了__iter__
class My:
def __iter__(self):
return self
def __next__(self):
return 1
for i in My():
print(i)
写了一个return1的死循环,因为没有退出条件
class My:
def __init__(self):
self.count = 1
def __iter__(self):
return self
def __next__(self):
if self.count < 6:
self.count += 1
return 1
else:
raise StopIteration()
for i in My():
print(i)
My()的对象是一个迭代器,实现了next iter方法
生成器本身就是迭代器,是一种特殊的迭代器
自定义xrange
- 优点:节省内存,不是一次性创建,惰性求值 思想来源于lisp语言
lambda表达式,迭代器,生成器,装饰器,各种生成器
- 自定义时要注意没有默认参数的要放在前面
- https://blog.csdn.net/weixin_40320794/article/details/79367243 否则会报错
class Range:
def __init__(self, end,start=0, step=1):
self.start = start
self.end = end
self.step = step
def __iter__(self):
return self
def __next__(self):
if self.start < self.end:
self.start += self.step
return self.start
else:
raise StopIteration
for i in Range(10):
print(i)
- 查看内存占用 sizeof()函数,单位字节
class Range:
def __init__(self, end,start=0, step=1):
self.start = start
self.end = end
self.step = step
def __iter__(self):
return self
def __next__(self):
if self.start < self.end:
self.start += self.step
return self.start
else:
raise StopIteration
a = Range(10)
print(a.__sizeof__())
- 生成器调用__iter__返回一个生成器
- 自定义的迭代器调用返回的就是自己的这个对象
- 可迭代对象调用__iter__返回的是一个迭代器
- itertools
- 指针指向内存地址
- 如果有一个字段想存储任意类型的数据,就可以将这个字段的类型设置为空接口
- abs返回数字的绝对值
a = 1
b = 2
print(id(a))
print(id(1))
print(id(b))
print(id(2))
b = a
print(id(a))
print(id(1))
print(id(b))
print(id(2))
a = 2
print(id(a))
print(id(1))
print(id(b))
print(id(2))
a = a+1
b = a
print(id(a))
print(id(1))
print(id(b))
print(id(2))
# def foo():
# print(3425)
# foo()
# def foo():
# x = []
# def add(k):
# x.append(k)
# return x
# return add
# res = foo()
# a = res(3)
# a = res(5)
# print(a)
import sys
a = 1
s = 2
a = s
del s
g = sys.getrefcount(a)
print(g)
# class A:
# def __call__(self):
# print("callable")
# a = A()
# a()
import random
def check(func):
def wrap():
result = func()
if result < 0:
return 0
else:
return result
return wrap
@check
def foo():
return random.randint(-10,10)
a = foo()
print(a)
# wrap = check(foo)
# wrap()
# obj = A()![在这里插入图片描述](https://img-blog.csdnimg.cn/20191010164121396.png)
# obj()
# 要实现__call__()
- 深入理解python
魔法方法
- name
- main
- doc
- init
- del:del先执行__del__里面的东西
- call
- next
- iter:初始化
- new:分配内存,产生对象
- str:格式化打印对象,在类中定义__str__方法,return init 里面的东西
- python3中默认继承object
- python的单例模式__new__
- add
- sub
- and
- or
- 可以在类中定义__add__ 魔法方法可以实现不可运算的东西可以进行运算
- 也可以重载比较运算符(场景,游戏公司一键整理背包需要进行比较)q
- 容器方法__len__ iter
- 上下文:enter :with的代码准备 exit:处理后事
- len() 进行比较只是因为那个对象里面支持__len__ 方法
- 多继承的问题
- python的垃圾清除机制
- len(l) 相当于l.len()
- 多继承菱形继承
- 要用super方法严格按照继承的顺序调用
- python都是引用,go中都是赋值
装饰器:
# 装饰器工厂方法
def deco(n):
def wrap1(func):
def wrap2(*args, **kwargs):
return func(*args, **kwargs)
return wrap2
return wrap1
# import itertools
# '''螺旋矩阵
# [description]
# '''
# res = itertools.chain([1,2,3], [4,5,6]) # 可以进行链式迭代
# print(next(res))
'''装饰器
[description]
'''
def deco(func):
'''a'''
def wrap(*args, **kwargs):
'''b'''
func(*args, **kwargs)
return wrap
@deco
def foo(a, b):
'''c'''
return a + b
print(foo.__doc__)
# def foo():
# '''this is a doc
# [description]
# '''
# print("hello vue")
# print(foo.__name__) # 知道自己的名字
# print(foo.__doc__) # 会打印出注释的内容
import time
'''装饰器
[description]:计时功能
'''
def run_time(run):
def timer_now(x):
start = time.time()
run(x)
end = time.time()
print(end-start)
print("计时完毕")
return timer_now
class Run():
def __init__(self):
print("计时开始")
@run_time
def run(self):
print("I can run")
# time.sleep(1)
# print(5)
# time.sleep(1)
# print(4)
# time.sleep(1)
# print(3)
# time.sleep(1)
# print(2)
# time.sleep(1)
# print(1)
res = [i for i in range(100000)]
print(res)
print(res.__sizeof__())
a = Run()
a.run()
-
ipython可以执行linux的命令
-
对象保存在 dict 的里面
-
动态语言可以在运行时增加或删除一些属性
-
可以进行动态添加属性,定义完的对象是什么就是什么样子
-
类本身也是一个对象,可以动态的添加方法
-
正常在类中创建的对象只是将这个对象添加到字典里
-
类可以动态添加的方法
-
在外部对对象进行加减操作,内部的类的属性不会被修改
-
当a.z删除时,A.z 不会被删除,有个优先级的概念,当再次去a.z时,会取到790 是类属性中定义的东西
-
python中还提供了三个内建函数 setattr getattr hasattr
-
setattr可以动态的修改,增加属性
-
getattr 获取一个属性
- 字典的get方法取不到可以设置一个默认值
- getattr(a,“oo”, 7777) 也是设置一个默认值7777
- hasattr:用来检查属性的是否存在某属性
- 用来检查类是否含有什么属性
- setattr:用来添加属性的操作 getattr:动态获取的本质操作
- a.调用变量参数的时候本质就是__getattribute__
- object 大多数都是由这个对象支撑
- 在运行的是否先获取到变量
- __getattr__处理属性或方法的默认行为
- 钩子函数
- setattr getattr 钩子函数 可以监听一些东西 ,比如一些金融服务
异常处理
- 将try except放在 __main__里面永远不会抛异常了
- 程序必须有刹车,用来保护程序
- 比如一些金融业务,必须要再三处理
- 有错误一定要抛出来
- 带着错误进行运行会有更大的损失
- Exception时所有类型的父类,要匹配到什么错误就处理什么错误
- 没有报错的错误最难受,单元测试,断点调式,打日志
- 在线上的时候用log模块进行处理
- 某公司进行项目开发,规定,一句try except都不让写
- 等着报错再进行修改
- try except里面包的语句越少越好
利用dict做简单的模式匹配
- 类似于switch语句
- 用字典进行case
- 字典的匹配比较快
- hashtable结构
- fun = mapping.get(cond,do4)
- python的缓存 ,会额外的战局一些内存
- join会节约内存, 一次性申请内存,因为传的是一个列表
- 可变对象和不可变对象的区别
python性能
- unicode-》二进制:encode
- 二进制-》unicode:decode
- 用列表推导来取代map和filter
- 用生成器表达式来改写数据量较大的列表推导
- 尽量用enumerate取代range
- 了解如何在闭包里使用外围作用域中的变量
- 考虑用生成起来改成直接返回列表的函数
- 用数量可变的位置参数减少视觉杂讯 *args
- 以@classmethod形式的多态去通用地构建对象
- 用super初始化父类
- 只在使用Mix-in组件制作工具类时进行多重继承
- 继承collections.abc以实现自定义的容器类型
- 考虑用@property来代替属性重构
- 用__getattr__、__getattribute__和__setatr__实现按需生成的属性
- 用subprocess模块来管理子进程
- 考虑用concurrent.futures来实现真正的平行计算
- 用Queue来协调个线程之间的工作
- 考虑以contextlib和with语句来改写可复用的try/finally代码
- 用copyreg实现可靠的pickle操作
- 应该用datetime模块来处理本地时间,而不是time模块
- 在重视精确度的场合,应该使用decimal
- 用tracemalloc来掌握内存的使用及泄露情况
- Python中的异步程序依赖于 Coroutines(协程) ,它与event loop(事件循环)一同工作
git
- git checkout file 暂存区回滚到工作区
- git reset --hard ‘版本号’ 回滚到指定版本的工作区
- git reset --soft ‘版本号’ 将指定版本回滚到暂存区
- git reset --mix ‘版本号’ 将指定版本回滚到修改过的内容
- git log 查看日志信息: 命令(版本号) 作者 时间 版本描述
- git log --pretty=oneline 日志信息:版本号 版本描述
- git reflog 日志信息:版本号缩写(7位) HEAD(x) 命令 操作记录
rm file
git checkout --file 回滚至修改前的状态
rm file
git add file
git reset HEAD file
git checkout --file 回滚至提交到暂存区前的状态
rm file
git reset --hrad '版本号' 回滚至指定版本号
https://www.cnblogs.com/zhangyafei/p/10307907.html
异步爬虫
import asyncio
import functools
import time
from collections.abc import Iterable
import async_timeout
from pyquery import PyQuery as pq
import aiohttp
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'}
class Request(object):
"""
用于封装用户请求信息所用
"""
def __init__(self, url, callback):
self.url = url
self.callback = callback
class RequestManager(object):
def __init__(self):
self.new_requests = set()
self.old_requests = set()
def has_new_request(self):
""" 判断是否有未爬取的url """
return self.new_request_size() != 0
async def get_new_request(self):
""" 获取一个未爬取的请求 """
new_request = self.new_requests.pop()
# 提取之后,将其添加到已爬取的链接中
self.old_requests.add(new_request)
return new_request
async def add_new_request(self, request):
""" 将新请求添加到未爬取的集合中(单个请求) """
if request is None:
return
if request not in self.new_requests and request not in self.old_requests:
self.new_requests.add(request)
async def add_new_requests(self, requests):
""" 将新请求添加到未爬取的集合中(集合) """
if requests is None or len(requests) == 0:
return
for request in requests:
self.add_new_request(request)
def new_request_size(self):
""" 获取未爬取的url大小 """
return len(self.new_requests)
def old_request_size(self):
""" 获取已爬取的url大小 """
return len(self.old_requests)
class HTMLParser(object):
def __init__(self):
pass
async def parse(self, response):
# items = {'name': 'parse'}
# doc = pq(await response.text())
# print(doc)
print('parse---', response.url)
return [Request(url='http://www.sxmu.edu.cn/', callback=self.next_parse)]
# yield items
async def next_parse(self, response):
doc = pq(await response.text())
print('next parse', response.url)
items = {'next_parse': response.url}
return [items]
class DataMemory(object):
def __init__(self):
pass
async def store_data(self, data):
print('store data: ', data)
def to_csv(self):
pass
def to_excel(self):
pass
def save_mongo(self):
pass
def save_redis(self):
pass
def save_mysql(self):
pass
class Crawler(object):
def __init__(self, urls, loop=None, pool=100):
self.manager = RequestManager()
self.parser = HTMLParser()
self.data_memory = DataMemory()
self.urls = urls
self.pool = pool
self.loop = loop
def filter_downloaded_urls(self):
return self.urls
async def start(self, session, request):
await self.manager.add_new_request(request)
while self.manager.has_new_request():
# 1. 取出新请求
new_request = await self.manager.get_new_request()
# 2. 将请求的url进行下载
url = new_request.url
sem = asyncio.Semaphore(self.pool, loop=self.loop)
async with sem:
print(f'make request to {url}')
with async_timeout.timeout(60):
async with session.get(url=url, headers=headers, verify_ssl=False) as response:
if response.status == 200:
# 3. 将下载的Html文本进行解析
result = await new_request.callback(response)
# 4. 判断解析之后返回的数据对象为新请求还是解析的数据
if not isinstance(result, Iterable):
raise Exception('返回的数据类型不可迭代')
for ret in result:
if isinstance(ret, Request):
# 5. 如果是新请求,则加入到请求管理器
await self.manager.add_new_request(ret)
elif isinstance(ret, dict) and ret:
# 6. 如果是解析的数据,则将数据进行存储
await self.data_memory.store_data(ret)
else:
raise Exception(
'返回数据类型或格式不正确,只能返回Request的实例对象或字典的实例对象,且不能为空')
else:
print(f'{new_request.url}\t请求失败\t{response.status}')
async def run(self, loop):
""" 异步执行爬虫项目 """
print('*******************开始下载***********************')
self.loop = loop
conn = aiohttp.TCPConnector(ssl=False,
limit=100, # 连接池在windows下不能太大, <500
use_dns_cache=True)
url_list = self.filter_downloaded_urls()
# astnc with上下文管理器。接收的是的是协程,内部自动关闭到服务器的连接释放资源
async with aiohttp.ClientSession(connector=conn, loop=loop) as session:
# await声明程序挂起 await + 异步程序/__await__属性的对象
datas = await asyncio.gather(*[self.start(session, Request(url=url, callback=self.parser.parse)) for url in url_list])
for index, url in enumerate(url_list):
if isinstance(datas[index], Exception):
print(f"{index}, {url}: 下载失败 请重新下载:", datas[index])
def timeit(func):
"""
装饰器: 判断函数执行时间
:param func:
:return:
"""
@functools.wraps(func)
def inner(*args, **kwargs):
start = time.time()
ret = func(*args, **kwargs)
end = time.time() - start
if end < 60:
print(f'花费时间:\t{round(end, 2)}秒')
else:
min, sec = divmod(end, 60)
print(f'花费时间\t{round(min)}分\t{round(sec, 2)}秒')
return ret
return inner
@timeit
def main():
""" 主函数 """
urls = ['https://www.researchgate.net' for _ in range(100)]
crawl = Crawler(urls)
# 创建循环,检测task运行的情况并返回结果
event_loop = asyncio.get_event_loop()
event_loop.run_until_complete(crawl.run(event_loop))
if __name__ == '__main__':
main()
https://github.com/HuberTRoy/aiohttp-chinese-documentation aiohttp中文文档
三次握手:(打电话,信号不好时)
client —SYN — server:想跟你建立连接
server — ACK+SYN ---- client:同意,再给你一个(减少通信次数)
client -----server
- TCP是双向全双工的,我给你发送数据的时候,你也可以给我发数据,收发互不影响
- 创建----绑定—监听端口,阻塞的等着接受数据—read—write—close
- 创建—连接—write—read—close
- 绑定对应连接
- 多任务:多进程-多线程-多协程
- 进程属于操作系统 线程属于进程 协程属于线程
- 内存消耗进程最大 线程消耗:2k左右
- 进程之间的通信:socket 消息队列 共享内存 UDS 管道 文件
- 共享内存
- 进程通信不灵活
- 多个线程共享一个进程的资源
- 多个进程对于一个单核CPU不是并行的
- 进程之间的上下文切换
- A B 线程(进程)来回执行一些操作 context
- 进程以时间片来切换,时间片轮转
- 进程由os来调度 线程由python解释器来调度
- 时间片轮转是低效的切换方式
- 改进:算法
- 服务器最好是利用60-70%
- 中断有优先级(信号,事件) 本身就是异步的,网络事件发生一种通知机制
- 中断可以进行回调程序
- 专门有一个程序监听事件,就是事件驱动
- 协程+事件驱动
- 多路复用:事件先由硬件接收到,先通知到操作系统,提供了一套接口,供应用程序调用
- select poll epoll
- 10个水壶10个炉子放在10个屋子,水开了通知,不通知就会轮询的查看
- 系统调用:epoll
- 协程:协作 多协程共存一个内核级线程
- python yield:让出cpu,交出控制权,执行后线程会挂起
- gevent python2里最成熟的实现 用c语言实现
- tornado 原生的yield语句
- asyncio
- 关键字:不允许定义成变量
- 包可以,但是内容会被覆盖
- python引入了两个关键字:async await
- rps:每秒请求量,多进程+多协程
- https://blog.csdn.net/weixin_30577801/article/details/95280996 asyncio