相对路径导入
from . import var
代表从当前目录名(包名)下的__init__.py文件导入。
from .模块名 import var
代表从当前目录下的模块名.py文件导入。
采用上面两种相对路径导入方式的模块,只能被导入到其他模块来运行,不能直接运行。
可迭代对象和迭代器
- 可迭代对象包含迭代器。
- 如果一个对象拥有__iter__方法,其是可迭代对象;如果一个对象拥有next方法,其是迭代器。
- 定义可迭代对象,必须实现__iter__方法;定义迭代器,必须实现__iter__和next方法。
- 判断是可迭代对象和迭代器:
from collections import Iterable
from collections import Iterator
my_list = [1, 2, 3, 4, 5]
iter_my_list = iter(my_list) iter(iterable),该方法将可迭代对象变成迭代器。
print(isinstance(my_list, Iterable)) # True
print(isinstance(my_list, Iterator)) # False
print(isinstance(iter_my_list, Iterator)) # True
print(isinstance(iter_my_list, Iterable)) # True
for迭代遍历
“for i in obj:”,Python解释器会在第一次迭代时自动调用iter(obj),
之后的迭代会调用迭代器的next方法,for语句会自动处理最后抛出的StopIteration异常。
生成器
生成器 是一种特殊的迭代器,生成器自动实现了“迭代器协议”(即__iter__和next方法)
定义迭代器有两种方式,第一个是使用yield关键词,另外一个是生成器表达式"()"
调用生成器函数,不会立马执行该函数里面的代码, 而是会返回一个生成器.
生成器只能遍历一次.
集合
集合内的元素不重复,无序。
- a = set() # a是一个空集合。该方法也可以将一个对象转变为集合,返回一个新set对象。
- a.update('hello') # 将要传入的元素拆分成个体添加到集合。
{'e', 'o', 'h', 'l'}
- a.add("world") # 将要传入的元素作为一个整体添加的集合。
{'world', 'h', 'l', 'e', 'o'}
集合的交集,合集,差集
- a = set("abc")
- b = set('cde')
- a&b # 交集
{'c'}
- a|b # 合集
{'e', 'b', 'c', 'd', 'a'}
- a-b # 差集
{'a', 'b'}
- b-a
{'e', 'd'}
基于通配符搜索文件
import glob
# 可使用3个通配符:
"*",代表0个或者任意个字符。
"?",代表一个字符。
"[]" ,匹配指定范围内的字符,如[0-9]匹配数字。
# 获取指定目录下的所有图片
print glob.glob(r"E:/Picture/*/*.jpg")
# 获取上级目录的所有.py文件
print glob.glob(r'../*.py') # 相对路径
***分割线***
# 获取父目录中的.py文件,用iglob返回的是一个生成器。
f = glob.iglob(r'../*.py')
print(f) #
for py in f:
print (py)
找出某一目录下,所有的目录和文件,用os.walk()
# ts1.py
import os
a = os.walk('./')
for i in a: os.walk() 返回的是一个生成器对象。
print(i) 每次返回的结果都是一个元组 --> (当前目录路径, 目录列表,文件列表)
输出结果如下:
('./', ['a', 'b', '__pycache__'], ['ts1.py', 'ts2.py', '__init__.py', '文件.txt', '目录.txt'])
('./a', [], ['ss.js'])
('./b', [], ['gg.js'])
('./__pycache__', [], ['ts2.cpython-36.pyc', '__init__.cpython-36.pyc'])
字符串的split方法, 去除某些字符,返回的是列表.
b = "*i**love*you**"
想象成"空*i*空*love*you*空*空" 空,表示空字符串
c = b.split('*')
print(c)
['', 'i', '', 'love', 'you', '', '']
模块导入简化过程。
def import(module_name):
if module_name in sys.modules:
return sys.modules[module_name]
else:
module_path = find(module_name)
if module_path:
module = load(module_path)
sys.modules[module_name] = module
return module
else:
raise ImportError
sys.modules 用于缓存,避免重复 import 带来的开销;
load 会将模块执行一次,类似于直接运行。
在一次执行中,同一模块只能被导入一次。
避免循环导入,但不代表循环导入就会出错。
在模块顶部循环导入会出错,可以考虑将导入语句换到被导入变量或者对象的后面,或者在使用变量或对象的前一句导入。
partial 类
from functools import partial
def test(a, b, c, d):
print(f'a is {a}')
print(f'b is {b}')
print(f'c is {c}')
print(f'd is {d}')
t = partial(test, 1, 2) 函数的参数不是一次获得的情况适合用。返回的是一个函数。
默认情况传入的参数按位置对应
t(3, 4)
输出结果:
a is 1
b is 2
c is 3
d is 4
可以通过关键字来传参
t(d=3, c=4)
输出结果:
a is 1
b is 2
c is 4
d is 3
getattribute
class Human(object):
def __init__(self, name):
self.name = name
def __getattribute__(self, obj):
调用实例的所有属性和方法,都会进入到这里。
为避免递归调用,返回父类的方法。
if obj.endswith("e"):
return object.__getattribute__(self, obj)
else:
return object.__getattribute__(self, obj)()
def info(self):
return "nice shot"
aa = Human("Taylor")
print(aa.name) name是e结尾,调用父类方法,父类 return getattr(self, obj),打印的是"Taylor"
print(aa.info) info不是e结尾,调用父类方法,父类 return self.obj,加上()就调用该方法。
输出:
Taylor
nice shot
getattr
class Human(object):
def __init__(self, name):
self.name = name
def __getattr__(self, item):
print(f'当访问的属性:{item},不存在时,进入这里。')
def say(self):
print('hello')
a = Human('Taylor')
a.say()
a.null
print(a.name)
输出结果:
hello
当访问的属性:null,不存在时,进入这里。
Taylor
描述符
类至少实现下列3个方法中的一个,就是 描述符类。
__get__(self, instance, owner) 用于访问属性,它返回属性的值
__set__(self, instance, value) 将在属性分配(赋值)操作中调用,不返回任何内容
__delete__(self, instance) 控制删除操作,不返回任何内容
使用:描述符类的实例作为另外一个类的类属性。
示例:
class MyDecriptor:
# instance: Test类的实例,即test。
# owner: Test类。
def __get__(self, instance, owner):
print("getting...", self, instance, owner)
def __set__(self, instance, value):
print("setting...", self, instance, value)
def __delete__(self, instance):
print("deleting...", self, instance)
class Test:
MyDecriptor()是 x 的描述符
x = MyDecriptor()
test = Test()
test.x 进入描述符的get方法。
test.x = 'hello' 进入描述符的set方法。
del test 进入描述符的delete方法。
序列化对象
import json
json.dumps(obj, default=fun)
default后面是一个函数,由函数来决定怎么序列化。
obj,代表一个类的对象。
第一次将整个obj作为参数传入fun函数,根据fun函数来序列化,
序列化后,还有不能序列化的部分,将该部分作为参数,再次调用该fun函数。。。直到完全序列化。
示例代码:
import json
class Me:
def __init__(self, obj):
self.name = "我是Me实例"
self.age = 11
self.p = obj
class Ti:
def __init__(self):
self.name = "我是Ti实例"
self.age = 12
t = Ti()
m = Me(t)
第一次传入的参数 o: <__main__.Me object at 0x0000000003A4AF60>
第二次传入的参数 o: <__main__.Ti object at 0x0000000003A4AF28>
def obj_to_str(o):
a = o.__dict__
return a
r = json.dumps(m, default=obj_to_str, ensure_ascii=False)
print(r)
输出结果:
{"name": "我是Me实例", "age": 11, "p": {"name": "我是Ti实例", "age": 12}}
枚举类型
from enum import Enum
class VIP(Enum):
YELLOW = 1
GREEN = 2
GREEN = 22 标签相同,报错.TypeError: Attempted to reuse key: 'GREEN'
green = 2 值相同,标签不同,不报错,相当于给GREEN指定了一个别名。
RED = 3
print(VIP.GREEN) 输出结果:VIP.GREEN
VIP.YELLOW = 11 定义后,不能修改值.AttributeError: Cannot reassign members.
获取标签下的数值
print(VIP.RED.value) 输出: 3
获取标签的名字
print(VIP.RED.name) 输出: RED
print(type(VIP.RED)) 输出:
print(type(VIP.RED.name)) 输出:
通过中括号 传 一个字符串
print(VIP['RED']) 输出:VIP.RED
遍历 三种
for i in VIP:
print(i)
输出:
VIP.YELLOW
VIP.GREEN
VIP.RED
for i in VIP.__members__:
print(i)
输出:
YELLOW
GREEN
green
RED
for i in VIP.__members__.items():
print(i)
输出:
('YELLOW', )
('GREEN', )
('green', )
('RED', )
比较
枚举跟枚举比较 支持
rt = VIP.YELLOW == VIP.RED
print(rt) 输出:False
枚举跟枚举的数值比较 不报错,但得不到正确结果。
rt = VIP.YELLOW == 1
print(rt) 输出:False
>或< 大小比较 不支持
print(VIP.RED > VIP.YELLOW)
TypeError: '>' not supported between instances of 'VIP' and 'VIP'
is 比较 支持
print(VIP.RED is VIP.RED) 输出: True
print(VIP.RED is VIP.YELLOW) 输出: False
装饰器 unique
from enum import Enum, unique
@unique
class VIP(Enum):
YELLOW = 1
GREEN = 2
green = 2
RED = 3
输出:
ValueError: duplicate values found in : green -> GREEN
将一个数值,转换成对应的枚举:
将数值作为参数传到枚举类中
a = 1
print(VIP(a))
输出:
VIP.YELLOW
wraps
装饰器会遗失被装饰函数的__name__和__doc__等属性,可以使用@wraps来恢复。
from functools import wraps
def my_decorator(f):
@wraps(f)
def wrapper():
"""wrapper_doc"""
print('Calling decorated function')
return f()
return wrapper
@my_decorator
def example():
"""example_doc"""
print('Called example function')
print(example.__doc__)
print(example.__name__)
输出结果:
example_doc
example
注释掉 @wraps(f)
example自身的__name__和__doc__都不显示。
再次运行,结果是:
wrapper_doc
wrapper
itemgetter attrgetter
from operator import itemgetter, attrgetter
- itemgetter(文档说明)
Return a callable object that fetches the given item(s) from its operand.
After f = itemgetter(2), the call f(r) returns r[2].
After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3])
示例:
my_dict = [dict(value=i) for i in range(4)]
g = itemgetter('value') # 返回的是一个可调用对象.
r = [g(i) for i in my_dict] # g(i) 等价于 i['value].
print(my_dict)
print(r)
输出:
[{'value': 0}, {'value': 1}, {'value': 2}, {'value': 3}]
[0, 1, 2, 3]
- attrgetter(文档说明)
Return a callable object that fetches the given attribute(s) from its operand.
After f = attrgetter('name'), the call f(r) returns r.name.
After g = attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).
After h = attrgetter('name.first', 'name.last'), the call h(r) returns
(r.name.first, r.name.last).
示例:
class Me:
def __init__(self, name, age):
self.name = name
self.age = age
a = Me("xyz", 22)
f1 = attrgetter('name', 'age')
print(f1(a)) # f1(a) 等价于 (a.name,a.age)
输出:
('xyz', 22)
yield from
yield from iterable
等价于
for i in iterable:
yield i
示例:
def mychain(*args):
for item in args:
for i in item:
yield i
def mychain1(*args):
for item in args:
yield from item
a = [1, 2, 3]
b = ('a', 'b', 'c')
for i in mychain(a, b):
print(i)
for i in mychain1(a, b):
print(i)
两个for,输出的结果都是一样的:1, 2, 3, a, b, c
还可以直接用chain
from itertools import chain
for i in chain(a, b):
print(i)
生成器正常执行完毕抛出 StopIteration.
若最后一句,是 return ,则会将 return 的值作为 StopIteration的内容。
用try...except 来获取 return的值。
调用方:调用委托生成器的。
委托生成器:包含 yield from 语句的。
子生成器:yield from 语句后面跟的。
委托生成器,相当于一个双向通道,在 调用方 和 子生成器 之间起 转发作用。
yield from 内部作了很多异常处理.
调用.close()方法,抛出的是 GeneratorExit,GeneratorExit 继承自 BaseException.
.close()方法,会让委托生成器和子生成器都关闭。
正常情况,子生成器 return 时,将抛出 StopIteration,
yield from 处理该异常,并将return 的内容作为自己的值。
子生成器发生异常,包括调用方主动throw()进来的异常,
子生成器return的值,不会返回给 yield from,
yield from 收到的是 None。
throw(StopIteration),子生成器和委托生成器都不需要去捕获处理,
yield from 自动处理了 StopIteration 异常,
委托生成器 恢复运行,向下执行。如有yield,运行到下一个 yield 后暂停。
通过throw()方法传递给子生成器的异常,需要手动处理,否则会向上冒泡。
若throw(GeneratorExit), 子生成器捕获后,pass,
则报RuntimeError: generator ignored GeneratorExit.
若throw(GeneratorExit),子生成器不要处理,直接在委托生成器的 yield from 处捕获。
生成器状态
import inspect
inspect.getgeneratorstate(生成器对象)
状态:
GEN_CREATED
GEN_SUSPENDED
GEN_CLOSED
协程 coroutine
协程:可以暂停的函数,并且可以在某个时候恢复,继续向下执行。
python 原生协程
用关键字 async 、await 来定义协程。
一个函数前面加上 async 就被定义为一个协程了。
await 相当于 yield from.
执行到 await 协程可以跳出去,执行其他函数或其他协程。
当 await 后面的条件满足时,才去执行 await 下面的语句。
协程 要和 事件循环 配合使用。
这里可以导入 asyncio 模块。
loop = asyncio.get_event_loop()
loop.run_until_complete() 可接收: 协程,future, task, asyncio.wait([协程列表])
获取协程的返回值,即 协程 return的内容。
方法一: 通过 future
fu = asyncio.ensure_future(协程)
fu.result()
方法二:通过 task
tk = loop.creat_task(协程)
tk.result()
当协程完成后,调用某个函数.
tk/fu.add_done_callback(函数名)
这个回调函数是在协程的return之后立马执行的。
注意,这个回调函数要有一个参数来接收 task.否则报错。
add_done_callback()方法,会将 task 传进 回调函数。
若回调函数有参数,但 add_done_callback()只接收函数名,
这时候要用 partial(函数名, 参数)
asyncio.wait() 和 asyncio.gather()
gather 比 wait 功能更多。
tasks = [协程1, 协程2...]
asyncio.wait(tasks)
asyncio.gather(*tasks) 注意必须有个星号。
group1 = [协程1, 协程2]
group2 = [协程3, 协程4]
group11 = asyncio.gather(*group1)
group22 = asyncio.gather(*group2)
asyncio.gather(*group1, *group2)
等价于
asyncio.gather(group11, group22)
取消任务
这里演示,通过 ctrl + c, 来取消。
tasks = [协程1, 协程2...]
try:
loop.run_until_complete(asyncio.wait(tasks)
except KeyboardInterrupt:
all_tasks = ayncio.Task.all_tasks()
for task in all_tasks:
task.cancel() 返回True/False,True代表取消成功。
loop.stop()
loop.run_forever()
finally:
loop.close()
==============================================
将普通函数添加到loop里面,通过call_soon(),call_later(),call_at().
启动: loop.run_forever().
停止: loop.run_forever(),将 loop 传给函数,在函数执行完后,
调用 loop.stop().
call_soon(),并不是立即执行,而是在下一次循环执行。
import asyncio
def mm(loop):
print('hello,girl.')
loop.stop() 将loop暂停,后面还可以再次调用这个loop。loop.close(),则不能再调用该loop.
loop = asyncio.get_event_loop()
loop.call_soon(mm, loop) 这里将loop作为参数传进函数,在打印完后,退出loop。
loop.run_forever()
print('*******')
loop.call_soon(mm, loop)
loop.run_forever()
call_later(),第一个参数是秒数,在等待这个秒数后,执行函数。
import asyncio
def mm(times, loop):
print(f'hello,girl.{times}')
if times == 3:
loop.stop()
loop = asyncio.get_event_loop()
loop.call_later(2, mm, 2, loop)
loop.call_later(1, mm, 1, loop)
loop.call_later(3, mm, 3, loop)
loop.run_forever()
print('*******')
call_at(),在某个时间点,执行函数。
用到 loop.time().这是loop内部时间。
import asyncio
def mm(times, loop):
print(f'hello,girl.{times}')
if times == 3:
loop.stop()
loop = asyncio.get_event_loop()
now = loop.time()
loop.call_at(now+2, mm, 2, loop)
loop.call_at(now+1, mm, 1, loop)
loop.call_at(now+3, mm, 3, loop)
loop.run_forever()
print('*******')
fu.set_result() 该方法用来 标记 该 future 完成,并设置值。
task 是 Future 的子类.
loop = asyncio.get_event_loop()
task = loop.creat_task(协程)
print(isinstance(task, asyncio.Future)) # True
=====================================
事件循环会阻塞当前线程,可以将其放入另外一个线程。
定义一个事件启动函数:
def loop_start(new_loop):
asyncio.set_event_loop(new_loop)
new_loop.run_forever()
获得一个loop: new_loop = asyncio.new_event_loop()
创建一个新线程:t = Thread(target=loop_start, args=(new_loop,))
启动新线程:t.start()
添加普通函数到事件循环中去:
new_loop.call_soon_threadsafe(函数, 参数)
添加协程到事件循环中去:
asyncio.run_coroutine_threadsafe(coro, loop)
线程池
import time
from concurrent.futures import ThreadPoolExecutor, as_completed, wait, FIRST_COMPLETED
def get(times):
time.sleep(times)
print(f'get {times} page success.')
return times
executor = ThreadPoolExecutor(max_workers=3)
通过 submit(),提交函数到线程池。submit()返回future对象,往下执行。
task1 = executor.submit(get, 1)
task2 = executor.submit(get, 2)
print('我没有等待task1,task2执行完毕才出现。')
cancel()方法,用于取消任务,若任务已经开始,不能取消。
若将 max_workers=1,就能成功取消任务2.
task1 = executor.submit(get, 3)
task2 = executor.submit(get, 2)
print(f'取消任务2:{task2.cancel()}')
done()方法,用于判断任务是否完成。
task1 = executor.submit(get, 1)
task2 = executor.submit(get, 2)
print(f'任务1完成: {task1.done()}')
time.sleep(2)
print(f'任务1完成: {task1.done()}')
result()方法,获取task的返回结果。
task1 = executor.submit(get, 1)
task2 = executor.submit(get, 2)
print(f'任务1的结果:{task1.result()}')
获取已经成功的task的返回值。
打印顺序,谁先完成打印谁。
sleep_times = [3, 2, 4]
all_task = [executor.submit(get, times) for times in sleep_times]
for future in as_completed(all_task):
data = future.result()
print(f'已经完成的任务的返回值:{data}')
通过 executor 获取已经完成的task的返回值。
注意 打印的顺序 和 sleep_times的顺序一致。
sleep_times = [3, 2, 4]
for data in executor.map(get, sleep_times):
print(f'已经完成的任务的返回值:{data}')
wait()方法,默认当wait()里面任务完成时,才往下执行。
sleep_times = [3, 2, 4]
all_task = [executor.submit(get, times) for times in sleep_times]
wait(all_task)
print('taylor')
wait(),可以传入 参数 来控制 何时往下执行。
sleep_times = [3, 2, 4]
all_task = [executor.submit(get, times) for times in sleep_times]
wait(all_task, return_when=FIRST_COMPLETED)
print('taylor')
GIL
GIL会根据执行的字节码行数,时间片,以及遇到 I/O 操作时,主动释放。
多线程中的 setDaemon
thread1.setDaemon ( True ):表示该线程会随着主线程的结束而结束。
setDaemon(),要放在 start() 前面。
threading.Semaphore
from threading import Thread, Semaphore
import time
Semaphore 控制线程数量。
内部维护着一个计数器.当 计数器 减为 0 时,阻塞。
sem = Semaphore(max)
sem.acquire() -1
sem.release() +1
class Spider(Thread):
def __init__(self, name, sem):
super().__init__()
self.name = name
self.sem = sem
def run(self):
print(f'{self.name}---start...')
time.sleep(2)
print('end...')
self.sem.release()
if __name__ == '__main__':
sem = Semaphore(5)
for i in range(20):
sem.acquire()
xiao = Spider(f'xiao.{i}', sem)
xiao.start()
条件变量 threading.Condition
条件变量 threading.Condition, 用于线程间 同步通信。
con = Condition()
con.acquire()
con.release()
可以使用with 语句, with con:
在with 语句下,才能使用:
con.notify() 发出通知,唤醒 wait 对象。
con.wait() 接收通知,醒来。
class Me(Thread):
def __init__(self, con):
这里的 name 是线程的名字。
super().__init__(name='我说')
self.con = con
def run(self):
with self.con:
print(f'{self.name}: one')
self.con.notify()
self.con.wait()
print(f'{self.name}: two')
self.con.notify()
self.con.wait()
class You(Thread):
def __init__(self, con):
super().__init__()
self.con = con
def run(self):
不用with语句,就直接调用 acquire() 和 release().
self.con.acquire()
self.con.wait()
print(f'{self.name}答: 1')
self.con.notify()
self.con.wait()
print(f'{self.name}答: 2')
self.con.notify()
self.con.release()
if __name__ == '__main__':
con = Condition()
me = Me(con)
you = You(con)
注意 启动顺序。
you.start()
me.start()
deque双端队列
from collection import deque
a = deque()
方法:
append()
appendleft()
extend()
extendleft()
pop()
popleft()
insert()
count()
index()
remove()
reverse()
ratate()
若 a = [1, 2, 3, 4, 5], a.rotate(3), a = [3, 4, 5, 1, 2]
这里的3,是从右向左数第三个元素,然后将该元素及其右边的所有元素移动到列表的前面。
若为负数,则相反, a = [4, 5, 1, 2, 3]
bcrypt
import bcrypt
加密
str = 'hello'
pw = bcrypt.hashpw(str.encode(), bcrypt.gensalt())
验证密码
my_str = 'hello'
if bcrypt.hashpw(my_str.encode(), pw) == pw:
print('correct.')
else:
print('invalid')