aliasing of mutable objects
在一个 list 里,我们 append 一个 object
list=[]
i={1,2}
list.append(i)
之后 query 它,还是 原先的 i
list[0]
但如果我们在 list 里更改它
list[0]|={5}
原先的 i 也会改变
i # return {1,2,5}
解决办法是
list.append(set(i))
这样相当于 append 了一个 copy
httpx.get(url)
httpx是一种modern的用法,比requests多很多功能
import httpx
page_text = httpx.get(url).text
[lambda x: x]
series.value_counts()>=10)[lambda s: s]
或者
s = pd.Series([1, 2, 3, 4])
s.loc[lambda x: x > 2]
""
"hi"
","
"there" # 相当于 "hi,there"
每个"..."之间不需要加逗号。
zip
如果已有同一个名字的.zip,再zip的时候会在.zip文件里添加文件。
所以如果想要overwrite原先.zip文件,需要先删除已有的.zip,
rm -f xx.zip
str.format
用一个空的{},后面.format(x)可以把x添加到{}里
tmpl = "./profile/{}"
tmpl.format("a") # './profile/a'
也可以多个
tmpl = "./profile/{}/{}"
tmpl.format("user", "settings") # → './profile/user/settings'
或者有名称
tmpl = "./profile/{name}"
tmpl.format(name="alice") # → './profile/alice'
X or Value
def send_df(df, *args, href, button=None):
json_data = df.to_json(orient='records')
return Form(
Hidden(value=json_data, name='dataframe'),
*args,
button or download_button(' Data'),
method='post',
action=href,
style='display:inline;'
)
用 or来表示,if button is None/False/empty, use default, else use button
- Using mutable or expensive defaults (lists, dicts, custom objects) as function defaults → dangerous. They get shared across calls. 也就是说,如果arg里是list,可能被改写。所以尽量不要让arg出现mutable default
- Using None + constructing inside → safe and predictable.
match case
之前
def as_pattern(p):
if isinstance(p,int):
print(f"You said a {p}")
elif isinstance(p,str):
print(f"You said a string {p}")
之后
def as_pattern(p):
match p:
case int() as num:
print('number',num)
case str() as string:
print('string',string)
case后可以跟value,string, 也可以跟class,这里的int()和str()就是 class。后面加的as是为了capture matched value。
另一个class的例子
class Point:
__match_args__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
match points:
case []:
print("No points")
case [Point(0, 0)]:
print("The origin")
case [Point(x, y)]:
print(f"Single point {x}, {y}")
case [Point(0, y1), Point(0, y2)]:
print(f"Two on the Y axis at {y1}, {y2}")
case _:
print("Something else")
if 后面不一定跟else
x = 5
if x > 3:
print("x is greater than 3")
plt title里show 逗号
plt.title(f'{len(x_values):,} pairs', fontsize=fontsize)
{variable:,}的格式
也可以定义小数点后几位
{variable:.4f} # 后四位
sampling
可以有不同的概率
import numpy as np
values = np.arange(100)
probabilities = np.random.rand(100)
probabilities /= probabilities.sum() # normalize to sum to 1
sampled = np.random.choice(values, size=10, replace=False, p=probabilities)
replace=False 意味着取出一个数之后不会再放回去重新pool。
pairwise
instead of using
list(zip(a[:-1],a[1:]))
可以用
pairwise(a)
比如
from itertools import pairwise
numbers = [1, 2, 3, 4, 5]
for pair in pairwise(numbers):
print(pair)
# return (1, 2)(2, 3)(3, 4)(4, 5)
is vs. ==
- is False: 只包含False
- == False: 其它类似0 或者None也算
defaultdict
在普通的dict[key]里,如果dict没有这个key,会raise error。
但如果a = defaultdict(list),a['key'] 则会返回一个空的list
defaultdict(set) 如果key没有会返回一个空的set
a = defaultdict(set)
a['a'].add('b')
a # defaultdict(set, {'a': {'b'}})
yield vs. yield from
yield 会return 一个 chunk
yield from 会iterate chunk里面的每个item,一个一个的return
def encode(text):
return [1, 2, 3]
def encode_iterable(iterable):
for chunk in iterable:
yield from encode(chunk)
for x in encode_iterable('sss'):
print(x)
# 返回
1
2
3
1
2
3
1
2
3
如果是用yield,返回
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
另一个例子:
for chunk in iterable:
yield from self.encode(chunk)
和下面这个是相同的
for chunk in iterable:
for token_id in self.encode(chunk):
yield token_id
安装/update miniconda
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
关上或者重新打开terminal,或者
export PATH="/home/sky1ove/miniconda3/bin:$PATH"
source ~/.bashrc
#or
source ~/.zshrc
防止每次ubuntu启动会自动开启conda
conda config --set auto_activate_base false
第一次启动conda
conda init
init会把miniconda相关的路径加到path里,这样以后就可以conda activate了
conda activate
conda deactivate
用linux
不知道为什么ubuntu/linux里,比在windows terminal快得多
async & await
用于asynchronous function。比如await bytes.read()
但如果是synchronous function,比如pd.read_csv() 或者pd.to_csv(),这种是order很重要的,不可以加任何await。
如果给await XXfunc 加一个func wrapper,def前需要加上async, 然后用的时候,func前要加上await,否则会报错。
如何区分sync和async呢?
import inspect
inspect.iscoroutinefunction(x.read)
macOS terminal env
python3 -m venv myenv
source myenv/bin/activate
gdown
可以把超过100Mb的文件放到google drive上share link,然后在link里找到ID
把link修改成以下格式
https://drive.google.com/uc?id={ID}
pip install gdown
url = "https://drive.google.com/uc?id=1l_5RK28JRL19wpT22B-DY9We3TVXnnQQ"
output = "fcn8s_from_caffe.npz"
gdown.download(url, output)
# 或者用cached_download,这样如果detect文件,下次就不会重新下载
gdown.cached_download(url, output, quiet=True)
get max cpus/gpus
import fastcore.all as fc
fc.defaults.cpus
parallel
超级好用的,不用额外下载,只需要tqdm,还有progress bar
pip install tqdm
from tqdm.contrib.concurrent import process_map
# or thread_map if io
results = process_map(func, list, max_workers=4,chunksize=256)
chunksize的default是1,如果很多line,会影响速度。
max_workers是min(32, os.cpu_count() + 4),会在cpu的数量基础上加4,保证速度更快。
好用的
pip install tqdm_joblib
from joblib import Parallel, delayed
from tqdm import tqdm
from tqdm_joblib import tqdm_joblib
# Enable tqdm within joblib
with tqdm_joblib(tqdm(desc="Processing Files", total=len(item_list))) as progress_bar:
dfs = Parallel(n_jobs=-1)(delayed(func)(item) for item in item_list)
parallel fastcore (不好用)
pip install fastprogress
from fastcore.utils import *
import fastcore.all as fc
parallel(func, items, n_workers=fc.defaults.cpus,progress=True)
#注意如果用progress=True需要提前安装fastprogress
parallel pandas
pip install pandarallel
pandarallel.initialize(nb_workers=32,
progress_bar=True,
use_memory_fs=False, # necessary for jupyter notebooks
)
out = df.col.parallel_apply(func)
out
另外注意,parallel里需要把output item pickle,class是不能被pickle的,所以如果输出是class object会报错,需要转换成dictionary,如下:
class to dict
如果用到了fastcore里的store_attr(),class object 转成dict时候用 obj.stored_args
如果是正常的class,用__dict__
dict iterate
当iterate dict的时候,for i in dict: ... , i是key,只有key会被iterate,相当于for i in dict.keys()
所以用sorted(dict, key=dict.get) 给入到key的item是key, 然后dict.get func里得到的是key,会返回value,所以会用返回的value排序。返回的是排列好的key的list。
如果想要返回排列好的dict,那么sorted里面用dict.items()
dict.items()返回(key,value)的tuple,
sorted(dict.items(), key=lambda x: x[1], reverse=True)
dict.items()可以iterate,但type是dict_items, 不可以dict.items()[:10], 所以如果想要query,需要先转换成list
list(dict.items())
combine two string
'a' + 'b' # 'ab'
b'\x01' +b'\x01' # 'b'\x01\x01'
zip 可以当做transpose
zip返回iterator,如果我现在有一个list of strings (类似matrix), transpose它们的办法是 zip(*list_strings)。
o=['#####', '.####', '.####', '.####', '.#.#.', '.#...', '.....']

添加图片注释,不超过 140 字(可选)
list(zip(*o))

添加图片注释,不超过 140 字(可选)
或者用next查看
next(zip(*o)) # output ('#', '.', '.', '.', '.', '.', '.')
可以把每行join起来
[''.join(i) for i in zip(*o)] # output ['#......', '######.', '####...', '#####..', '####...']

添加图片注释,不超过 140 字(可选)
dict(zip(list1,list2))
可以把两个list join起来变成dictionary
比如我有一个list of paths (Path),想要用stem作为key
dict(zip([p.stem for p in paths], result_list))
global variable
function里可以放global a 就能和 function外的a同步了
设置环境变量
command: export A="aaa"
in python
import os
os.environ['AA']='ssss'
dictionary function
可以用dictionary of functions 替代 if, elif, elif, 比如
func = {'XOR': lambda x,y: x^y, 'OR': lambda x,y: x|y, 'AND': lambda x,y: x&y}
而不用
if a == "AND":
d = x & y
elif a == "OR":
d = x | y
elif a == "XOR":
d = x ^ y
int(string, 2) 转换成二进制
第二位是base number, default 是10
max(list,key=lambda x: ..)
在max里可以用key=func返回最大的item
max(['apple','banana','peach'],key=lambda x: len(x))
mutable 可变的,immutable不可变的
tuple 是immutable,创建之后不可变。tuple可用于作dict的key。速度更快。
list是mutable,创建之后可以append, extend之类的。不可以用作dict key
progress_apply
给df.apply加上progress bar
from tqdm import tqdm
tqdm.pandas()
df.progress_apply()
indices & range
range(number) , number往往指代一个长度,里面的indices是number-1。 比如range(3)是0,1,2。
d[idx]: 比如有个string,d=brwrr,五个字母, 它的每一个indices是从0 到 len(d)-1,可以用[i for i in range(5)], 也就是0,1,2,3,4,来指代。|PS:range(5)不包括5
如果用for i in range(len(d)),刚好可以指代里面的每一个字母。
[d[i] for i in range(len(d))] # output ['b', 'r', 'w', 'r', 'r']
d[:idx], 范围。如果现在我不是指代每一个字母,而是指代一个范围,如果做同样的事情,就会最后少一位。
[d[:i] for i in range(len(d))] # output ['', 'b', 'br', 'brw', 'brwr']
这是因为在:i 里,i是不包含的。所以正确的写法是range(len(d)+1) ,也就是range(6) |PS: 0,1,2,3,4,5
[d[:i] for i in range(len(d)+1)] # output ['', 'b', 'br', 'brw', 'brwr', 'brwrr']
左侧指针: 想象一个指针在每个字母的左侧。0:1 就是在d[0]左侧的指针和d[1]的左侧的指针的范围。
np.vectorize 把string转换成number array
mapping = {'.': 0, '#': 1, 'O': 2, '@': 4}
warehouse_vector = np.vectorize(mapping.get)(warehouse)
warehouse_vector

添加图片注释,不超过 140 字(可选)
上面是转换前,下面是转换后

添加图片注释,不超过 140 字(可选)
可以用plt.imshow画图
def plot_grid(grid):
mapping = {'.': 0, '#': 1, 'O': 2}
grid_vector = np.vectorize(mapping.get)(grid)
colors = ['gray', 'darkblue', 'yellow']
cmap = ListedColormap(colors)
plt.figure(figsize=(3,3))
plt.imshow(grid_vector,cmap=cmap,vmin=min(mapping.values()),vmax=max(mapping.values())) # must put vmin & vmax, if any element is absent
plt.axis('off');
AOC tricks
pipeline:
先用ai把题做出来,知道大概思路,然后用solve it,把每个function分成小function写出来。
过程中用小的example把每一步的结果都显示/画出来,确定是对的,再进行下一步。
list
如果是加单一的item
mylist.append()
加多个item
mylist.extend()
list会保留添加的顺序。
set
把list和tuple转成set的最简单的方法就是set(list/tuple), 而不是 {*list/tuple}
如果是加单一的item,用add; list里用append
s = {1, 2}
s.add(3) # -> {1, 2, 3}
s.add(2) # -> {1, 2, 3}(2 已存在)
如果加 iterable items,用update; list里用extend
s = {1, 2}
s.update([2, 3, 4]) # -> {1, 2, 3, 4}
s.update("abc") # -> {1, 2, 3, 4, 'a', 'b', 'c'}
combine两个set
s = {1, 2}
s |= {3, 4}
print(s) # -> {1, 2, 3, 4}
set不保留顺序。
时间上,set 和dictionary一样都是1,list是N,会search list里所有的东西。所以用set会更快。
定义一个空的set ,不用{},而是
set()
定义一个不是空的set,直接用{...}
{1, 2, 3, 4, 5}
get overlap item
set(a) & set(b)
merge sets
set(a) | set(b)
merge multiple sets at the same time, 用set().union(*)
set().union(*g['sub_gene'])
set().union({'G1','G2'}, {'G3'}, {'G4','G2'}) # return {'G1', 'G2', 'G3', 'G4'}
如果set里有None,或者NaN,里面也会包含这个,所以需要提前dropna()
df.groupby(col).agg({'x': lambda x: set(x.dropna())})
dict
iterate dict的时候,是iterate它的key
for i in dict: print(i) # 返回的是key
所以可以用
max(dict,key=dict.get)
dict.get里是key,可以根据dict的value进行排序。
merge dict可以用|
dict1 | dict2
查看dict 有没有key不是 if dict[key] 而是 if key in dict, 反向的话不是if not dict[key] 而是if key not in dict
添加单一的item
my_dict = {'name': 'Alice', 'age': 25}
my_dict['city'] = 'New York'
用update添加多个k:v pair,如果key有重复且不同,会更新为最新的value
my_dict = {'name': 'Alice', 'age': 25}
my_dict.update({'city': 'New York', 'age': 30}) # 'age' gets updated
setdefault(key,default_value)
得到dictionary的key的value,如果没被定义,return 第二个position的值作为它的value
adjacency = {}
adjacency.setdefault('b', set())
setdefault(node1,set()).add(node2) 常用于创建adjacency lists
这里会return一个空的set(),可以在空的set里用add增加value
adjacency = {}
adjacency.setdefault('b', set()).add('a')
# output {'b':{'a'}}
pass vs. continue
pass 是一个paceholder,不做任何事情
continue前面满足的话,会skip for loop后面的code
for i in range(5):
if i == 2:
continue # 当i为2的时候,会skip print 2, 所以这个output是0,1,3,4
print(i)
mutable as argument in func
如果把mutable object作为function的argunent,如果它在function里被mutated,那么下次call这个function的时候会保留上次call的印记。check this: https://florimond.dev/en/posts/2018/08/python-mutable-defaults-are-the-source-of-all-evil
解决办法就是,在pass argument的时候为None,然后function里写, if a is None, a=[] the list. 这样在function里mutate的时候就不会保留之前的mutate痕迹了。
也可以用a??=[]
recursive function
写recursive function的一些trick
for loop recursive function
无需return什么,但需要告诉什么时候结束
下面这一坨,本质上是for loop一直循环,知道graph穷尽
middle_wires = {*operations[z][0]}
for middle_wire in middle_wires: # 把这个东西放到function的for loop,后面跟上function本身
mm_wires ={*operations[middle_wire][0]} # 把这两行放到function 里
z_related_wires.update(mm_wires)
for mm in mm_wires:
mmm={*operations[mm][0]}
z_related_wires.update(mmm)
for mmm in ...
# till operation[key] not found, # 把这个作为base,放在最开始
for loop recursive function里分为三个部分
- 主体部分,就是for loop 里面关键的两行操作,所以先把这两行放到recursive func里。
- for loop,放到主体部分后面,for loop里放function本身。
- 最后,找到结束语,就是base,如果穷尽,那就return
z_related_wires = set()
def traverse_wire(wire):
if wire not in operations:
return
middle_wires = set(operations[wire][0])
z_related_wires.update(middle_wires)
for w in middle_wires:
traverse_wire(w)
没有for loop, 但是value based recursive function
需要在func里的base和最终return 出一个value。
def traverse_calc(wire):
if wire in wires:
return wires[wire]
op_key = operations[wire][1] # AND, OR, XOR
a,b = operations[wire][0] # (mjb,nsx)
a_value,b_value = traverse_calc(a),traverse_calc(b) # (1,0)
output_wire = func[op_key](a_value,b_value) # (1&0) -> wire
wires[wire]=output_wire
return output_wire
上面的这个,base是 if wire in wires: return wires[wire]
function最后面还要加上return output_wire最为最外层的输出,否则会一直loop下去。这个return,决定了traverse_calc(a),traverse_calc(b) 的value
value based recursive function里分为四个部分
- 主体部分计算
op_key = operations[wire][1] # AND, OR, XOR
a,b = operations[wire][0] # (mjb,nsx)
a_value,b_value = traverse_calc(a),traverse_calc(b) # (1,0) # 这里可以先写成wires[a]
output_wire = func[op_key](a_value,b_value) # (1&0) -> wire
wires[wire]=output_wire
- return 部分,没有这部分会报错
return output_wire
- base 部分,决定了最里面的值
if wire in wires:
return wires[wire]
- 最终,自己的function放到哪里
a_value,b_value = traverse_calc(a),traverse_calc(b)
就是function里套function,比如我现在想要for loop 一个nested list,可以一直for loop, 直到item里没有list
def get_sum(x):
value = 0
for o in x:
if isinstance(o,int): value+=o
elif isinstance(o,list): value+=get_sum(o)
return value
a = [[1,2],3,[2,[1,1]]]
get_sum(a) # output is 10
if & elif
elif意味着如果if 是false,就用elif,如果有一长串elif,就用第一个True的elif,其余skip
如果全部用if,那么每个if都是independent的,即便True,也会检查下一个if。
下面满足一个,就会省略剩下的elif,即便满足。
x = 15
if x < 10:
print("x is less than 10")
elif x == 15:
print("x is 15") # This runs
elif x > 20:
print("x is greater than 20")
else:
print("x is something else")
但如果改成if,会print全部满足condition的x
SimpleNamespace
快速创建class
from types import SimpleNamespace
def ns(a,b,c): return SimpleNamespace(apple=a,banana=b,cat=c)
ns(1,2,3) # output namespace(apple=1, banana=2, cat=3)
k =ns(1,2,3)
k.apple # output 1
json
json.load(f) 和 json.dump(f)
import json
# load through file
with open(file_path,'r') as f: data =json.load(f)
# loads through string text
json.loads(path.read_text())
上面一个是load, 一个是loads,一定要注意!!
# save
with open(file_path,'w') as f: json.dump(data, f)
shutil.copy 可以复制文件
import shutil
shutil.copy(source file path, destination file path)
设置limit
小的数用max设置,大的数用min设置
# 设置一个值的下限(最小值)
value = -10
lower_limit = 0
value = max(value, lower_limit) # 如果value小于lower_limit,则取lower_limit
print(value) # 输出: 0
# 设置一个值的上限(最大值)
value = 150
upper_limit = 100
value = min(value, upper_limit) # 如果value大于upper_limit,则取upper_limit
print(value) # 输出: 100
# 同时设置上下限
value = 200
lower_limit = 50
upper_limit = 150
value = max(min(value, upper_limit), lower_limit) # 先限制上限,再限制下限
print(value) # 输出: 150
reversed(list)
generate 一个相反的iterator
a=[1,2,3]
for i in reversed(a): print(a) # output: 3 2 1
isinstance(value, dtype)
判断当前value的type
o = 1
if isinstance(o,int): return o # output is 1
list.index(value)
在一个list里得到某个value的位置(从0开始)
dnames='zero,one,two,three,four,five,six,seven,eight,nine'.split(',') # make a list
dnames.index('eight') # return 8
list.count(0)
可以count这个list里有多少个0
concatenate string via +
a='8'
b= '6'
a+b # output is '86'
concatenate int
a=8
b=6
a*10+b # output is '86'
%取余
10%3 = 1
两个小圈圈可以想象成除以之后的边角料,也就是余数。
/ 除以
10/3 = 3.333
砍大刀
//除以,得到floor的整数
10//3 = 3
-10 // 3 = -4 (-3.33向小的取,就是-4)
砍两个大刀,把小数点后面的数砍没了。
split
split 的sep default是空格,\t和\n
" Hello world \tthis\nis Python ".split() # output ['Hello', 'world', 'this', 'is', 'Python']
splitline
把\n变成list of items
samp = '''1abc2
pqr3stu8vwx
a1b2c3d4e5f
treb7uchet
'''
lines = samp.splitlines() # output ['1abc2', 'pqr3stu8vwx', 'a1b2c3d4e5f', 'treb7uchet']
.isdigit()
判断是否是数字
'5'.isdigit()
named tuple
create a class
nt = namedtuple('Bodient', ['a','b'])
my_tuple = (10, 20)
print(my_tuple[0]) # Hard to tell what this index means
bodient = nt(a=10, b=20)
print(bodient.a) # Clear that this is the 'a' field
Dictionary pop
dict.pop(‘key’, None) 可以移除key,同时不反回error,如果第二个位置define了
poetry
install
# install pipx
sudo apt update
sudo apt install python3-venv # requirements for poetry
sudo apt install pipx
# install poetry
pipx install poetry
或者
curl -sSL https://install.python-poetry.org | python3 -
echo 'export PATH="PATH""' >> ~/.bashrc
source ~/.bashrc
如果folder里有poetry 的pyproject.toml文件(里面有tool.poetry之类的),可以用poetry来安装
cd folder
poetry install
class SS(Enum)
原先
class Color:
def __init__(self, name):
self.name = name
def show_name(self):
return f"The color is {self.name}"
# Create an instance of the class
blue_color = Color("blue")
现在可以写成
from enum import Enum
class Color(Enum):
blue = "blue"
red = "red"
def show_name(self):
return f"The color is {self.value}"
# Access an enum member and call the method
print(Color.blue.show_name())
通过Enum, Color.blue 就是self.value
getpass可以输入密码
from getpass import getpass
token = getpass("Type your token: ")
会蹦出一个框用来输入密码,储存在token里
调用的时候直接用token即可。
用function来定义param
原先
param = {xxxxx.....}
现在
def param():
return {xxxxx....}
这样import py file的时候,就不会load很多