基础
基本概念
特点
- 简单优雅,使用难度曲线平缓,可以快速上手;
- 跨平台,在不同平台上开发;
- 大量库(内置或第三方库)、开发快;
- 扩展性强,可以将C/C++等高效语言包含成Python的库;
- 解释型、动态型语言、慢;
- 不可加密;
官方&发行
- 官方版:只包含解释器;
- anaconda:包含解释器及常用库包(如科学计算相关包);
工具
- iPython:交互式解释器、代码补全等;
- jupyter notebook/lab:通过web交互的编程环境,简洁、插件多、功能强大;
- VScode:文本编辑器,有丰富的插件扩展;
- pycharm:集成开发环境;
- pip:帮助解决依赖问题;
随机函数
- 随机数种子的作用是设置一个起点,起点的设置一次有效,但实际效果是持续的;
- Pytorch需要专门为GPU设置seed;
书写规范
起始
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
- 前者在Linux、Mac OS中指定编译器;后者告诉编译器用utf-8来读取源码。
库写法
if __name__ = '__main__':
# 当直接执行代码时,条件为True,执行;而当作为库被导入时条件为False
pass
缩进
- 不同级别的代码块通过缩进进行区分;
- 缩进是4个空格,许多文本编辑器会默认将tab键转为4个空格;
注释
- 合适比例的注释可以增强代码的可读性;
# 注释放在这里面
命名
- 字母下划线开头,字母下划线数字组成;
- 但一般很少使用下划线开头定义常规变量和函数,下划线开头用在特殊场景;
- 通常变量、函数名全小写,类名首字母大写;
- 变量、函数、类等的命名应当尽可能顾名思义,降低歧义,增加可读性;
- 降低硬编码,更多用变量常量代表;
PEP8
- Python代码风格优雅的定义
条件语句
if a:
# 代码块
elif b:
# 代码块
...
else:
# 代码块
循环语句
while a: # 当a为真时执行代码
# 代码块
continue # 也可以通过continue结束本次循环
break # 也可以通过break终止循环
for b_i in b: # b为可迭代对象,如列表、元组、字符串等,b_i为其中的每一个元素
# 代码块
continue # 也可以通过continue结束本次循环
break # 也可以通过break终止循环
- for语句可以作用于迭代器;迭代器内部都需要实现__iter__方法。
- 顺序、条件、循环可以相互嵌套,但循环直接放在条件中的情况比较少,一般过于复杂的代码会用函数包装;
lambda表达式
- 用于代替极其简单的函数
- 格式:lambda 参数: 返回
lambda x,y=7: x+y
数据结构
基本数据结构
- 整数(int)、浮点数(float)、字符串(str)、布尔值(bool)
base_type = [10, 23.4, 'sdfh32h45', True, False]
- 可以通过其它类型合适的数据相互初始化,如
print(int('23')) # 23
序列类型数据结构
- 字符串:由连续的字符组成;整个都不能改变,改变需要新建对象;
- 元组:由连续的任意对象组成,存放的是这些对象的引用;元组存放的引用不能边变,但是引用指向的内容可以变;
- 列表:由连续的任意对象组成,存放的是这些对象的引用;列表存放的引用也可以变;
a = 'ahdk432' b = (2, 6, '45', [4,'78']) c = ['gs', 4, 7, [34, '56']] sequence_type = [a, b, c]
字符串str
常见操作
- $in/not in$ 判断成员关系;
- +连接两个字符串;
- *可以对字符串进行重复;
- $[::]$,索引、切片操作;
- $.strip()$去掉开头和末尾的字符
替换
print('可以在%s,以及%s,通过替换的方式得到所需字符串' % ('这里', '这里...等'))
not_trans = r'在字符串前加上r,可以指定字符串不转义,如\n等'
元组tuple
- 相比list,更多的是用在不需要改变的场景,通过元组可以防止更改;
列表list
列表list是Python中的一个强大的数据结构,本质上是一个可以动态调整大小的ArrayList,list存储数据是在一个数组中存储元素的地址,地址实际指向的元素可以是很多种形式:数字、布尔、字符串、元祖、链表。
创建
- 用中括号$[]$包裹元素直接生成,元素使用逗号分隔;
- 用$list()$方法,将其它数据结构(字符串、元组、range函数等)转化成列表;
- 列表生成式/列表解析式/列表推导式,生成列表。
扩充
- $append()$在尾部添加元素;
- $insert(k,value)$在指定位置添加元素;
- $+$和$list_1.extend(list_2)$将两个链表拼接成一个新列表。
删除&销毁
- $del$ $list[m]$;
- $remove(value)$删除第一个匹配项;
- $pop(m)$删除指定位置元素;
- $clear()$清空;
- $del$ $list$销毁。
切片
- $[i:i+n:m]$:从第i位开始至i+n位,不包括i+n位,每隔m位取一个。
- 默认值$[0:length:1]$。
- 值都可以大于列表长度,取不到就是空列表,不会越界报错。
- 从0开始索引,左闭右开区间使得切片很优雅。
step可以是负数,代表从后往前取,这种情况是先翻转再看索引来截取。
a1 = [1, 2, 'f']
a2 = list('asdfgjsld')
a3 = [2*i for i in range(10)]
print(a1, a2, a3)
a1.append(4)
a1.insert(5, 'asd')
a4 = a1+a2
a2.extend(a1)
print(a1, a2, a4)
del a1[0]
a1.remove(4)
a1.pop(-1)
a2.clear()
del a4
print(a1, a2)
其它操作
- $len(list)$获取元素个数。
- $count()$统计指定元素个数。
- $max(list)$统计元素最大值(要求元素类型相同才可比较,数值类型、字符串等直接比较,其它类型比较id)
a=[2,4,5,6] max(a) out:6
a=['dvs','vsvd','be','dfbeab'] max(a) out:'vsvd'
- $min(list)$与$max$类似,比较正好相反。
- $index()$获取指定元素索引(第一个匹配项)
- $reverse()$翻转列表
- $copy()$浅拷贝
- 深拷贝
import copy copy.deepcopy(list)
- $sort()$原地排序
- $sorted(list)$生成新列表
列表生成式&生产器表达式
l=[x*2 for x in range(5)] # 列表生成式,4以内整数的2倍数
g=(x*2 for x in range(5))# 生成器表达式
type(l)# 结果:<type 'list'>
type(g)# 结果:<type 'generator'>
print(l)# 结果:[0,2,4,6,8]
print(g)# 结果:<generator object at 0x000002173F0EBC50>
next(g)# 0
next(g)# 2
next(g)# 4
next(g)# 6
next(g)# 8
next(g)# Traceback (most recent call last): ....StopIteration
for x in g:
print(x,end=' ')# 结果:0 2 4 6 8
有时候不需要同时使用所有数据的时候,使用生成器表达式会更节省空间,当然也有其它方式来替代。
yield
# 计算斐波那契数列的生成器
def fibon(n):
a=b=1
for i in range(n):
yield a # 使用yield
a,b=b,a+b
g=fibon(1000000)
next(g)# 1
next(g)# 1
next(g)# 2
字典dict
- 底层是hash表,是一个个的$(key, value)$对;
- key要求是可以进行hash计算的元素,对value则限制较少;
dict1 = dict() dict1['name'] = 'wan chuan' dict1['age'] = 24 dict1.keys() # 是key的list dict1.values() # 是value的list dict1.items() # 是(key, value)元组的list
字典推导式
d={x:x*2 for x in range(5)}
函数
定义&参数
- 通过指定参数名字,可以变化参数的先后位置,但一般不建议变位置;
- 默认参数可以不输入
# c,d是可变长参数,c是list形式,d是字典形式 def fun_name(a, b=1, *c, **d): print(c) print(d) if b == 1: return a else: return a**b print(fun_name(3)) print(fun_name(3, 2))
变量作用域
- 在函数中出现与外部同名的变量,会出现两个备份,在函数内使用内部局部变量;
- 通过在函数内的变量前加上global,可以指定函数内的变量为全局变量,以影响函数外;
闭包
- 实现内部函数调用外部变量
- 可以得到带变量/状态的函数,这个状态可以是函数
# 得到一个任意数量的累加器 def counter(*c): if not c: cnt = [0] else: cnt = [i for i in c] def add_one(): for i in range(len(cnt)): cnt[i] += 1 return cnt return add_one f = counter(1,7,3) print(f())
- 考虑到变量有不同的层次,因此闭包也可以多层嵌套,得到更加抽象的闭包
装饰器
装饰器通过闭包得到,简单说是把函数包一层;
# 得到一个计时装饰器 def timer(func): def wrapper(*a,**b): start_time = time.time() func(*a,**b) end_time = time.time() print('time is %s' % (end_time-start_time)) return wrapper @timer def do_something(n,show='null'): time.sleep(n) print(show) do_something(2,'hello world') # 也可以将@的形式写为: def do_something(n,show='null'): time.sleep(n) print(show) do_something = timer(do_something)
装饰器还可以带参数,如
decorator(arg1, arg2)
意味着它会返回一个正常的装饰器装饰器还可以是类的形式,如下代码同样实现了装饰器的功能,它将代码分为了初始化和执行两个部分。
class myDecorator(object): def __init__(self, fn): print("inside myDecorator.__init__()") self.fn = fn def __call__(self): self.fn() print("inside myDecorator.__call__()") @myDecorator def aFunction(): print("inside aFunction()") aFunction()
装饰器还可以根据需要改变参数设置
通过functools的wraps可以让装饰后的函数更加像原函数一些
from functools import wraps def hello(fn): @wraps(fn) def wrapper(): print("hello, %s" % fn.__name__) fn() print("goodby, %s" % fn.__name__) return wrapper @hello def foo(): '''foo help doc''' print("i am foo") pass foo() print foo.__name__ #输出 foo print foo.__doc__ #输出 foo help doc
装饰器还可以给函数加上缓存
from functools import wraps def memo(fn): cache = {} miss = object() @wraps(fn) def wrapper(*args): result = cache.get(args, miss) if result is miss: result = fn(*args) cache[args] = result return result return wrapper @memo def fib(n): if n < 2: return n return fib(n - 1) + fib(n - 2)
装饰器注册回调函数
class MyApp(): def __init__(self): self.func_map = {} def register(self, name): def func_wrapper(func): self.func_map[name] = func return func return func_wrapper def call_method(self, name=None): func = self.func_map.get(name, None) if func is None: raise Exception("No function registered against - " + str(name)) return func() app = MyApp() @app.register('/') def main_page_func(): return "This is the main page." @app.register('/next_page') def next_page_func(): return "This is the next page." print app.call_method('/') print app.call_method('/next_page')
装饰器还可以把函数异步化
from threading import Thread from functools import wraps def async(func): @wraps(func) def async_func(*args, **kwargs): func_hl = Thread(target = func, args = args, kwargs = kwargs) func_hl.start() return func_hl return async_func if __name__ == '__main__': from time import sleep @async def print_somedata(): print 'starting print_somedata' sleep(2) print 'print_somedata: 2 sec passed' sleep(2) print 'print_somedata: 2 sec passed' sleep(2) print 'finished print_somedata' def main(): print_somedata() print 'back in main' print_somedata() print 'back in main' main()
上下文管理器
- 在文件操作后需要对文件进行关闭,不能漏掉,可以通过with as语句,更加优雅、安全的达到目的;
常用内建方法
文件
- 增删改查等:Python中将文件、网络、IO等都抽象为了文件,因此操作方法比价通用简洁;与Linux类似;
f = open('file.txt', 'w') # mode='w'/'r' f.write('这里需要是字符串') f.close() f = open('file.txt', 'r') # mode='w'/'r' print(f.readline()) # 读一行 print(f.readline(m)) # 读m个字符 print(f.tell()) # 文件指针的位置 print(f.readlines()) # 列表形式读全部,每行一个元素 f.seek(n, whence=0) # 将文件指针的位置移到whence+n,即可以从指定位置开始偏移 print(f.read()) # 全部读 f.close() # .close()如果漏掉是严重错误,通过下面的with as语句,可以不用写.close(),相当于隐式的包含了 with open('file.txt', 'r') as f: f.read()
- os库:
import os, pathlib os.path.abspath('.') # 获取绝对路径 os.path.exists('/Users/wanc/Desktop') os.path.isdir('/Users/wanc/Desktop') os.path.isfile('/Users/wanc/Desktop') os.path.join('/Users/wanc', 'Desktop') p = pathlib.Path('.') p.resolve() p = pathlib.Path('./tmp/a/') pathlib.Path.mkdir(p, parents=True)
异常处理
- 各种异常都是Exception的子类,所以Exception可以捕获所有异常
try: # 需要监控的代码 except (ValueError, KeyError, AttributeError): # 捕获到特定异常时的处理 except Exception as e: # 捕获到所有异常时的处理 print(e) finally: # 无论是否异常都会执行的代码,如关闭文件等
高阶函数
from functools import reduce
filter(fun, iterable) # 根据fun得到True/False,对iterable的值进行过滤
map(fun, *iterables) # 对iterables中的值用根据fun进行处理
reduce(fun, sequence) # 根据fun对sequence中的值进行聚合
zip(iter1[,iter2[...]]) # 将多个iter合并成一个iter,返回列表迭代器
sorted(iterable, key=fun) # 通过key的函数来选择排序的key,以实现对复杂对象的排序
正则化表达式 re
- 正则化表达式匹配:用特殊的符号表达特殊形式的字符串,可以看成是对字符串的压缩或泛化
# .任意单个字符 # *前面的字符出现0到多次 # +前面的字符出现1到多次 # ?前面的字符出现0或1次 # ^以后面的字符开头 # $以前面的字符结尾 # {n}前面的字符出现n次 # {m,n}前面的字符出现m到n次,左右闭区间 # [s]匹配括号中的任意一个字符 , [12378], [0-9], # |左或右的字符 # \d匹配一串数字,相当于[0-9] # \D匹配不包含数字 # \s匹配一串小写字符,[a-z] # ^$ 空行 # .*? 不使用贪婪模式,?是控制.*只匹配第一个符合的部分 # ()通过用()将一部分扩起来,匹配后可以通过.group(n)取出其中的部分,或通过.groups(n)取得所有; import re p = re.compile('3{4,5}') # 正则表达式 print(p.match('3333')) # 查看是否匹配,要求从前往后开始匹配 p = re.compile('^5(a|c)(.*)3$') print(p.match('5caafefwe42643333').groups())
- 正则化表达式搜索:search,不要求从第一个字符开始匹配
- 多次搜索:findall,避免search只找第一个
- 替换:$sub(被替换的字符,替换的字符,原字符)$
p = re.compile('3{4,5}') print(p.search('ghakhs3333')) print(p.findall('ghakhs3333fsadf3333jnkjn3333')) print(re.sub('3{4,5}','新的','ghakhs3333'))
时间日期
import time, datetime
time.time() # 秒数
time.localtime() # 各种日期时间类
time.strftime(r'%Y-%m-%d %H:%M:%S') # 结构化输出,选择输出哪几个量
nowtime = datetime.datetime.now() # 当前时间
settime = datetime.datetime(2021,10,19) # 设定时间
deltatime = datetime.timedelta(minutes=10) # 偏移时间
print(nowtime - deltatime, settime + deltatime) # 时间计算
数学
- math
- random
print(math.cos(3), math.sin(4),random.randint(1, 5),random.choice([3,5,'gs',[4,5]]))
面向对象
class Class_name1():
def __init__(self,att1,att2):
'''
构造函数,初始化
'''
self.att1 = att1
self.__att2 = att2 # 通过双下划线,可以将属性隐藏起来,避免外部直接访问,即类的封装
def func1(self):
pass
def __enter__(self): # 初始化前执行
pass
def __exit__(self, exc_type, exc_val, exc_tb):
# 使用with语句时,在代码块执行结束时执行
# exc_tb是捕获的with语句执行期间的异常
pass
class Class_name2(Class_name1): #Class_name2从Class_name1继承
def __init__(self,att1,att2,att3,att4):
super().__init__(att1,att2) # 调用父类的初始化方法初始化父类定义的属性
# Class_name1.__init__(att1,att2) 也可以用这种方式进行父类属性初始化,当从多个父类继承时,只能用这种方法
self.att3 = att3
self.att4 = att4
# self.att1 = att4 如果子类定义了父类已经定义的变量或函数,则会覆盖/重写,即多态
def func2(self):
pass
并发
多线程
import threading
def my_thread(arg):
pass
threads = []
for arg in args:
t = threading.Thread(target=my_thread,args=(arg)) # 创建线程,并设置执行方法和参数
t.start() # 启动线程,开始执行
threads.append(t)
for t in threads:
t.join() # 分别等待所有线程都运行结束
队列
import queue # 队列库,可以实现生产者消费者模型
q = queue.Queue()
q.put(3) # 加入队列
q.get() # 从队列中取出
q.task_done() # 提示已经取出元素并执行完,方便join判断是否结束
q.join() # 等待队列被消耗完
常用库
模块
- 导入模块:
from ab import cde as f f.func()
- 制作模块:将方法、类等写成py文件或者用文件夹组织;
JSON
- JSON 的全称是 JavaScript Object Notation,是一种轻量级的数据交换格式。最初,JSON 只是JavaScript 的子集,但由于其简单易用而迅速走红。
- 现今大部分编程语言都支持对 JSON 的解析与生成,而近些年异军突起的 NoSQL 数据库也多参照 JSON来设计数据存储格式,例如 Mongodb 的BSON(Binary JSON)。
- JSON 有以下六种数据类型:number、boolean、string、null、array、object。前三种很好理解,第四个 null 对应 Python 的 None,最后两种,对应 Python 的列表和字典。
{
"name":"小明",
"age":14,
"gender":true,
"grade":null,
"skills":[
"JavaScript",
"Java",
"Python"
]
}
- json.dumps() 把 Python 对象序列化;
- json.dump() 先序列化,然后将内容存入文件
import json
d=dict(name='Tom',age='8',score=88)
json.dumps(d)
#'{"name": "Tom", "age": "8", "score": 88}'
with open('test.json','w') as f:
json.dump(d,f)
- json.loads() 从内存中读取内容解析;
- json.load() 从文件中读取内容解析
import json
d=dict(name='Tom',age='8',score=88)
tom_json=json.dumps(d)
json.loads(tom_json)
#{'age':'8','name':'Tom','score':88}
with open('test.json','r') as f:
print(json.load(f))
#{'name':'Tom','age':'8','score':88}
ujson可以代替json,且速度更快。
numpy
Numpy
基于c
语言实现,因此十分高效,pandas
又基于numpy
,提供更加丰富的功能。- 生成
ndarray
数据的三种主要方式:- 通过
Python
内置list
转化:np.array(list)
,可以用dtype
设置默认数据类型; - 直接用
numpy
生成:np.arange(m), np.zeros(), np.ones(), np.random.rand*(), np.empty(), np.eye(), np.linspace()
; - 通过文件导入:如
np.loadtxt('cdscacc.csv',)
,可以选择分隔符、使用那些行列等。
- 通过
Ndarray
是数组,所以无论多少维都用一个中括号[start:end:step]
完成索引、切片,而list可以层层嵌套,因此多层索引需要多个中括号;这种差别本质上是因为Ndarray
无论多少维都是一个Ndarray
,list则不是,它是不同层的。- 调用方法有两种途径,
np.fun(self),self.fun()
。 - 常用基本方法:
(arg)min,(arg)max,(cum)sum, diff
差分,nonzero
输出非零位置索引,median,mean,variance,sort, reshape
。 np.sort()
生成新的排序后数组,self.sort()
是在原数组基础上操作,reshape
等也是。- 数学运算:
np.sin(),cos(),tan(),……
np.linspace(start,end,number)
用于生成等间隔序列。ndarray
常用属性:ndim
:维度;shape
:形状;size
:元素数量;dtype
:元素类型,可以用self.astype()
来设置元素类型;
ndarray
的一个强大的筛选功能是直接比较并返回同样大小并有true/false
组成的数组,如:np.array(list)>4
,还可以矩阵与矩阵比较,只要符合广播机制要求,可以形状不一样。np.any(), np.all()
, 可以用于判断返回的bool
数组是否有True
或全都是True
。ndarray
直接相乘是元素乘,np.matmul()
是矩阵乘,np.dot()
可以适应多种乘法。ndarray
相乘的时候会使用广播机制np.transpose(), self.T
转置。np.clip()
截尾,self.flatten()
铺平。np.concatenate((a,b,….,n),axis=)
指定维度合并,np.split()
分割。Python
中大量使用指针,因此许多操作都只是复制指针值,并没有真正的拷贝数据,这样容易出现一个地方修改了,其它地方也跟着变的情况,因此如果需要修改值同时又要保留以前内容的话可以尝试深度拷贝,如self.copy()
。基本上任何一个对数据修改后返回的运算都是创建了新值。np.unique()
可以找出一个数组中的元素,可以看成去掉重复。
Pandas
- 如果把
numpy
看成是加强版的列表list
,pandas
就像是加强版的字典dict
,为数据加上了许多标签。pd.Series([23,43,24,np.nan,42,42]) 0 23.0 1 43.0 2 24.0 3 NaN 4 42.0 5 42.0 dtype: float64;
pd.date_range('2020-05-02',periods=4)
DatetimeIndex(['2020-05-02', '2020-05-03', '2020-05-04', '2020-05-05'], dtype='datetime64[ns]', freq='D');
pd.DataFrame(np.random.randint(0,100,(23,3)),index=np.arange(10,33),columns=['d','f','w'])
pandas
的操作方法大都是生成新的对象并返回。DataFrame
中的index
指示数组(字典)的行索引,columns
代表列索引,不指定则使用默认自然数,也可以指定。- 如果用字典生成
DataFrame
,默认字典的key
对应columns
。 DataFrame
常用属性:dtypes
:每一列的属性;index
:DataFrame
的index
;columns
:DataFrame
的columns
;values
:用矩阵形式输出内容;T
:转置;
DataFrame
常用方法:describe()
:获取每一列的计数、均值、标准差等等信息;sort_index()
:可以按照index
或columns
排序;sort_values(by=)
:选定某一特征排序。
DataFrame
常见索引方式:df[‘columns’]
,df.columns
进行列选择;df[m:n]
按行索引;df.loc[m:n ,[‘columns’,’’]]
:按标签进行选择,需要同时考虑行和列;df.iloc[:,:]
:按位置进行索引,不连续的时候可以用[‘’,’’,…,’’]代替:。
df[df[]><==data]
等方式筛选。df.dropna()
可以设置以多种方式来去掉有缺失值的行或列。df.fillna()
可以用于填入缺失值。df.isnull()
返回各个位置是否为空。- 通常用
read_*()
和to_*()
来读写文件。 pd.concat([a,b,…,c],axis=,ignore_index=)
可以用于连接多个DataFrame
:axis
:指定连接维度;ignore_index
:是否忽略原行索引;join
:用于指定索引不同时的处理方式,inner
:取交,outer
:并;
- 也可以用
append
来实现concat
的功能。 pd.merge(left,right)
也可以用于合并:how: str = 'inner'
,指定合并的方式on=None
,指定用哪个公共的列(关键字)合并left_on=None
,分别指定on
right_on=None
,分别指定on
left_index: bool = False
,是否考虑左边的index
right_index: bool = False
,是否考虑右边的index
suffixes=('_x', '_y')
,如果连接的两个有相同的key
,为了避免重复可以用这个加上后缀;indicator: bool = False
,增加一列说明数据来源,左右都;
- 可以配合
matplotlib
进行显示,并直接使用自带的plot
等方法。 - 选中某一列使用
self.value_counts()
可以查看该列的分布数量。 df.melt()
是df.pivot()
逆转操作函数,将列名转换为列数据(columns name → column values),重构DataFrame.如果说 df.pivot() 将长数据集转换成宽数据集,df.melt() 则是将宽数据集变成长数据集。
matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(5,3),dpi=100)
plt.plot([1,2,3,5,6,7])
plt.hist([1,2,3,5,6,7])
plt.scatter(x,y)
plt.show()
urllib/requests/BeautifulSoup
from urllib import request, parse
import requests
from bs4 import BeautifulSoup
url = 'http://www.baidu.com'
response = request.urlopen(url, timeout=1)
response.read().decode('utf-8')
response = requests.get(url)
response = requests.post(url)
response.text
response.json()
soup = BeautifulSoup(response.text, 'lxml')
soup.prettify()
soup.title.string
soup.p
soup.a
heapq
heapq.heapify('list')
:将一个list堆化,默认最小堆,最大堆可以将元素乘以-1。heapq.heappush('list', item)
:向堆中添加一个元素,继续维持小根堆。heapq.heappop('list')
:弹出堆顶(最小)元素,继续维持小根堆。heapq.heappushpop('list', item)
:先push,再pop。heapq.heapreplace('list', item)
:先pop,再push。heapq.nlargest(n, iterable, key=None)
:取前n大。heapq.nsmallest(n, iterable, key=None)
:取前n小。
bisect
bisect.bisect_left('list',x)
:在升序list
中插入一个x
,使得列表仍然有序,返回插入位置,如果有列表中有x
,则插入位置在原x
左侧,即原x
的位置。bisect.bisect_right('list',x)
:同上,如果有列表中有x
,则插入位置在原x
右侧,即原x
的位置+1。
pytorch
基础操作
view & reshape
- view:只改视图/索引方式,不会增加数据副本,如果遇到不连续的情况就报错;
- reshape:改视图/索引方式,必要时增加数据副本,如果数据连续只改视图/索引方式,如果遇到不连续就新增数据副本;
- 转置会带来数据存储不连续;
x = torch.tensor([1,2,3,4,5,6]) # 原数据连续存储
tensor([1, 2, 3, 4, 5, 6])
y = x.view(3, 2) # 改变视图
tensor([[1, 2],
[3, 4],
[5, 6]])
y = y.view(2, 3) # 改变视图
tensor([[1, 2, 3],
[4, 5, 6]])
y = y.t() # 转置后不再连续
tensor([[1, 4],
[2, 5],
[3, 6]])
y.view(2,3) # 无法改变,报错
z=y.reshape(2,3) # 改变视图,增加副本
tensor([[1, 4, 2],
[5, 3, 6]])
z[0,0] = 0 # 新副本不影响原数据
x
tensor([1, 2, 3, 4, 5, 6])
y[0,0] = 0 # view不增加副本,影响原数据
x
tensor([0, 2, 3, 4, 5, 6])
d = x.reshape(2,3) # 改变视图,不增加副本
tensor([[0, 2, 3],
[4, 5, 6]])
d[0,1] = 0 # 影响原数据
x
tensor([0, 0, 3, 4, 5, 6])
广播
- 形状不同的tensor操作可能会触发广播机制,以满足运算要求
Tensor和NumPy相互转换
# 共享内存的方法,快速
torch.from_numpy()
.numpy()
# 不共享内存的方法,需要拷贝数据
torch.tensor()
CPU和GPU相互转换
# 改变位置的同时指定数据类型
.to("cpu", torch.double)
.to("cuda", torch.float)
自动求梯度
# 从该位置开始反向传播
.backward()
# 配置tensor是否需要追踪梯度
.requires_grad
# 梯度保存位置
.grad
# 该部分操作不追踪
with torch.no_grad():