基本数据结构
序列分解为单独变量
对于一个可分解对象(迭代器,生成器,列表,元组,集合),我们可以直接用变量去接收里面的值
1 2 3 4 5 6 7 8 9 10 l = [1 ,2 ,3 ,4 ,5 ] a,b,c,d,e = l _ , a , *_ = l print (a)
如果解压出来的数据里面又包含序列,我们还想解压的话,必须使用()包裹起来
a = [1,(2,3)]
b,(c,d) = a
举例实用性
去除一个列表的最高值和最低值,求中间值的平均值
1 2 3 4 5 def average (socres ): first,*mid,last = socres return sum (mid) / len (mid) print (average([5 ,4 ,3 ,2 ,1 ]))
保留最后N个元素
我们希望我们的队列中只保留最后N个元素,当元素超过N的时候就把前面的删掉,我们可以用队列实现
1 2 3 4 5 6 7 from collections import dequequeue = deque(maxlen=5 ) queue.extend([1 ,2 ,3 ,4 ,10 ,6 ,7 ]) for i in queue: print (i)
注意 append 和 extend 的区别 ,append是添加一个元素,extend是把后面这个可迭代对象拆开放进去
列表适合做栈,不适合做队列,它的出队列是o(N)时间复杂度
获得最大或者最小的N个元素
从可迭代数据中或者最大或者最小的N个元素
1 2 3 4 5 6 7 8 9 10 11 12 13 import heapqimport pprintportfolio = [ {'name' : 'IBM' , 'shares' : 100 , 'price' : 91.1 }, {'name' : 'AAPL' , 'shares' : 50 , 'price' : 543.22 }, {'name' : 'FB' , 'shares' : 200 , 'price' : 21.09 }, {'name' : 'HPQ' , 'shares' : 35 , 'price' : 31.75 }, {'name' : 'YHOO' , 'shares' : 45 , 'price' : 16.35 }, {'name' : 'ACME' , 'shares' : 75 , 'price' : 115.65 } ] pprint.pprint(heapq.nsmallest(2 ,portfolio,lambda x:x['price' ])) pprint.pprint(heapq.nlargest(2 ,portfolio,lambda x:x['price' ]))
heapq的使用
1 2 3 4 5 6 import heapqnums = [10 ,34 ,1 ,3 ,5 ] heapq.heapify(nums) print (heapq.heappop(nums)) heapq.heappush(nums,20 ) print (nums)
字典中的键映射多个值
其实就是利用列表这些数据结构当成键对应的值,然后可以往这个列表中多添加些数据,其实还是只能一个键对应一个值
我们可以自己这样
1 2 3 4 5 a = {} a.setdefault("name" ,[]).append("djm" ) a.setdefault("name" ,[]).append("nb" ) print (a)
当然,看着这么多代码还是有点不舒服的,我们就用python提供的 defaultdict吧
1 2 3 4 5 6 from collections import defaultdicta = defaultdict(list ) a["name" ].append("djm" ) a["name" ].append("nb" ) print (a)
控制字典顺序
当我们遍历字典的时候,发现出来的数据并不是我们原先添加进去的顺序,因为这个用的是hash算法来放位置的,如果我们想要按添加时候的顺序来遍历数据就要用 orderdict ,它的底层多了一个双向链表,这也就意味着内存的增加,所以我们要酌情使用
1 2 3 4 5 6 7 8 9 10 from collections import OrderedDictd = OrderedDict() d['foo' ] = 1 d['bar' ] = 2 d['spam' ] = 3 d['grok' ] = 4 for key in d: print (key, d[key])
切片命名
如果对于一个固定的切片大小,而且要经常使用,我们不妨使用slice 函数来生成切片序列
1 2 3 4 5 s = slice (1 ,-1 ,2 ) a = [1 ,2 ,3 ,4 ,5 ,6 ] b = [2 ,3 ,4 ,5 ,6 ] print (a[s])print (b[s])
counter
这个类可以用来对一个存放有可hash的对象的可迭代的列表进行计数,厉害的是它还可以进行数据运算,还有其他的强大的功能,比我们自己用dict 去计数要方便很多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from collections import Counterimport randoma = [int (random.random()*3 ) for i in range (5 )] b = [int (random.random()*3 ) for i in range (5 )] print (a) print (b) ca = Counter(a) cb = Counter(b) print (ca) print (cb) print (ca.most_common(2 )) print (cb.most_common(2 )) print (ca-cb) print (ca+cb)
提供关键字排序函数
在排序的时候,我们有时候会按照一些自定义规则排序,或者是使用关键字排序,我们可以使用lambda,也可以使用python提供的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from operator import attrgetter,itemgetterfrom pprint import pprintrows = [ {'fname' : 'Brian' , 'lname' : 'Jones' , 'uid' : 1003 }, {'fname' : 'David' , 'lname' : 'Beazley' , 'uid' : 1002 }, {'fname' : 'John' , 'lname' : 'Cleese' , 'uid' : 1001 }, {'fname' : 'Big' , 'lname' : 'Jones' , 'uid' : 1004 } ] class User : def __init__ (self, user_id ): self.user_id = user_id def __repr__ (self ): return 'User({})' .format (self.user_id) users = [User(23 ), User(3 ), User(99 )] pprint(sorted (rows,key=itemgetter("lname" ))) print (sorted (users, key=attrgetter("user_id" )))
根据某个字段分组
有时候我们希望根据某个字段分组,然后依次遍历这个分组,这是非常使用的一个东西,如果让我们自己来遍历的话,必须得先定义一个变量比较是否属于一个分组,这样太麻烦了,我们看看python提供的groupby函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from itertools import groupbyfrom operator import itemgetterrows = [ {'address' : '5412 N CLARK' , 'date' : '07/01/2012' }, {'address' : '5148 N CLARK' , 'date' : '07/04/2012' }, {'address' : '5800 E 58TH' , 'date' : '07/02/2012' }, {'address' : '2122 N CLARK' , 'date' : '07/03/2012' }, {'address' : '5645 N RAVENSWOOD' , 'date' : '07/02/2012' }, {'address' : '1060 W ADDISON' , 'date' : '07/02/2012' }, {'address' : '4801 N BROADWAY' , 'date' : '07/01/2012' }, {'address' : '1039 W GRANVILLE' , 'date' : '07/04/2012' }, ] rows.sort(key=itemgetter("date" )) groups = 0 for date, items in groupby(rows,key=itemgetter("date" )): groups += 1 print (f'第{groups} 分组的内容为:' ) for item in items: print (item)
记得先排好序
将下标序列变成命名序列
有时候我们通过下标访问序列的方式让代码看起来很不清晰,不知道这个下标表示什么意思,我们不妨将这个序列变成命名分组,这样我们可以通过.名字的方法访问数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from collections import namedtupleStudents = [ ["djm" ,10 ,20 ], ["dd" ,80 ,20 ], ["aa" ,10 ,90 ], ["cc" ,100 ,20 ], ] for student in Students: print (student[0 ],":" ,student[1 ]+student[2 ]) Student = namedtuple("Student" ,["name" ,"chinese" ,"math" ]) for student in Students: student = Student(*student) print (student.name,":" ,student.chinese+student.math)
这种方法是不是可以让代码看起来更直观
记住这里生成的对象里面的数据是不可以变的,它这个对象跟元组差不多,都是不可变对象,如果非要更改,必须使用_replace方法生成一个新的对象
合并多个字典
现在有多个字典或者映射,你想将它们从逻辑上合并为一个单一的映射后执行某些操作, 比如查找值或者检查某些键是否存在。其实也就是用个链表将多个dict连接起来,查找的时候从当前一直向后查找
1 2 3 4 5 6 7 8 9 from collections import ChainMapa = {'x' :1 ,'y' :2 } b = {'x' :2 ,'z' :3 } m = ChainMap(a,b) print (m["x" ]) print (m["z" ]) m["u" ] = 10 print (m)
二分查找库
bisect
模块提供了以下两个主要函数:
bisect_left(a, x, lo=0, hi=len(a))
:在已排序的列表a中查找元素x应该插入的位置,返回值是一个整数,表示x应该插入的位置。如果x已经存在于a中,则返回与之相等的元素的左侧位置。lo
和hi
参数用于指定查找范围,默认是整个列表a。
bisect_right(a, x, lo=0, hi=len(a))
:与bisect_left()
函数类似,但如果x已经存在于a中,则返回与之相等的元素的右侧位置。
除此之外,bisect
模块还提供了以下两个函数,用于在已排序的序列中插入元素:
insort_left(a, x, lo=0, hi=len(a))
:将元素x插入到已排序的列表a中,并保持列表的升序排列。
insort_right(a, x, lo=0, hi=len(a))
:与insort_left()
函数类似,但如果x已经存在于a中,则将x插入到相等元素的右侧。
字符串
指定分隔符分割字符串
我们可以用字符串提供的split方法 以及 更强大的re.split方法,前者只能指定一个分隔符
1 2 3 4 5 6 7 8 9 10 11 12 13 import reline = 'asdf fjdk; afed, fjek,asdf, foo' print (line.split(';' )) print (line.split(',;' )) print (re.split(r'[ ,;]+' ,line)) print (re.split(r'([ ,;]+)' ,line))
多个开头或者结尾匹配
如果我们要看一个字符串的开头或者结尾是否满足一群要求,我们可以在endswith 或者 startswith中传递一个元组
1 2 3 4 5 url = "http://www.baidu.com" def ishttp (url ): return url.startswith(("http" ,"https" )) print (ishttp(url))print (ishttp("https://www.baidu.com" ))
使用通配符匹配字符串
就像用shell里面的通配符去匹配字符串一样,我们需要使用fnmatch模块
1 2 3 4 5 import fnmatchprint (fnmatch.fnmatch("A.TXT" ,"*.txt" )) print (fnmatch.fnmatchcase("A.TXT" ,"*.txt" ))
将将字符串变量变成原始字符串
有时候我们希望不要进行转义,就要原始字符串,有两种方法,如果是在定义的时候,可以用r加在前面,如果是一个变量,我们可以用repr函数
1 2 3 a = '\n' a = repr (a) a = r'\n'
字符串的替换
有两种方法,一个是字符串自带的replace方法,还有一个就是re模块的sub方法,这个方法比较强大
replace定义
re.sub函数定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import retext = 'yeah, but no, but yeah, but no, but yeah' print (text.replace("yeah" ,"yep" )) print (text.replace("yeah" ,"yep" ,2 )) text = 'now is 3/17/2023, now is 3/17/2023' print (re.sub(r"(\d{1,2})/(\d{1,2})/(\d{4})" ,r'\3-\2-\1' ,text)) print (re.sub(r"(\d{1,2})/(\d{1,2})/(\d{4})" ,r'\3-\2-\1' ,text,1 )) def func (m ): print (m) return f"{m.group(3 )} -{m.group(2 )} -{m.group(1 )} " print (re.sub(r"(\d{1,2})/(\d{1,2})/(\d{4})" ,func,text)) print (re.subn(r"(\d{1,2})/(\d{1,2})/(\d{4})" ,r'\3-\2-\1' ,text))
字符与数字的相互转换
我们有时候需要使用一个字符的ASCII码对应的数字,也需要将一个数字对应的字符
使用 ord 可以将一个字符变成数字, chr可以将数值变成字符
1 2 print (chr (69 )) print (ord ('D' ))
字符串的拼接
对于少量的字符串我们可以使用 + 连接起来, 对应很多的字符串拼接我们可以使用 ‘’.join这样会快很多, join方法是将调用者为分割符,连接所有字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import timea = "10" b = "20" n = 1000000 start = time.time() s = "" for i in range (n): s += a+b end = time.time() print (f"+拼接字符串耗时{end-start} s" ) start = time.time() s = "" s = s.join(a+b for i in range (n)) end = time.time() print (f"join拼接字符串耗时{end-start} s" )
当n的不多的时候,两者差不多,当n比较大的时候差距就明显了
字符串与字节的转换
我们在文件操作的时候,通常要用到字符串和字节的转换
数字 日期 时间
浮点数的精确运算
我们都知道,使用float变量计算是肯定有误差的,我们需要使用decimal模块
1 2 3 4 5 6 from decimal import Decimalprint (4.2 +2.1 ) num = Decimal("4.2" ) + Decimal("2.1" ) print (num)
二进制八进制十六进制十进制转化
我们可以将十进制数据变成对应进制的字符串,也可以用int函数将对应的进制字符串变成10进制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 x = 100 print (bin (x)) print (oct (x)) print (hex (x)) print (format (x,'b' ))print (format (x,'o' ))print (format (x,'x' ))print (0B1111 ) print (0O1111 ) print (0X1111 ) print (int ("1111" ,2 )) print (int ("1111" ,8 )) print (int ("1111" ,16 ))
字节与整数的相互转换
你有一个字节字符串并想将它解压成一个整数。或者,你需要将一个大整数转换为一个字节字符串。
调用整数提供的方法
1 2 3 4 5 a =-128 print (a.to_bytes(4 ,"big" ,signed=True )) print (int .from_bytes(b'\xff\xff\xff\x80\x80' ,"big" ,signed=True ))
我们可以通过bit_length() 来获得需要的字节数量
无穷大和NaN
1 2 3 4 5 6 nan = float ("Nan" ) inf = float ("inf" ) finf = float ("-inF" ) print (nan)print (inf)print (finf)
无穷大数在执行数学计算的时候会传播
NaN值会在所有操作中传播,而不会产生异常
NaN值的一个特别的地方时它们之间的比较操作总是返回False
测试一个NaN值得唯一安全的方法就是使用 math.isnan()
大型数组运算
当我们有大量的数组数据需要计算的时候,我们可以用Numpy这个库,这个库比我们用循环快很多
1 2 3 4 5 6 7 8 9 10 11 12 13 import numpy as npa = np.array([1 ,2 ,3 ,4 ,5 ]) a[1 ] = 10 b = np.array([4 ,5 ,6 ,7 ,8 ]) print (type (a)) print (a+10 ) print (a*2 ) a = [[1 ,2 ,3 ],[4 ,5 ,6 ]] a = np.array(a) print (a[1 ,1 ])
numpy有很多厉害的功能,这里就不介绍了,上面这些都只是数组的运算,而不是矩阵的运算,我们得用matrix去产生矩阵
随机选择
从一个序列中随机抽取若干元素,或者想生成几个随机数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import randoma = [1 ,2 ,3 ,4 ,5 ] print (random.choice(a))print (random.choices(a,k=3 ))print (random.sample(a,3 ))random.shuffle(a) print (a)print (random.randint(10 ,20 ))print (random.random())
日期与时间的转换
有时候需要得到时间信息,以及时间之间的转换,我们可以用datetime模块,这个模块可以自己处理闰年
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 import datetimefivedays = datetime.timedelta(days=5 ) tendays = 2 *fivedays print (tendays.days) print (tendays.seconds) print (tendays.total_seconds()) datea = datetime.datetime(2002 ,4 ,5 ) dateb = datetime.datetime(2001 ,4 ,5 ) subdate = dateb-datea print ((datea-dateb).days) print ((datea-dateb).seconds) datec = datea + fivedays print (datec) datec = datetime.datetime(2023 ,3 ,17 ) print (datec.year)print (datec.month)print (datec.day)print (datec.weekday()) now = datetime.datetime.now() prefive = now-datetime.timedelta(7 -(now.weekday()-4 )) print (prefive.date())
如果涉及大量的日期时间计算,推荐使用第三方包,calendar是用来打印日历这些的
迭代器和生成器
使用生成器代替迭代器
如果用迭代器的话,我们需要自己去维护很多状态,而使用生成器,就方便很多了,它会自己保存好局部变量
我们来对比一下吧
自己实现迭代器
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 class Node : def __init__ (self,value ) -> None : self._value = value self.left = None self.right = None def setleft (self,node ): self.left = node def setright (self,node ): self.right = node def __iter__ (self ): return helpIterator(self) def __str__ (self ) -> str : return f"node({self._value} )" class helpIterator : def __init__ (self,node ) -> None : self.node = node self.leftiter = None self.rightiter = None def __iter__ (self ): return self def __next__ (self ): if self.node == None : raise StopIteration if self.leftiter is None : self.leftiter = iter (helpIterator(self.node.left)) return self.node elif self.rightiter is None : try : value = next (self.leftiter) return value except StopIteration as s: self.rightiter = iter (helpIterator(self.node.right)) return next (self) else : return next (self.rightiter) root = Node(10 ) root.setleft(Node(11 )) root.setright(Node(12 )) for val in root: print (val)
使用生成器生成迭代器
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 class Node : def __init__ (self,value ) -> None : self._value = value self.left = None self.right = None def setleft (self,node ): self.left = node def setright (self,node ): self.right = node def __iter__ (self ): yield self if self.left is not None : yield from iter (self.left) if self.right is not None : yield from iter (self.right) def __str__ (self ) -> str : return f"node({self._value} )" root = Node(10 ) left = Node(11 ) right = Node(14 ) root.setleft(left) root.setright(right) left.setleft(Node(12 )) left.setright(Node(13 )) right.setleft(Node(15 )) right.setright(Node(16 )) for val in root: print (val)
对比一下,真是节省了不少代码啊,太牛了
反向迭代
反向迭代必须实现__reversed()__方法,它的返回值也必须是一个具有__next()__方法的对象
1 2 3 4 5 6 7 8 9 class test : def __iter__ (self ): for i in range (10 ): yield i def __reversed__ (self ): for i in range (10 ,0 ,-1 ): yield i print (list (reversed (test())))
islice
我们可能只需要迭代器的一部分数据,那么我们可以使用itertools.islice方法
1 2 3 4 5 6 7 8 9 10 11 import itertoolsdef testiter (): n = 0 while (True ): yield n n += 1 it = testiter() print (list (itertools.islice(it,10 ,20 )))
dropwhile
跳过某些符合要求的的数据直到一个不符合要求的数据
1 2 3 4 5 6 7 8 9 10 import itertoolsdef testiter (): a = [i for i in range (20 )] yield from a it = testiter() print (list (itertools.dropwhile(lambda x:x<5 ,it)))
排列与组合
permutations 可以进行排列 combinations可以进行组合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import itertoolsl = [1 ,2 ,2 ] for i in itertools.permutations(l,3 ): print (i) for i in itertools.combinations(l,2 ): print (i) for i in itertools.combinations_with_replacement(l,2 ): print (i)
要注意他们的区别哦 另外如果元素里面有相同的数据,使用这个方法得到的结果并不正确
迭代索引
我们发现使用迭代器并没有记录我们当前是第几个,如果我们需要索引可以使用enumerate函数,这个函数使用起来就很优雅了,我们不需要额外定义一个变量去计数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 for index,num in enumerate (range (10 ,0 ,-1 ),1 ): print (f"第{index} 个数:{num} " )
同时迭代多个序列
我们可以使用zip函数同时迭代多个序列,但是迭代次数以最短的为主
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 for i,j in zip (range (10 ),range (11 ,0 ,-1 )): print ((i,j)) import itertoolsfor i,j in itertools.zip_longest(range (10 ),range (11 ,0 ,-1 )): print ((i,j))
连续迭代不同集合
如果我们对不同的集合里面的数据有相同操作,我平时肯定是写两段相同的代码,或者是拼接起来,这样都不是最好的选择,我们可以使用chain
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 a = [1 ,2 ,3 ,4 ,5 ] b = [1 ,2 ,3 ,4 ,5 ] sums = 0 for i in a: sums += i for i in b: sums += i print (sums)print (sum (a+b))import itertoolsprint (sum (itertools.chain(a,b)))
chain 比加法要快,而且使用加法对于集合,有时候可不是那么友好,毕竟有去重功能
按顺序连续迭代不同序列
如果我们有两个序列,他们都是有序的,但是我们想将他们合并然后使用,如果让我们自己来,肯定还要判断大小,我们不妨使用heapq.merge
1 2 3 4 5 import heapqa = [1 ,4 ,6 ,8 ,9 ] b = [2 ,3 ,8 ,10 ,11 ] print (list (heapq.merge(a,b)))
使用迭代器代替while
使用iter函数,如果所给参数不是一个可迭代对象而是一个函数的话,那么会一直调用这个函数,直到返回值满足要求
1 2 print (sum (map (int ,iter (input ,"quit" ))))
文件与io
其实对于文件读写的文本模式与二进制模式, 文本模式只能写入字符串, 二进制模式只能写入字节, 其实本质没有什么区别,都是写入01数据, 之所以二进制模式会乱码是因为我们写入了多字节数据,比如整数 ,文本编辑器打开的时候将这些多个字节拆分来译码,那当然就会造成乱码了,如果我们只写写入字符串,无论哪种模式,只要跟文本编辑器的编码一样,那就都不会乱码
输出重定向
我们可以将print的输出内容写入到文件里面去,必须是文本模式,如果是二进制模式的话,会出错,因为文本模式与二进制模式打开的文件对象不是一样的,他们的写入方法需要的参数有区别,一个是字符串,一个是字节数据,而print只能传递字符串
1 2 3 a = [1 ,2 ,3 ] with open ("test.txt" ,"wt" ,encoding="UTF-8" ) as f: print (a,file=f)
模拟文件io
我们可以通过 io 模块的StringIO 和 BytesIO 来模拟文件IO,在一些单元测试中用得着
1 2 3 4 5 6 7 import iofiles = io.StringIO("helloworld" ) print (files.read())fileb = io.BytesIO(b'hello world' ) print (fileb.read())
读写压缩文件
导入相应的模块就行了
1 2 3 4 5 import gzipwith gzip.open ("test.gz" ,"wb" ) as f: f.write(b"12345" )
os中相关的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import osfilepath = r"C:\Users\asus\Desktop\hexoblog\source\_posts\python\代码文件\python实用技巧\迭代器跟生成器\ostest.py" print (os.path.basename(filepath)) print (os.path.dirname(filepath)) print (os.path.isabs(filepath)) print (os.path.isdir(filepath)) print (os.path.isfile(filepath)) print (os.path.islink(filepath)) print (os.path.ismount(filepath)) print (os.path.exists(filepath)) print (os.path.getsize(filepath)) import timedef strftime (seconds ): return time.strftime("%Y-%m-%d %H:%M:%S" ,time.localtime(seconds)) print (strftime(os.path.getatime(filepath))) print (strftime(os.path.getmtime(filepath))) print (strftime(os.path.getctime(filepath)))
os.rename(src, dst)
:重命名文件或目录,将src改名为dst。
os.stat(path)
:获取指定路径的文件或目录的状态信息。
os.access(path, mode)
:检查指定路径的文件或目录是否有指定的权限。
os.chmod(path, mode)
:修改指定路径的文件或目录的权限。
os.utime(path, times)
:修改指定路径的文件或目录的访问和修改时间。
os.walk(top, topdown=True, onerror=None, followlinks=False)
:遍历指定目录及其子目录中的所有文件和目录。
os.environ
:一个包含环境变量的字典对象。
os.getenv(var)
:获取指定环境变量的值。
os.putenv(var, value)
:设置指定环境变量的值。
os.system(command)
:在命令行上执行指定的命令。
os.startfile(path)
:使用关联程序打开指定的文件。
os.popen(command[, mode[, bufsize]])
:打开一个管道用于读取命令的输出。
os.pipe()
:创建一个管道并返回两个文件描述符,一个用于读取,一个用于写入。
os.dup(fd)
:复制文件描述符。
os.execl(path, arg0, arg1, ..., argN)
:替换当前进程的映像,使用指定的可执行文件。
os.abort()
:生成异常,中止当前进程。
os.kill(pid, signal)
:发送指定的信号给进程。
os.waitpid(pid, options)
:等待指定的子进程结束并返回其状态。
os.getpid()
:获取当前进程的ID。
os.getppid()
:获取当前进程的父进程ID。
os.getuid()
:获取当前进程的用户ID。
os.getgid()
:获取当前进程的组ID。
os.setuid(uid)
:设置当前进程的用户ID。
os.setgid(gid)
:设置当前进程的组ID。
os.system()
:在新进程中执行系统命令。
os.times()
:获取当前进程的CPU时间和系统时间等信息。
os.cpu_count()
:获取当前系统的CPU核心数。
os.listdir(path)
:返回指定目录下的文件和目录列表。
os.link(src, dst)
:创建指向现有文件的硬链接。
os.symlink(src, dst)
:创建指向现有文件的符号链接。
元编程
让装饰器保留函数元信息
普通的装饰器并不会保留函数的元信息,比如name doc 这些,我们可以使用functools里面的wraps装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from functools import wrapsdef logger (func ): @wraps(func ) def wrapper (*args,**kwds ): print (f"{func} 被调用" ) return func(*args,**kwds) return wrapper @logger def func (a,b ): a-=1 b-=1 return a+b print (func.__name__)
如果使用了wraps, 我们还可以通过 func.__wrapped__得到原始函数
属性描述符
既然是属性描述符,它当然是描述一个属性, 而且这个属性必须是类的属性 ,有了属性描述符,我们就可以对这个属性进行限制,检查
如果一个类实现了__get__ 方法,他就是一个属性描述符,比如一个function类,属性描述符只有在类或者对象通过点的方式去调用才会生效
因此我们可以解释为什么对象调用方法不用写self参数, function对象就是一个属性描述符, 它会帮我们执行的时候自动加入self参数
1 2 3 4 5 6 7 8 class test : def hello (self ): pass t = test() print (t.hello,test.hello,sep="\n" )
我们发现这两个方法并不一样,这是因为在function 里面的 get方法返回的值不一样导致的
包和模块
运行目录或者压缩文件
如果我们在目录中定义了__main__文件的话,我们就可以使用 python 目录名字 这样的方式运行目录 , 如果我们的压缩包也包含__main__文件的话,我们也可以直接使用 python 压缩包的方式运行