参考资料
格式化输出
参考
1 2 3 4 i = "my name is {name}" i = i.format(name="hg") print(i) #>>> my name is hg
1 2 3 4 name = "hg" i = f"my name is {name}" print(i) #>>> my name is hg
感觉还是f前缀好用
格式化字符串
在数字或字符串后跟冒号,可以格式化字符串.
1 2 3 4 5 6 7 8 9 10 print(f"|{'qcdxhg':10}|") #>>> |qcdxhg | # 冒号后跟数字,表示占用多大的位置.超过最大长度不作截取. print(f"|{'qcdxhg':10.4}|{'qcdxhg':10.8}|{10 + math.pi:10.5}|{10 + math.pi:.5}|") #>>> |qcdx |qcdxhg | 13.142|13.142| # 冒号后面数字的小数部分,表示字符串最大长度,截去超过长度的部分,如果被截取的是小数,同时还会四舍五入,并且小数点计入总长长度计算,不计入截取位数时的计算 # 数字向右对齐,字符串向左对齐 print(f"|{10 + math.pi:10.3f}|") #>>> | 13.142| # 内容是数字时,冒号后面的小数部分的后面跟个f,表示保留小数后几位(依然四舍五入)
1 2 3 4 5 6 7 #除了用冒号以外,还可以用just函数来实现字符串占位长度(限字符串) print(f"|{'123'.ljust(10)}|{'123'.center(10)}|{'123'.rjust(10)}|") #>>> |123 | 123 | 123| # str.zfill(x)向数字字符串左侧填充0,到达长度为x.此时"-"和"."(负号和小数点)均计数 print(f"|{str(-123.54).zfill(10):15}|") #>>>|-000123.54 |
数字文字
可以添加下划线增加可读性
1 2 3 4 5 6 7 i = 1_000 print(i) #>>> 1000 print(str(i)) #>>> 1000 print(repr(i)) #>>> 1000
重载运算符
运算符
运算
正向方法
反向方法
就地方法
+
取正
__pos__
-
取负
__neg__
~
按位取反
__invert__
==
相等
__eq__
!=
不等
__ne__
>
大于
__gt__
__lt__
<
小于
__lt__
__gt__
>=
大于等于
__ge__
__le__
<=
小于等于
__le__
__ge__
+
加法
__add__
__radd__
__iadd__
-
减法
__sub__
__rsub__
__isub__
*
乘法
__mul__
__rmul__
__imul__
/
除法
__truediv__
__rtruediv__
__itruediv__
//
整除
__floordiv__
__rfloordiv__
__ifloordiv__
%
取余
__mod__
__rmod__
__imod__
divmod()
整除并取余
__divmod__
__rdivmod__
__idivmod__
**, pow()
取幂
__pow__
__rpow__
__ipow__
@
矩阵乘法
__matmul__
__rmatmul__
__imatmul__
&
按位与
__and__
__rand__
__iand__
|
按位或
__or__
__ror__
__ior__
^
按位异或
__xor__
__rxor__
__ixor__
<<
按位左移
__lshift__
__rlshift__
__ilshift__
>>
按位右移
__rshift__
__rrshift__
__irshift__
不支持计算时,函数返回NotImplement.
分派机制见 流畅的python p312
切片
流畅的python p234解释了原理
a[i:j]表示a中的[i,j)
a[::-1] 将a倒叙.实际上用到第三个参数的时候,就是将a按每x位取一位
除了基本切片操作,还有somestr[start: end :stride],实现步进式切割,即从每n个元素中提取1个出来.
1 2 3 4 5 6 7 a = "abcdefgh" print(a[::2]) #>>> aceg print(a[::-1]) #>>> hgfedcba # 通过[::-1]可实现倒序排列 # effective python建议不要指定了start或end的同时指定stride,因为难以理解. (可以分开写)
切片放在赋值语句的左边或者作为del操作的对象,可以轻松地操作序列
1 2 3 4 5 6 7 8 9 10 11 12 13 l = list(range(10)) l[2:5] = [20, 30] print(l) #>>> [0, 1, 20, 30, 5, 6, 7, 8, 9] del l[5:7] print(l) #>>> [0, 1, 20, 30, 5, 8, 9] l[::2] = [22, 33, 44, 55] print(l) #>>> [22, 1, 33, 30, 44, 8, 55] l[2:5] = [100] print(l) #>>> [22, 1, 100, 8, 55]
encode和decode
str.encode()可以用于使用给定的文本编码将str转换为bytes,
bytes.decode()可以用于实现相反.
元组
元组与list类似,不同之处:
元组的元素不能修改
元组可以作为map的键,list不可以
元组的拆包
1 2 3 4 5 6 7 8 9 # 一个元组只有一个元素时,一定要在这个元素后加逗号 tup1=("sqh",) tup2=("sqh","123") print(tup1) #>>> ('sqh',) print(tup2) #>>> ('sqh', '123') print(tup2[0]) #>>> sqh
1 2 3 4 5 t = ("ddd","sss") m = {t:"aaa"} print(m[t]) #>>> aaa #元组作为map的键
1 2 3 4 5 6 7 8 9 a, b, c = (1, 2, 3) print(a, b, c) #>>> 1 2 3 #元组的平行赋值就是拆包 x, y, *rest = range(5) print(x, y, rest) #>>> 0 1 [2, 3, 4] #用*处理剩下的元素
具名元组
collections.namedtuple构建的类的实例所消耗的内存跟元组是一样的,因为字段名都存在对应的类里面.这个实例跟普通的对象实例比起来也要小一些,因为python不会用__dict__来存放这些实例的属性.
1 2 3 4 5 6 7 8 from collections import namedtuple T = namedtuple("ANAMEDTUPLE","a b c d") t = T("A","B","C","D") print(t) #>>> ANAMEDTUPLE(a='A', b='B', c='C', d='D') print(f"t.a = {t.a}") #>>> t.a = A
字典的默认值
dict.get(k,default)
dict.setdefault(self,key,value)
1 2 3 4 5 6 7 8 9 10 11 12 d = { "a": 0 } print(d.get("b", 255)) #>>> 255 #dict.get(key,default)实现默认值 d.setdefault("b", 233) print(d["b"]) #>>> 233 #dict.setdefault(self,key,value)实现默认值
collections.defaultdict(type)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import collections d2 = collections.defaultdict(list) print(d2) #>>> defaultdict(<class 'list'>, {}) print(d2["a"]) #>>> [] #出现了,默认值是空list d2["b"].append(666) #d2["b"]的默认值是[],所以可以直接append print(d2) #>>> defaultdict(<class 'list'>, {'a': [], 'b': [666]}) #访问过的d2["a"]也有值了 d2["c"] = 3 print(d2) #>>> defaultdict(<class 'list'>, {'a': [], 'b': [666], 'c': 3}) #defaultdict并没有限制数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13 d3 = collections.defaultdict(tuple) print(d3["a"]) #>>> () d4 = collections.defaultdict(int) print(d4["a"]) #>>> 0 d5 = collections.defaultdict(bool) print(d5["a"]) #>>> False #各个数据类型的默认值貌似是bool值为False的值
missing
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class strDict(dict): def __init__(self,*args,**kwargs): super(strDict, self).__init__(*args,**kwargs) def __missing__(self, key): if isinstance(key,str): raise KeyError(key) #如果找不到的键本来就是str,则报错 else: return self[str(key)] #如果找不到的键不是str,则找str化的键 def __contains__(self, key): return key in self.keys() or str(key) in self.keys() #先按照原本的值查找,找不到再str之后再找 a = strDict() a["123"] = 233 #存值的时候,键是字符串 print(a[123]) #>>> 233 #取值的时候用int也能取出.
__getattr__,__getattribute__,__setattr__控制对象属性
effective python 第97页
如果类定义了__getattr__,同时系统在该类对象的实例字典(instance.__dict__)中又找不到待查属性,那么系统将调用__getattr__
与__getattr__不同,每次在对象上调用hasattr和getattr时.__getattribute__都会执行
__setattr__可以向对象中新添属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class O: def __init__(self): self.a = 1 self.b = 2 def __getattr__(self, item): print(f"""not has attr named '{item}'""") def __getattribute__(self, item): print(f"""try to accept to '{item}'""") return super().__getattribute__(item) o = O() o.__setattr__("c",3) #>>> try to accept to '__setattr__' setattr(o,"d",4) print(o.__dict__) #>>> try to accept to '__dict__' #>>> {'a': 1, 'b': 2, 'c': 3, 'd': 4} o.e #>>> try to accept to 'e' #>>> not has attr named 'e'
排序
sorted(iterable,key,reverse)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class S: def __init__(self, v: int): self.v = v l = [ S(8), S(6), S(1), S(2) ] for s in l: print(s.__dict__) #>>> {'v': 8} # {'v': 6} # {'v': 1} # {'v': 2} l = sorted(l, key=lambda x: x.v) for s in l: print(s.__dict__) #>>> {'v': 1} # {'v': 2} # {'v': 6} # {'v': 8}
list.sort()是就地修改
bisect
bisect.bisect 返回一个数插入有序列表的位置,不会将这个数插入数组
bisect.insort 将一个数插入有序列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import bisect x = list(range(20)) x = x[::3] print(x) #>>> [0, 3, 6, 9, 12, 15, 18] i0 = bisect.bisect(x, 2) print(i0) #>>> 1 #返回应该插入的位置 print(x) #>>> [0, 3, 6, 9, 12, 15, 18] #原列表未被修改 i1 = bisect.insort(x, 2) print(i1) #>>> None #返回None print(x) #>>> [0, 2, 3, 6, 9, 12, 15, 18] #原列表已被修改
array
流畅的python P41
浅拷贝与深拷贝
流畅的python p187
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import copy l0 = [233,255] l1 = [0,1,2,l0,5] #引用了l0 l2 = l1 #引用赋值 l3 = list(l1) #构造函数实现浅拷贝 l4 = l1[:] #list的解构(?)实现浅拷贝 l5 = copy.copy(l1) #copy模块浅拷贝 l6 = copy.deepcopy(l1) #copy模块深拷贝 print(l2 is l1) #>>> True print(l3 is l1) #>>> False print(l4 is l1) #>>> False print(l5 is l1) #>>> False print(l6 is l1) #>>> False l0.append(798) print(l2) #>>> [0, 1, 2, [233, 255, 798], 5] print(l3) #>>> [0, 1, 2, [233, 255, 798], 5] print(l4) #>>> [0, 1, 2, [233, 255, 798], 5] print(l5) #>>> [0, 1, 2, [233, 255, 798], 5] #因为是浅拷贝,拷贝了l1中对l0的引用,所以l0更改了,l3,l4,l5也会更改 print(l6) #>>> [0, 1, 2, [233, 255], 5] #l6是深拷贝,没有拷贝l1对l0的引用,而是新创建了一份和l0一样的列表,因此没有改变
在pythontutor 执行可以清晰地看懂原理
关于浅拷贝的时候,什么时候传值,什么时候传递引用,好像是可变类型传递引用(?)(tuple深拷贝的时候,传递的还是引用)
另外,要避免可变类型作为函数参数的默认值.(流畅的python p191,还没认真看)
del和垃圾回收
python手册,data model一章中的一句话:对象绝不会自行销毁;然而,无法得到对象时,可能会被当做垃圾回收.
我对这句话的理解是,如果没有变量引用一个对象,那么这个对象就会被回收.而del则是删除变量对对象的引用.
弱引用
流畅的python p196
弱引用不会增加对象的引用数量,但是着实可以访问到对象.对象存在时,可以通过弱引用访问到对象;对象引用数量为0,被回收后,通过弱引用访问到的值为None.
不是每个python对象都可以作为弱引用的目标.如基本的list和dict的实例,不能做弱引用所指对象.而他们的子类可以,如:
1 2 class myList(list): pass
yield
yield是协程的体现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def fab(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 f = fab(5) print(f) #>>> <generator object fab at 0x056AF0F0> print(type(f)) #>>> <class 'generator'> for n in f: print(n,end="|") #>>> 1|1|2|3|5| ``` 私自理解为,yield可以使一个函数先后返回几个不同的值
import requests
def findBiliAidInURL(url: str):
# 在第一个yield前,找出av号然后yield出去
aid = url.split(“av”)[1]
aid = aid.split("/")[0]
yield aid
# 在第二个yield前,通过网络IO请求视频某些信息,然后yield出去
api = f"https://api.bilibili.com/x/web-interface/archive/stat"
url_ = f"https://www.bilibili.com/video/av{aid}"
headers = {
"refer": url_,
"Origin": "https://www.bilibili.com",
"Host": "api.bilibili.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
}
res = requests.get(api, params={"aid": aid}, headers=headers)
res.encoding = "utf8"
yield res.text
if name == ‘main ’:
res = findBiliAidInURL(“https://www.bilibili.com/video/av42684155 ”)
print(res)
# >>> <generator object findBiliAidInURL at 0x0352F0F0>
print(next(res))
# >>> 42684155
print(next(res))
# 在打印出下面这条信息前等待了一个网络IO的时间
# >>> {“code”:0,“message”:“0”,“ttl”:1,“data”:{“aid”:42684155,“view”:129167,“danmaku”:477,“reply”:553,“favorite”:4477,“coin”:7181,“share”:1773,“like”:7452,“now_rank”:0,“his_rank”:0,“no_reprint”:1,“copyright”:1}}
1 2 3 4 ## is 和 == is比较二者内存地址,\=\=比较二者值.与js的\=\=和\=\=\=不同,js的\=\=和\=\=\=的差异在于是否比较等号两边的数据类型.id(x)获取x内存位置 用is判断None:
x = None
x is None
True
import math
a = [x for x in range(10) if math.sqrt(x) > 2]
print(a)
#>>> [5, 6, 7, 8, 9]
#for的时候判断
a = (“a”,“b”,“c”)
b = (“一”,“二”,“三”)
c = [(x,y) for x in a for y in b]
print©
#>>> [(‘a’, ‘一’), (‘a’, ‘二’), (‘a’, ‘三’), (‘b’, ‘一’), (‘b’, ‘二’), (‘b’, ‘三’), (‘c’, ‘一’), (‘c’, ‘二’), (‘c’, ‘三’)]
#列表推导式中多个循环实现笛卡尔积
c = [(x,y) for x in a
for y in b]
#多个循环时加上换行更易读
class A:
def init (self, a: int):
self.x = a
ls = []
for x in range(5):
ls.append(A(int(random.random() * 100)))
ds = {a.x: a.dict for a in ls}
print(ds)
1 2 3 4 ## 生成器表达式 列表推导有个缺点:在推导过程中,对于输入序列中的每个值来说,都要创建一个仅含一项元素的全新列表,当输入数据比较大时容易占内存较大. 生成器表达式则不会有这种问题. 生成器表达式生成一个\<class'generator'\>,通过next(generator)取值.相对列表推导式的优点在于可以按需取用,避免过大占用内存
列表推导式
a = [x for x in range(100)]
print(a)
生成器表达式
b = (x for x in range(100))
try:
while True:
print(next(b),end=",")
except:
pass
a = sum(x for x in range(101))
print(a)
#>>> 5050
#如果生成器表达式是一个函数调用过程中的唯一参数,则不需要额外的括号
a = sum((x for x in range(101)))
print(a)
#>>> 5050
#效果一样的
1 2 ## 可迭代对象 可迭代对象要实现\_\_iter\_\_函数.如果实现了\_\_next\_\_函数,则\_\_iter\_\_函数可return self;否则要return一个iterable,如列表,生成器等
class R:
def init (self):
self.x = 0
def __iter__(self):
return self
#只要实现了__iter__函数,实例自身就是一个iterable.被遍历的时候会调用__next__函数
def __next__(self):
if self.x > 5:
raise StopIteration
self.x += 1
return self.x
for i in R():
print(i)
#>>> 1
2
3
4
5
6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ``` class I: def __init__(self): pass def __iter__(self): return (int(random.random() * 100) for i in range(5)) #返回生成器 for i in I(): print(i) #>>> 33 # 50 # 97 # 65 # 60
对象的格式化输出
1 2 3 4 5 6 7 8 9 10 11 12 class People: def __init__(self,name:str,age:int): self.name = name self.age = age def __format__(self, format_spec): return format_spec.replace("%n",self.name).replace("%a",str(self.age)) p = People("ababa",15) f = format(p,"%n ages %a") print(f) #>>> ababa ages 15
对象的__slots__
定义了__slots__属性的目的是告诉解释器:“这个类中的所有实例属性都在这儿了”,这样,python会在各个实例中使用类似元组的数据结构存储实例变量,从而避免使用消耗内存的__dict__
有数以百万实例存在的时候,使用__slots__可以节省大量内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class A: __slots__ = ("__x") #__slots__是一个iterable,用元组好点 #只需声明实例变量名即可,不用声明方法名. def __init__(self, a: int): self.__x = a def addA(self): self.__x += 1 @property #通过property声明的属性不用写在__slots__里 def x(self): return self.__x @x.setter def x(self, x): self.__x = x @x.getter def x(self): return self.__x a = A(5) print(a.x) #>>> 5 a.x = 6 print(a.x) #>>> 6 a.addA() print(a.x) #>>> 7 print(vars(a)) #>>> TypeError: vars() argument must have __dict__ attribute #不能使用vars()或者__dict__获得其属性了.
有需求的话,也可以在__slots__里写上"dict ",实例也会在__dict__里存储属性.不过这样做就达不到
装饰器
装饰器能在被装饰的函数执行之前和之后分别执行一些代码,可以做到访问及修改原函数的参数和返回值,以实现约束语义,调试函数,注册函数等目标
装饰器是一个函数,接受被装饰的函数做参数,里面定义一个新的函数通常是在新的函数里执行一次被装饰的函数,然后返回新定义的函数.
1 2 3 4 5 6 7 8 9 10 11 12 def aDecorator(aFn): def aWapper(): return "-------" + aFn() + "----------" return aWapper @aDecorator def a(): return "aaa" s = a() print(s) #>>> -------aaa----------
参数化装饰器
参数化的装饰器其实不是装饰器,而是装饰器的工厂函数,返回一个装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #装饰器函数工厂函数,接受参数 def aDecorateFactory(anArg="nothing"): #装饰器函数,接受被装饰的函数做参数 def decorate(func): #装饰后的函数,接受任意参数以传给被装饰的函数 def retfunc(*args): #执行装饰器附加代码 print(anArg,end="") res = func(*args) #执行被装饰的函数,装饰后的函数返回其执行结果 return res return retfunc #装饰器函数返回装饰后的函数 return decorate #工厂函数返回装饰器 @aDecorateFactory("something") def printsth(sth): print(sth) printsth("666") #>>> something666
装饰器的问题
修饰器返回了一个新的函数,并非原函数.两者在使用之外的地方可能会有不同.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def addA(s:str)->str: return s + "aaaaaaa" print(addA) #>>> <function addA at 0x048ACFA8> print(addA.__name__) #>>> addA def aDecorator(func): def aWapper(*args,**kwargs): res = func(*args,**kwargs) print(res) return res return aWapper @aDecorator def addA2(s:str)->str: return s + "aaaaaaa" print(addA2) #>>> <function aDecorator.<locals>.aWapper at 0x048D3030> print(addA2.__name__) #>>> aWapper
这个问题可以用functools模块的@functools.wraps()修饰器解决.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import functools def aDecorator2(func): @functools.wraps(func) #重点在这行 def aWapper(*args,**kwargs): res = func(*args,**kwargs) print(res) return res return aWapper @aDecorator2 def addA3(s:str)->str: return s + "aaaaaaa" print(addA3) #>>> <function addA3 at 0x045B2108> print(addA3.__name__) #>>> addA3
标准库中的装饰器:
@property
廖雪峰教程 | effective python 第84页
读取或修改对象的属性时,可以执行其他操作.可用于赋值的验证.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Candies: def __init__(self): self._peoples = 1 self._candies = 10 self._candiesPerple = 10 @property def peoples(self): return self._peoples @peoples.setter def peoples(self,peoples): self._peoples = peoples self._candiesPerple = self._candies // self._peoples print("设置peoples") @peoples.getter def peoples(self): print("读取peoples") return self._peoples c = Candies() c.peoples = 5 #>>> 设置peoples p = c.peoples #>>> 读取peoples print(p) #>>> 5
@staticmethod和@classmethod
Python类中有3个方法,即静态方法(staticmethod),类方法(classmethod)和实例方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 def fn(x): print(f"executing fn({x})") class A(object): # 实例方法,参数带有self def fn(self,x): print(f"executing fn({self},{x})") # 类方法,参数带有cls,感觉是拿来更改类变量的 @classmethod def class_fn(cls,x): print(f"executing class_fn({cls},{x})") # 静态方法 @staticmethod def static_foo(x): print(f"executing static_foo({x})") a=A() a.fn(3) #>>> executing fn(<__main__.A object at 0x057A7AD0>,3) a.class_fn(4) #>>> executing class_fn(<class '__main__.A'>,4) a.static_foo(5) #>>> executing static_foo(5) A.fn(6) # 报错 A.fn(7,8) #>>> executing fn(7,8) # 实际是把7传给self,self作为一个普通的参数 A.class_fn(9) #>>> executing class_fn(<class '__main__.A'>,9) A.static_foo(10) #>>> executing static_foo(10)
类方法可以拿来做备选的构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class A: def __init__(self,a): self.x = a @classmethod def from10(cls): return cls(10) a = A(5) print(a.__dict__) #>>> {'x': 5} b = A.from10() print(b.__dict__) #>>> {'x': 10}
见functools.lru_cache
见装饰器的问题
函数注释
python文档
就是对函数参数和返回值的注释.
1 2 3 4 def f(a : str, b : "b is a string", c : int = 5) -> str: return str(a) + str(b) + str(c) print(f(123,"sss")) #>>> 123sss5
时间计算
参考
python包发布
python包发布
官方文档
pickle
python文档
自己对其的理解,限于可以将python对象存储在硬盘中,日后可读取
较实用的python内置方法
divmod(num,num)
把除法和求余结合起来,返回a // b, a % b
enumerate(list)
遍历列表,同时列出下标
all(iterable)
一个iterable全部元素为True,则返回True,否则返回False
any(iterable)
一个iterable全部元素为False,则返回False,否则返回True
repr(object)
将对象转化为供解释器读取的形式。
本人理解:repr将对象转化为供程序 理解的字符串,str将对象转化为供人类 理解的字符串
1 2 3 4 5 6 i = "my name is sqh\ni age 20" print(str(i)) #>>> my name is sqh i age 20 print(repr(i)) #>>> 'my name is sqh\ni age 20'
1 2 3 4 5 6 7 8 9 10 11 # 对于dict和list i = {"name":"sqh","age":20} print(i) #>>> {'name': 'sqh', 'age': 20} print(str(i)) #>>> {'name': 'sqh', 'age': 20} print(repr(i)) #>>> {'name': 'sqh', 'age': 20} print(repr(i)[0]) #>>> { # repr后转换为字符串,打印字符串的0位置的字符
map(function,iterable)
map() 会根据提供的函数对指定序列做映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 a = [ {"id": 0, "name": "foo"}, {"id": 1, "name": "bar"} ] b = map(lambda x: x["name"], a) print(b) # 返回迭代器 #>>> <map object at 0x02D3E150> print(list(b)) #>>> ['foo', 'bar'] import math c = [2,3,4] d = map(math.sqrt,c) print(d) #>>> <map object at 0x00336B50> print(list(d)) #>>> [1.4142135623730951, 1.7320508075688772, 2.0] print("\n".join(map(lambda a:" | ".join(map(lambda b:f"{a} * {b} = {a * b}",range(1,a + 1))),range(1,10)))) #一行输出九九乘法表
tuple(iterable)
tuple() 函数将列表(?)转换为元组
1 2 3 4 5 6 7 8 9 10 11 12 l = ["a", "b", "c"] print(tuple(l)) #>>> ('a', 'b', 'c') d = { "k1": "v1", "k2": "v2", "k3": "v3" } print(tuple(d)) #>>> ('k1', 'k2', 'k3') #dict 返回dict的键
filter(function,list)
筛选list的元素
1 2 3 4 5 6 import math l = filter(lambda x:not math.sqrt(x) % 1, range(1, 101)) print(l) #>>> <filter object at 0x01B66B50> print(list(l)) #>>> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
vars(object)
vars函数返回对象object的属性和属性值的字典对象。实现方式好像是 return obj.__dict__?(所以还是直接obj.__dict__吧…)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class A: x = 1 def __init__(self): self.y = 2 def __getattribute__(self, item): print(f"""try to accept to '{item}'""") return super().__getattribute__(item) a = A() print(vars(a)) #>>> try to accept to '__dict__' #>>> {'y': 2} #只返回object的属性
reversed(list)
倒序列表
1 2 3 4 5 6 7 8 9 10 11 12 a = ["a", "s", "d", "f", "g"] print(list(reversed(a))) #>>> ['g', 'f', 'd', 's', 'a'] #reversed返回一个迭代器 a.reverse() print(a) #>>> ['g', 'f', 'd', 's', 'a'] # a.reverse()直接将a倒序,而不是a = a.reverse() #但是感觉没必要啊...切片就好了 print(a[::-1])
zip()
这玩意不知怎么描述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 a = "qwertyui" b = "asdfghjk" c = "zxcvbnm," print(list(zip(a,b,c))) #>>> [('q', 'a', 'z'), ('w', 's', 'x'), ('e', 'd', 'c'), ('r', 'f', 'v'), ('t', 'g', 'b'), ('y', 'h', 'n'), ('u', 'j', 'm'), ('i', 'k', ',')] #就是组成一个个元组 d = [ [100,200,300], [40,50,60], [7,8,9,1,1,1,1,1,1,1,1,1] ] print(list(zip(*d))) #>>> [(100, 40, 7), (200, 50, 8), (300, 60, 9)] #将每三个数组成一个元组,可以后续传递给函数
set(iterable)
set()函数创建一个无序不重复元素集,可进行关系测试,删除重复数据,还可以计算交集,差集,并集等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 a = [1,1,2,3,4,5,1,6] print(a) print(set(a)) #>>> {1, 2, 3, 4, 5, 6} #可以去重 b = [4,5,6,9,8,7,18,29,38] # print(a|b) #>>> TypeError: unsupported operand type(s) for |: 'list' and 'list' print(set(a)|set(b)) #>>> {1, 2, 3, 4, 5, 6, 7, 8, 9, 38, 18, 29} c = "qwe" d = "asd" # print(c|d) #>>> TypeError: unsupported operand type(s) for |: 'str' and 'str' print(set(c)|set(d)) #>>> {'a', 's', 'd', 'w', 'e', 'q'}
slice
创建一个切片对象,经常用到的切片参数可以用slice存给一个变量
1 2 3 4 s = slice(None, None, -1) a = "qwer" print(a[s]) #>>> rewq
reduce(function,iterable,initial)
function接受两个参数,reduce首先把iterable的前两个元素传给函数参数,函数加工后,再把返回值和第三个元素作为两个参数传给函数参数,以此类推
1 2 3 4 5 6 7 8 9 10 from functools import reduce l = [1, 2, 3, 4, 5, 6, 7, 8, 9] a = reduce(lambda x,y:x*10 + y,l) print(a) #>>> 123456789 b = reduce(lambda x,y:x*10 + y,l,7) print(b) #>>> 7123456789 #如果传入了initial参数,则第一次调用function的时候,是先将initial和iterable第一个元素作为参数
partial
将函数的第一个参数冻结住
1 2 3 r = functools.partial(range,10) print(list(r(20))) >>> [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
operator
operator模块为多个算术运算符提供了对应的函数,从而避免lambda a, b: a * b这种平凡的匿名函数.
比如,operator.mul是乘法函数
1 2 3 4 5 6 import operator import functools def fact(n): return functools.reduce(operator.mul, range(1, n + 1)) return functools.reduce(lambda a, b: a * b, range(1, n + 1)) #两行效果一样
操作
表达式
函数
加法
a + b
add(a, b)
减法
a - b
sub(a, b)
乘法
a * b
mul(a, b)
除法
a / b
div(a, b)
整除
a // b
floordiv(a, b)
取余
a % b
mod(a, b)
乘方
a ** b
pow(a, b)
正运算
+ a
pos(a)
负运算
- a
neg(a)
取绝对值运算
abs(a)
abs(a)
按位与
a & b
and_(a, b)
按位或
a | b
or_(a, b)
按位异或
a ^ b
xor(a, b)
按位取反
~a
invert(a)
左移位
a << b
lshift(a, b)
右移位
a >> b
rshift(a, b)
小于比较
a < b
lt(a, b)
小于等于比较
a <= b
le(a, b)
等于比较
a == b
eq(a, b)
不等于比较
a != b
ne(a, b)
大于等于比较
a >= b
ge(a, b)
大于比较
a > b
gt(a, b)
算了,不总结了,随用随查…
itemgetter()
序列,dict元素的获取
1 2 3 4 5 6 7 8 a = ["a","b","c"] b = [2,5,7,4,8,9,6] g = operator.itemgetter(1) print(g(a)) #>>> b print(g(b)) #>>> 5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ls = [ [8, 5, 4, 7], [0, 1, 2, 3], [6, 3, 1, 5, 4], [2, 8, 7, 4] ] print(ls) #>>> [[8, 5, 4, 7], [0, 1, 2, 3], [6, 3, 1, 5, 4], [2, 8, 7, 4]] ls.sort(key=operator.itemgetter(0)) ls.sort(key=lambda l: l[0]) # 效果一样 print(ls) #>>> [[0, 1, 2, 3], [2, 8, 7, 4], [6, 3, 1, 5, 4], [8, 5, 4, 7]]
1 2 3 4 5 6 7 8 9 ls2 = [ {"q":5}, {"q":8}, {"q":4}, {"q":7} ] ls2.sort(key=operator.itemgetter("q")) print(ls2) #>>> [{'q': 4}, {'q': 5}, {'q': 7}, {'q': 8}]
attrgetter()
对象属性的获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class A: def __init__(self): self.x = int(random.random() * 100) ls = [A() for i in range(3)] for l in ls: print(l.__dict__,end="") #>>> {'x': 95}{'x': 89}{'x': 89} ls.sort(key=operator.attrgetter("x")) ls.sort(key=lambda a:a.x) #效果一样 print("\n",end="") for l in ls: print(l.__dict__,end="") #>>> {'x': 89}{'x': 89}{'x': 95}
lru_cache
据说是非常实用的装饰器,实现了备忘功能.这是一项优化技术,把耗时的函数的结果保存起来,避免传入相同的参数时重复计算.lru是"latest recently used"的缩写,表明缓存不会无限制增长,一段时间内不用的缓存条目会被扔掉.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 def fibonacci(n): t = time.perf_counter() if n < 2: res = n else: res = fibonacci(n - 2) + fibonacci(n - 1) t1 = time.perf_counter() - t print(f"function fibonacci({n}) has excuted in {t1}s") return res fibonacci(4) #>>> function fibonacci(0) has excuted in 3.950615723213543e-07s # function fibonacci(1) has excuted in 0.0s # function fibonacci(2) has excuted in 3.950615723213542e-05s # function fibonacci(1) has excuted in 0.0s # function fibonacci(0) has excuted in 3.950615723213573e-07s # function fibonacci(1) has excuted in 3.950615723213505e-07s # function fibonacci(2) has excuted in 1.540740132053282e-05s # function fibonacci(3) has excuted in 2.8444433207137508e-05s # function fibonacci(4) has excuted in 8.414811490444843e-05s #拿functools.lru_cache修饰后,函数调用次数减少 @functools.lru_cache() def fibonacci(n): t = time.perf_counter() if n < 2: res = n else: res = fibonacci(n - 2) + fibonacci(n - 1) t1 = time.perf_counter() - t print(f"function fibonacci({n}) has excuted in {t1}s") return res fibonacci(4) #>>> function fibonacci(0) has excuted in 0.0s # function fibonacci(1) has excuted in 3.950615723213505e-07s # function fibonacci(2) has excuted in 5.9259235848203126e-05s # function fibonacci(3) has excuted in 1.5802462892854156e-06s # function fibonacci(4) has excuted in 9.362959264016094e-05s
使用lru_cache()时要加括号,因为其可以接受两个可选参数.
1 functools.lru_cache(maxsize=128,typed=false)
maxsize参数置顶存储多少个调用结果.为了得到最佳性能,maxsize应该设为2的幂.typed设为True,则把不同类型的参数得到的结果分开保存
singledispatch
单分派反函数
该装饰器可以把整体方案拆分成多个模块,使用@singledispatch装饰的普通函数会变成泛函数:根据第一个参数的类型,以不同方式执行相同操作的一组操作(因为是单分派,所以根据第一个参数的类型,否则要是根据多个参数选择专门的函数就是多分派了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @functools.singledispatch def str2list(obj): pass @str2list.register(str) #各个专门函数用@{base_function}.register({type})装饰. def _(aStr): #函数名无所谓,"_"占位是个不错的选择 return list(aStr) @str2list.register(int) def _(anInteger): return list(str(anInteger)) @str2list.register(tuple) @str2list.register(list) #指定多个类型 def _(anIterable): return anIterable print(str2list(123)) #>>> ['1', '2', '3'] print(str2list("abc")) #>>> ['a', 'b', 'c'] print(str2list([",",".","/"])) #>>> [',', '.', '/']
collections
流畅的python p65,暂不研究
上下文管理器
可以理解为,上下文管理器是一个特殊的类,包含__enter__和__exit__两个特殊方法.进入with里的语句时,执行上下文管理器的__enter__方法,离开时执行__exit__方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from datetime import datetime class timePrint: def __enter__(self): global print self.rawPrint = print print = self.timePrint def timePrint(self,*args,**kwargs): timeStr = datetime.now().strftime("[%Y-%m-%d %H:%M:%S]") self.rawPrint(timeStr,*args,**kwargs) def __exit__(self, exc_type, exc_val, exc_tb): global print print = self.rawPrint if __name__ == '__main__': with timePrint(): # 进入上下文管理器timePrint时执行__enter__(),print函数会被替换 print(1) >>> [2019-02-08 02:18:07] 1 # 离开时,执行__exit__(),print函数会换回来 print(2) >>> 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # 这里是一个简陋的类似python自带的open上下文管理器的myopen上下文管理器. # 与上面的例子区别在于,这个上下文管理器在构建时有传入参数,且__enter__函数有返回值 class myOpen(): # 构建上下文管理器时,传入的参数还是传给__init__函数而非__enter__函数 def __init__(self, filePath): self.filePath = filePath def __enter__(self): self.__F = open(self.filePath, "r", encoding="utf8") return self.__F def __exit__(self, exc_type, exc_val, exc_tb): self.__F.close() if __name__ == '__main__': # 会将上下文管理器的__enter__的返回值传给as后面的变量 with myOpen("0.txt") as f: print(f.read()) # >>> here's content of 0.txt
@contexmanager修饰器
@contexmanager修饰器能减少创建上下文管理器需要的样板代码量.
不用再实现一个类,定义__enter__和__exit__甚至__init__方法,只需实现一个有yield语句的生成器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from contextlib import contextmanager # contextmanager在contextlib中 @contextmanager def myOpen2(filePath): # 定义一个函数而非一个类 # 构建上下文管理器时需要传给上下文管理器的参数直接传给本函数 __F = open(filePath, "r", encoding="utf8") # yield前做__enter__函数需要做的事 # yield的值为__enter__函数返回的值 yield __F # yield后做__exit__函数需要做的事 __F.close() if __name__ == '__main__': with myOpen2("0.txt") as f: print(f.read()) # >>> here's content of 0.txt # 与上面的例子一致