2025-09-10:仓库迁移
This commit is contained in:
14
1.数据结构与算法/1.序列分解.py
Normal file
14
1.数据结构与算法/1.序列分解.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
# 普通的序列分解方式
|
||||||
|
def normal_func(list):
|
||||||
|
# list是可迭代对象,能够直接分解成多个元素
|
||||||
|
length = list.__len__()
|
||||||
|
length= 2
|
||||||
|
x, y = list
|
||||||
|
|
||||||
|
# 如果list长度和接数据的元素数量不匹配,会造成错误
|
||||||
|
x, y, z = list
|
||||||
|
|
||||||
|
# 在知道元素顺序的情况下想要丢弃list中的某些元素,可以用下划线来无视变量
|
||||||
|
_ ,y = list
|
||||||
|
|
||||||
25
1.数据结构与算法/10.移除重复元素保留顺序.py
Normal file
25
1.数据结构与算法/10.移除重复元素保留顺序.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# 如果序列中存储的元素是可哈希的,那就可以使用集合与生成器解决
|
||||||
|
|
||||||
|
def dequpe(items):
|
||||||
|
seen = set()
|
||||||
|
for item in items:
|
||||||
|
if item not in seen:
|
||||||
|
yield item
|
||||||
|
seen.add(item)
|
||||||
|
|
||||||
|
# 如果序列里的元素是不可哈希的,比如字典这种东西,就要先转化为可哈希对象;
|
||||||
|
# 这里自定义的key函数就起到了给字典解包的作用
|
||||||
|
|
||||||
|
def dequpe(items, key):
|
||||||
|
seen = set()
|
||||||
|
for item in items:
|
||||||
|
val = item if key is None else key(item)
|
||||||
|
if val not in seen:
|
||||||
|
yield item
|
||||||
|
seen.add(val)
|
||||||
|
|
||||||
|
# 如果单纯的做不改变顺序的去重,直接转集合再转列表就行了
|
||||||
|
|
||||||
|
a = [1,5,2,1,9,1,5,10]
|
||||||
|
a = list(set(a))
|
||||||
|
print(a)
|
||||||
18
1.数据结构与算法/11.切片命名.py
Normal file
18
1.数据结构与算法/11.切片命名.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
items = [0,1,2,3,4,5,6]
|
||||||
|
|
||||||
|
# 我们可以使用内置的slice函数对切片进行命名,参数分别为start|stop|step
|
||||||
|
a = slice(2,6,2)
|
||||||
|
|
||||||
|
print(items[a])
|
||||||
|
print(a.start, a.stop, a.step)
|
||||||
|
|
||||||
|
# indeces方法可以帮我们把start和stop自动限制在序列长度内,并返回一个限制后的切片
|
||||||
|
s = "sukahelloworld"
|
||||||
|
# 上面那个可能看着不明显,但是下面这个看着就明显多了
|
||||||
|
# s = "suka"
|
||||||
|
|
||||||
|
print(a.indices(len(s)))
|
||||||
|
|
||||||
|
# 因为返回的是元组,所以要使用得先解包
|
||||||
|
for i in range(*a.indices(len(s))):
|
||||||
|
print(s[i])
|
||||||
24
1.数据结构与算法/12.找出现最多元素.py
Normal file
24
1.数据结构与算法/12.找出现最多元素.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
words = ["look", "into", "my", "eyes", "look", "into", "my", "eyes",
|
||||||
|
"the", "eyes", "the", "eyes", "the", "eyes", "not", "around", "the",
|
||||||
|
"eyes", "don't", "look", "around", "the", "eyes", "look", "into",
|
||||||
|
"my", "eyes", "you're", "under"]
|
||||||
|
|
||||||
|
# collect里面的Counter库可以帮我们计数
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
|
word_counts = Counter(words)
|
||||||
|
top_three = word_counts.most_common(3)
|
||||||
|
print(top_three)
|
||||||
|
|
||||||
|
# Counter的底层是一个字典,是一个DefaultDict,这里处于学习目的把它实现一遍
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
def counter(word_list):
|
||||||
|
counter = defaultdict(int)
|
||||||
|
for word in word_list:
|
||||||
|
counter[word] += 1
|
||||||
|
|
||||||
|
return counter
|
||||||
|
|
||||||
|
print(counter(words))
|
||||||
|
|
||||||
25
1.数据结构与算法/13.字典公共键排序.py
Normal file
25
1.数据结构与算法/13.字典公共键排序.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# 我们有一个字典列表,想要按照字典里的某些键对列表进行排序
|
||||||
|
# 字典列表如下:
|
||||||
|
rows = [
|
||||||
|
{"fname":"Brian", "lname": "Jones", "uid":1003},
|
||||||
|
{"fname":"David", "lname": "Beazley", "uid":1002},
|
||||||
|
{"fname":"John", "lname": "Cleese", "uid":1001},
|
||||||
|
{"fname":"Big", "lname": "Jones", "uid":1004}
|
||||||
|
]
|
||||||
|
|
||||||
|
# 这时候用到itemgetter模块
|
||||||
|
from operator import itemgetter
|
||||||
|
|
||||||
|
# itemgetter的主要功能是通过可查询的标记进行数据获取;
|
||||||
|
# 下面分别根据fname和uid进行数据获取
|
||||||
|
|
||||||
|
rows_by_fname = sorted(rows, key=itemgetter("fname"))
|
||||||
|
rows_by_uid = sorted(rows, key=itemgetter("uid"))
|
||||||
|
|
||||||
|
print(rows_by_fname)
|
||||||
|
print(rows_by_uid)
|
||||||
|
|
||||||
|
# 当然,itemgetter也能被lambda函数替代,替代操作如下:
|
||||||
|
|
||||||
|
rows_by_fname_v2 = sorted(rows, key= lambda row: row["fname"])
|
||||||
|
print(rows_by_fname_v2)
|
||||||
32
1.数据结构与算法/14.原生不支持比较操作的对象排序.py
Normal file
32
1.数据结构与算法/14.原生不支持比较操作的对象排序.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# 众所周知,对class这种原生不支持比较的对象直接进行比较会造成暴毙;
|
||||||
|
# 我们但如果非比不可呢?比如这个东西:
|
||||||
|
class User:
|
||||||
|
def __init__(self,user_id):
|
||||||
|
self.user_id = user_id
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "User({})".format(self.user_id)
|
||||||
|
|
||||||
|
# 这里有一个列表user
|
||||||
|
user = [User(23), User(3), User(99)]
|
||||||
|
|
||||||
|
# 如果这时候需要按照某个属性给这个列表排序,那么直接sorted会报错:
|
||||||
|
try:
|
||||||
|
sorted(user)
|
||||||
|
pass
|
||||||
|
except:
|
||||||
|
print("你看吧,报错了")
|
||||||
|
|
||||||
|
# 这里的原因是对象不能直接进行比较,所以想要排序只能按照对象里的某个属性
|
||||||
|
# 有两种解决方案:
|
||||||
|
sorted_list = sorted(user, key=lambda x: x.user_id)
|
||||||
|
print(sorted_list)
|
||||||
|
|
||||||
|
# 要么就用operator库里的attrgetter
|
||||||
|
from operator import attrgetter
|
||||||
|
sorted_list = sorted(user, key=attrgetter("user_id"))
|
||||||
|
print(sorted_list)
|
||||||
|
|
||||||
|
# 用这东西的好处和针对字典的itemgetter一样,可以取多个字段
|
||||||
|
# sorted_list = sorted(user, key=attrgetter("user_id", "word_2", "word_3", ...))
|
||||||
|
# 同样的,max和min这种有key参数的内建函数都能用这些东西折腾
|
||||||
39
1.数据结构与算法/15.根据字段分组.py
Normal file
39
1.数据结构与算法/15.根据字段分组.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# 假如我们有一个记录需要进行分组
|
||||||
|
|
||||||
|
rows = [
|
||||||
|
{"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"},
|
||||||
|
]
|
||||||
|
|
||||||
|
# 如果我们想按日期进行分组,那itertools里的groupby会很好用
|
||||||
|
from operator import itemgetter
|
||||||
|
from itertools import groupby
|
||||||
|
|
||||||
|
# 因为groupby只能检查连续的项,所以我们先对列表进行排序
|
||||||
|
rows.sort(key=itemgetter("date"))
|
||||||
|
print(rows)
|
||||||
|
|
||||||
|
# 然后我们进行分组操作:
|
||||||
|
for date, items in groupby(rows, itemgetter("date")):
|
||||||
|
print(date)
|
||||||
|
for i in items:
|
||||||
|
print(' ', i)
|
||||||
|
# groupby每次返回的是一个值(分组名称)和一个子迭代器(组内数据)
|
||||||
|
|
||||||
|
|
||||||
|
# 当然如果是单纯的分组,一键多值字典是个好东西
|
||||||
|
from collections import defaultdict
|
||||||
|
data = defaultdict(list)
|
||||||
|
for item in rows:
|
||||||
|
data[item["date"]].append(item)
|
||||||
|
|
||||||
|
print(data)
|
||||||
|
|
||||||
|
# 在不考虑内存开销的情况下,这东西比先排序再groupby快
|
||||||
|
|
||||||
71
1.数据结构与算法/16.元素筛选.py
Normal file
71
1.数据结构与算法/16.元素筛选.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# 比如我有一个列表
|
||||||
|
mylist = [1,4,-5,10,-7,2,3,-1]
|
||||||
|
|
||||||
|
# 最脑瘫的方法就是用列表推导式重新生成一遍
|
||||||
|
filted_list_b0 = [x for x in mylist if x > 0]
|
||||||
|
filted_list_s0 = [x for x in mylist if x < 0]
|
||||||
|
# print(filted_list_b0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 当然如果列表很大,这样做会裂开,因为列表推导式本质上是遍历,会建立一份mylist的复制
|
||||||
|
# 这个时候我们可以用生成器的方式表达解析结果以节省内存
|
||||||
|
filted_list_b0 = (x for x in mylist if x > 0)
|
||||||
|
# for i in filted_list_b0:
|
||||||
|
# print(i)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 当然有的列表比较抽象:
|
||||||
|
mylist_suka = ['1', '2', '-3', '-', '4', 'N\A', '5']
|
||||||
|
|
||||||
|
# 这个时候我们可以先写逻辑进行过滤:
|
||||||
|
def drop_suka(val):
|
||||||
|
try:
|
||||||
|
x = int(val)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# 然后我们用内建的filter函数进行过滤,
|
||||||
|
# 第一个位置是一个值判断布尔函数,第二个参数是要过滤的列表,布尔函数里写过滤逻辑;
|
||||||
|
# filter返回一个迭代器,所以可以直接用list方法转成列表
|
||||||
|
my_nonsuka_list = list(filter(drop_suka, mylist_suka))
|
||||||
|
# print(my_nonsuka_list)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 列表推导式和生成器推导式都可以在值的位置进行:
|
||||||
|
# 1.计算 x*2
|
||||||
|
# 2.三元判断 x*2 if x < 1 else x
|
||||||
|
filted_list_b0 = [x*2 if x < 1 else x for x in mylist if x > 0]
|
||||||
|
filted_list_b0 = (x*2 if x < 1 else x for x in mylist if x > 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 如果要筛选的元素在另一个列表里怎么办?比如我们有:
|
||||||
|
rows = [
|
||||||
|
"5412 N CLARK",
|
||||||
|
"5148 N CLARK",
|
||||||
|
"5800 E 58TH",
|
||||||
|
"2122 N CLARK",
|
||||||
|
"5645 N RAVENSWOOD",
|
||||||
|
"1060 W ADDISON",
|
||||||
|
"4801 N BROADWAY",
|
||||||
|
"1039 W GRANVILLE",
|
||||||
|
]
|
||||||
|
|
||||||
|
counts = [0,3,10,4,1,7,6,1]
|
||||||
|
|
||||||
|
# counts表示rows里面对应元素出现的次数,如果我们想找count>5的元素咋整?
|
||||||
|
# 1.搞一个布尔列表,告诉程序谁才是我们要的
|
||||||
|
wanted = [n > 5 for n in counts] # 这和[n for n in counts if n > 5]不一样,之前是在for的同时筛选,现在是for完看看是否符合n>5
|
||||||
|
# print(wanted)
|
||||||
|
# 2.用itertools的compress函数来进行对比
|
||||||
|
from itertools import compress
|
||||||
|
|
||||||
|
# itertools里的compress函数输入一个列表和一个布尔列表,返回一个迭代器,里面是列表中同样位置在布尔列表里为True的元素
|
||||||
|
result = list(compress(rows, wanted))
|
||||||
|
print(result)
|
||||||
|
|
||||||
19
1.数据结构与算法/17.字典子集.py
Normal file
19
1.数据结构与算法/17.字典子集.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# 假如我们有一个字典,需要提取子集:
|
||||||
|
price = {
|
||||||
|
"ACME": 45.23,
|
||||||
|
"AAPL": 612.78,
|
||||||
|
"IBM": 205.55,
|
||||||
|
"HPQ": 37.2,
|
||||||
|
"FB": 10.75
|
||||||
|
}
|
||||||
|
|
||||||
|
# 直接用字典推导式
|
||||||
|
p1 = {key :value for key, value in price.items() if value > 200}
|
||||||
|
|
||||||
|
# 提取key也是
|
||||||
|
keys = {"AAPL", "IBM", "HPQ", "MSFT"}
|
||||||
|
p2 = {key : value for key, value in price.items() if key in keys}
|
||||||
|
|
||||||
|
# 字典推导式是最快的方法,比下面两个办法都快:
|
||||||
|
dict((key, value) for key, value in price if value > 100) # 快两倍
|
||||||
|
{key:price[key] for key in price.keys() if key in price.keys() & keys} # 快1.6倍
|
||||||
37
1.数据结构与算法/18.将名称映射到序列元素.py
Normal file
37
1.数据结构与算法/18.将名称映射到序列元素.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# collection库中有一个东西叫做namedtuple,有点像结构体,可以给元组中的元素起名
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
Subscribe = namedtuple('Subscriber', ["addr", "joined"])
|
||||||
|
sub = Subscribe("suka@qq.com", "2024/07/17")
|
||||||
|
print(sub)
|
||||||
|
print(sub.addr)
|
||||||
|
print(sub.joined)
|
||||||
|
|
||||||
|
# 可以看到,这个东西和对象有点相似,但这种操作又保留了适用于元组的所有操作,比如索引和分解
|
||||||
|
print(sub[0])
|
||||||
|
print(len(sub))
|
||||||
|
addr, joined = sub
|
||||||
|
print(addr, joined)
|
||||||
|
|
||||||
|
# 如果我们获取的数据总是list类型,不是标准的json数据,那这东西就很有用
|
||||||
|
# 比如返回的数据是这种东西: data = [0,1,1,3,2,6]
|
||||||
|
# 如果是这种非标数据,普通代码处理长这样:
|
||||||
|
def compute_cost(records):
|
||||||
|
total = 0.0
|
||||||
|
for rec in records:
|
||||||
|
total += rec[1] * rec[2]
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
||||||
|
# 这造成一个问题,鬼知道rec[1]和rec[2]是什么东西,代码可读性很傻逼;
|
||||||
|
# 所以我们可以在拿到数据的时候先标准化一下
|
||||||
|
Stock = namedtuple("Stock", ['name', 'share', 'price'])
|
||||||
|
def compute_cost(records):
|
||||||
|
total = 0.0
|
||||||
|
for rec in records:
|
||||||
|
stock = Stock(*rec)
|
||||||
|
total += stock.share * stock.price
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
||||||
|
# 当然如果返回的已经是对象了,那就没有做namedtuple的必要了,在不想用类的时候可以用这东西替代字典
|
||||||
30
1.数据结构与算法/19.同时对数据做转化和换算.py
Normal file
30
1.数据结构与算法/19.同时对数据做转化和换算.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# 这是一种简化操作,可以将转换和换算合为一体,比如我们有一个列表
|
||||||
|
nums = [1,2,3,4,5]
|
||||||
|
|
||||||
|
# 计算平方和我们一般这样子做:
|
||||||
|
nums_square = [x*x for x in nums]
|
||||||
|
s = sum(nums_square)
|
||||||
|
|
||||||
|
# 这个时候我们就可以这样来简化一下运算:
|
||||||
|
s = sum(x*x for x in nums)
|
||||||
|
|
||||||
|
|
||||||
|
# 又或者我们要在一堆参数中间加一些分隔符:
|
||||||
|
# 注意,这里的join和os.path.join不一样,这里是用来给jion输入的列表加分隔符的
|
||||||
|
s = ("ACME", 50, 123.45)
|
||||||
|
print(','.join(str(x) for x in s))
|
||||||
|
|
||||||
|
# 这里我们将生成器表达式从()里薅出来,当作可迭代参数塞进了函数里,这样避免了列表生成器造成的内存浪费
|
||||||
|
|
||||||
|
# 对单个字典内,我们用键值反转的方法来找元素,但是如果这是一个字典列表,那情况就大大不同了
|
||||||
|
portfolio = [
|
||||||
|
{'name': 'GOOG', 'shares': 50},
|
||||||
|
{'name': 'YHOO', 'shares': 75},
|
||||||
|
{'name': 'AOL', 'shares': 20},
|
||||||
|
{'name': 'SCOX', 'shares': 65}
|
||||||
|
]
|
||||||
|
max_share = max(portfolio, key=lambda x: x["shares"])
|
||||||
|
print(max_share)
|
||||||
|
# 当然如果仅仅只要拿到最小值就用到了生成器表达式内嵌的方法
|
||||||
|
max_share_num = max(s['shares'] for s in portfolio)
|
||||||
|
print(max_share_num)
|
||||||
36
1.数据结构与算法/2.从任意可迭代对象中分解元素.py
Normal file
36
1.数据结构与算法/2.从任意可迭代对象中分解元素.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
# 可迭代对象分解
|
||||||
|
def iterable_breakdown(list):
|
||||||
|
# 如果可迭代对象长度大于接数据的变量数量,分解值过多报错
|
||||||
|
length = list.__len__()
|
||||||
|
length = 20
|
||||||
|
# 这样会报错,因为list长度是20,但分解变量只有两个
|
||||||
|
x, y = list
|
||||||
|
|
||||||
|
# 想要接住任意长度的列表,需要用*
|
||||||
|
# *x是包含list前n-1个元素的列表,y是最后一个元素
|
||||||
|
# 我们可以在任意位置接入带*的参数来接住一些值,这些值会在*变量中以列表存储
|
||||||
|
*x, y = list
|
||||||
|
x, *y, z = list
|
||||||
|
|
||||||
|
# 假设有一个变长元组序列
|
||||||
|
record = [("foo",1,2),("bar","suka"),("foo",2,4)]
|
||||||
|
|
||||||
|
def do_foo(x,y):
|
||||||
|
print(x,y)
|
||||||
|
|
||||||
|
def do_bar(s):
|
||||||
|
print(s)
|
||||||
|
|
||||||
|
# 我们可以使用*来解包从而将参数薅出来
|
||||||
|
for tag, *args in record:
|
||||||
|
if tag == "foo":
|
||||||
|
do_foo(*args)
|
||||||
|
elif tag == "bar":
|
||||||
|
do_bar(*args)
|
||||||
|
|
||||||
|
# 同理,也可以用*变量来占位挖掉不要的变量,比如在特定位置用*ignore或*_来挖掉参数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
57
1.数据结构与算法/20.多个映射转换成单个映射.py
Normal file
57
1.数据结构与算法/20.多个映射转换成单个映射.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# 假如又两个字典:
|
||||||
|
a = {"x":1, "z":3}
|
||||||
|
b = {"y":2, "z":4}
|
||||||
|
|
||||||
|
# 怎么将这两个字典映射到一个新字典里呢?愚蠢的办法是使用字典合并
|
||||||
|
|
||||||
|
# 这个方法会把右边的b刷写到a里
|
||||||
|
c = a|b
|
||||||
|
print(c)
|
||||||
|
# 但是,你会发现,如果原字典被改了,那c不会有变化:
|
||||||
|
a["x"] = 2
|
||||||
|
print(c)
|
||||||
|
|
||||||
|
# 这个时候使用映射方案的chainmap就会很好用
|
||||||
|
from collections import ChainMap
|
||||||
|
c = ChainMap(a,b)
|
||||||
|
|
||||||
|
print(",".join(str(x) for x in [c['x'], c['y'], c['z']]))
|
||||||
|
print(len(c))
|
||||||
|
|
||||||
|
# 如果此时更改原字典的值:
|
||||||
|
a["x"] = 1
|
||||||
|
|
||||||
|
# 输出也跟着更改了
|
||||||
|
print(",".join(str(x) for x in [c['x'], c['y'], c['z']]))
|
||||||
|
print(len(c))
|
||||||
|
|
||||||
|
# 注,chainmap中如果有重复的键,则会采用左值,比如上面的例子使用a["z"]作为重复键z的取值;
|
||||||
|
# 如果修改字典值,也会作用在左值上
|
||||||
|
del c["z"]
|
||||||
|
print(",".join(str(x) for x in [c['x'], c['y'], c['z']]))
|
||||||
|
print(a, b)
|
||||||
|
# 如果尝试删除a里已经没有但b里有的键,那么会报错
|
||||||
|
try:
|
||||||
|
del c["z"]
|
||||||
|
except:
|
||||||
|
print("在a里没找到键")
|
||||||
|
|
||||||
|
# 这东西的底层其实是一个链表,演示如下:
|
||||||
|
dic = ChainMap()
|
||||||
|
dic["x"] = 1
|
||||||
|
dic = dic.new_child()
|
||||||
|
print(dic)
|
||||||
|
dic["x"] = 2
|
||||||
|
dic = dic.new_child()
|
||||||
|
print(dic)
|
||||||
|
dic["x"] = 3
|
||||||
|
print(dic)
|
||||||
|
# 从这里我们开始回溯历史
|
||||||
|
dic = dic.parents
|
||||||
|
print(dic)
|
||||||
|
dic = dic.parents
|
||||||
|
print(dic)
|
||||||
|
dic = dic.parents
|
||||||
|
print(dic)
|
||||||
|
|
||||||
|
# 可以看到我们用new_child()函数创建了一个新节点;用parents属性来回溯
|
||||||
19
1.数据结构与算法/3.保存最后N个元素.py
Normal file
19
1.数据结构与算法/3.保存最后N个元素.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# 队列可以对最后N个元素进行保存
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
# 从历史记录列表history得到length条最新历史记录
|
||||||
|
def latest_history(history, length):
|
||||||
|
window = deque(maxlen=length)
|
||||||
|
for record in history:
|
||||||
|
window.append(record)
|
||||||
|
|
||||||
|
# window引用双头队列组件deque,该组件有方法如下
|
||||||
|
# 从队尾加入
|
||||||
|
window.append(1)
|
||||||
|
# 从队尾弹出
|
||||||
|
window.pop()
|
||||||
|
# 从队头加入
|
||||||
|
window.appendleft(1)
|
||||||
|
# 从队头弹出
|
||||||
|
window.popleft()
|
||||||
|
|
||||||
33
1.数据结构与算法/4.最大N个和最小N个.py
Normal file
33
1.数据结构与算法/4.最大N个和最小N个.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import heapq
|
||||||
|
|
||||||
|
# heapq里有两个函数,nlargest和nsmallest可以找最大N个和最小N个;
|
||||||
|
# 参数接受一个列表和一个N
|
||||||
|
|
||||||
|
def max_n(search_list, n):
|
||||||
|
list_max_n = heapq.nlargest(n ,search_list)
|
||||||
|
return list_max_n
|
||||||
|
|
||||||
|
def min_n(search_list, n):
|
||||||
|
list_min_n = heapq.nsmallest(n, search_list)
|
||||||
|
return list_min_n
|
||||||
|
|
||||||
|
# 这两个函数都提供了一个key输入来实现在字典列表中的应用
|
||||||
|
def max_in_dictlist(search_list, n, key):
|
||||||
|
max_key_n = heapq.nlargest(n, search_list, key=lambda dic : dic[key])
|
||||||
|
return max_key_n
|
||||||
|
|
||||||
|
# 如果寻找最大N个和最小N个元素,这个方法在N的规模不大的时候好用;
|
||||||
|
# 当日在N=1的时候还是用max和min方法更快,要是N巨大那就得排序了
|
||||||
|
|
||||||
|
# heapq事实上是一个堆方法,将列表在底层序列化为一个最小堆
|
||||||
|
def heapq_func():
|
||||||
|
ori_list = [1,2,3,4,5]
|
||||||
|
heap = list(ori_list)
|
||||||
|
# 将列表进行堆排序
|
||||||
|
heapq.heapify(heap)
|
||||||
|
# 弹出堆顶元素
|
||||||
|
heapq.heappop(heap)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
l = [1,2,3,4,5]
|
||||||
|
print(max_n(l, 3))
|
||||||
43
1.数据结构与算法/5.优先级队列.py
Normal file
43
1.数据结构与算法/5.优先级队列.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import heapq
|
||||||
|
|
||||||
|
|
||||||
|
# 优先级队列(使用小根堆实现)
|
||||||
|
class PriorityQueue:
|
||||||
|
def __init__(self):
|
||||||
|
self._queue = []
|
||||||
|
# 由于优先级在比较大小时可能相同造成比较失败,维护一个下标index来进行二级辨识
|
||||||
|
self._index = 0
|
||||||
|
|
||||||
|
# 由于heapq的原理是生成一个小根堆,所以优先级取负,这样优先级越大,堆识别到的优先级越小;引入index,在优先级相同时比较入栈先后顺序
|
||||||
|
def push(self, item, priority):
|
||||||
|
heapq.heappush(self._queue, (-priority, self._index, item))
|
||||||
|
self._index += 1
|
||||||
|
|
||||||
|
# 弹出堆顶值
|
||||||
|
def pop(self):
|
||||||
|
return heapq.heappop(self._queue)[-1]
|
||||||
|
|
||||||
|
|
||||||
|
# 在Python中,实例化对象是不可以直接进行比较的,例如:
|
||||||
|
class Item:
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
|
||||||
|
return 'Item {!r}'.format(self.name)
|
||||||
|
# a = Iter('foo') b = Iter('bar'), 进行a<b比较会输出报错
|
||||||
|
|
||||||
|
# 一个优先级队列的简单调用示例
|
||||||
|
if __name__ == "__main__":
|
||||||
|
q = PriorityQueue()
|
||||||
|
q.push(Item("foo"), 1)
|
||||||
|
q.push(Item("bar"), 5)
|
||||||
|
q.push(Item("spam"), 4)
|
||||||
|
q.push(Item("grok"), 1)
|
||||||
|
a = q.pop()
|
||||||
|
b = q.pop()
|
||||||
|
c = q.pop()
|
||||||
|
d = q.pop()
|
||||||
|
print(a)
|
||||||
|
|
||||||
38
1.数据结构与算法/6.一键多值字典.py
Normal file
38
1.数据结构与算法/6.一键多值字典.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
|
||||||
|
# 当你新建一个普通字典,对字典中不存在的key进行调用时会报错
|
||||||
|
a = {}
|
||||||
|
try:
|
||||||
|
print(a["key"])
|
||||||
|
pass
|
||||||
|
except:
|
||||||
|
print("err")
|
||||||
|
|
||||||
|
|
||||||
|
# 如果使用defaultdict进行字典初始化建立,就不存在这个问题;
|
||||||
|
# 当出现不存在的key的时候,会根据默认设置进行默认值创建
|
||||||
|
b = defaultdict(list)
|
||||||
|
print(b["key"])
|
||||||
|
|
||||||
|
# 但是这样就有一个问题,这个key就会一直在这里等待调用,如果没有用到就是垃圾
|
||||||
|
print(b)
|
||||||
|
|
||||||
|
# 这个方法可以用于记录的添加;比如一个方法如下:
|
||||||
|
|
||||||
|
pairs = [{"a":10},{"b":10},{"b":10}]
|
||||||
|
d = {}
|
||||||
|
for key, value in pairs:
|
||||||
|
if key not in d:
|
||||||
|
d[key] = []
|
||||||
|
d[key].append(value)
|
||||||
|
|
||||||
|
# 很明显这样就显得很蠢,我还要判断一下键在不在里面
|
||||||
|
# 使用defaultdict就没有这个困扰,直接省略一键添加
|
||||||
|
|
||||||
|
d = defaultdict(list)
|
||||||
|
for key, value in pairs:
|
||||||
|
d["key"].append(value)
|
||||||
|
|
||||||
|
# 当然为了避免生成空键,使用这东西的时候切记在里面翻东西的时候别直接用dict[key]输入;
|
||||||
|
# 先用keys函数拿到所有键再动手
|
||||||
20
1.数据结构与算法/7.有序字典.py
Normal file
20
1.数据结构与算法/7.有序字典.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
# 如果需要严格控制字典元素的顺序,可以使用OrderedDict
|
||||||
|
# 比如用于网络通信协议的未序列化字典对象
|
||||||
|
|
||||||
|
d = OrderedDict()
|
||||||
|
d['foo'] = 1
|
||||||
|
d['bar'] = 2
|
||||||
|
d['spam'] = 3
|
||||||
|
d['grok'] = 4
|
||||||
|
|
||||||
|
for key in d:
|
||||||
|
print(d[key])
|
||||||
|
|
||||||
|
# 这样就可以序列化得到一个有序的json编码
|
||||||
|
import json
|
||||||
|
a = json.dumps(d)
|
||||||
|
print(a)
|
||||||
|
|
||||||
|
# 尤其注意:这东西内部有一个双向链表导致它的内存开支有点恐怖,大规模情况下谨慎使用
|
||||||
28
1.数据结构与算法/8.字典计算问题.py
Normal file
28
1.数据结构与算法/8.字典计算问题.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
price = {
|
||||||
|
"ACME": 45.23,
|
||||||
|
"AAPL": 612.78,
|
||||||
|
"IBM": 205.55,
|
||||||
|
"HPQ": 37.2,
|
||||||
|
"FB": 10.75
|
||||||
|
}
|
||||||
|
|
||||||
|
# 对这样的数据,有时候我们想找到最大值对应的键
|
||||||
|
# 传统方法长这样
|
||||||
|
a = max(price.values())
|
||||||
|
print(a)
|
||||||
|
for key, value in price.items():
|
||||||
|
if value == a:
|
||||||
|
print(key)
|
||||||
|
# 显然,我们要对字典里的这些东西进行遍历才能根据值把键找出来
|
||||||
|
|
||||||
|
# 如果我们不想要这样的开销,可以把键值反转:
|
||||||
|
|
||||||
|
max_price = max(zip(price.values(), price.keys()))
|
||||||
|
print(max_price)
|
||||||
|
|
||||||
|
# 注意,在对键值反转的zip中使用max和min这类函数时需要注意,如果出现:
|
||||||
|
# a = {10, "AA"}, b = {10, "ZZ"}的情况
|
||||||
|
# 由于值相同,会根据键大小返回结果
|
||||||
|
# 所以如果有多个值相同的字段,还得是老办法
|
||||||
|
max_price = [key for value, key in zip(price.values(), price.keys()) if value == a]
|
||||||
|
print(max_price)
|
||||||
33
1.数据结构与算法/9.字典差异.py
Normal file
33
1.数据结构与算法/9.字典差异.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# 我们有两个字典如下:
|
||||||
|
|
||||||
|
a = {
|
||||||
|
"x":1,
|
||||||
|
"y":2,
|
||||||
|
"z":3
|
||||||
|
}
|
||||||
|
|
||||||
|
b = {
|
||||||
|
"w":10,
|
||||||
|
"x":11,
|
||||||
|
"y":12
|
||||||
|
}
|
||||||
|
|
||||||
|
# 如果想要得到相同处,只需要用keys和items这类可迭代方法执行集合操作就行
|
||||||
|
|
||||||
|
# 大家都有
|
||||||
|
print(a.keys() & b.keys())
|
||||||
|
|
||||||
|
# a有b没有
|
||||||
|
print(a.keys() - b.keys())
|
||||||
|
|
||||||
|
# b有a没有
|
||||||
|
print(b.keys() - a.keys())
|
||||||
|
|
||||||
|
# 想快速给a减键,可以这样:
|
||||||
|
c = {key:a[key] for key in a.keys() - {"w", "z"}}
|
||||||
|
print(c)
|
||||||
|
|
||||||
|
# python3.9更新了使用|的字典合并和与+=一样的合并运算符|=
|
||||||
|
d = a
|
||||||
|
d |= b
|
||||||
|
print(d)
|
||||||
20
12.并发/1.启动和停止线程.py
Normal file
20
12.并发/1.启动和停止线程.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
|
def countdown(n):
|
||||||
|
while n > 0:
|
||||||
|
print("T-minus", n)
|
||||||
|
n -= 1
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
def countdown2(n):
|
||||||
|
while n > 0:
|
||||||
|
print("T-minus", n)
|
||||||
|
n -= 1
|
||||||
|
time.sleep(6)
|
||||||
|
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
t = Thread(target=countdown, args=(10, ))
|
||||||
|
t.start()
|
||||||
|
t2 = Thread(target=countdown2, args=(10, ))
|
||||||
|
t2.start()
|
||||||
23
2.字符串和文本/1.任意多分隔符字符串拆分.py
Normal file
23
2.字符串和文本/1.任意多分隔符字符串拆分.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# 众所周知,split函数可以针对单个分隔符把字符串拆开
|
||||||
|
|
||||||
|
a = "suka,caonimade,shabi"
|
||||||
|
b = a.split(",")
|
||||||
|
print(b)
|
||||||
|
|
||||||
|
# 但是假如系统可以检测到这种粗鄙之语,那就会让🐎🐎消失
|
||||||
|
# 想要保卫自己的🐎,那就要粗鄙的隐晦一点:
|
||||||
|
|
||||||
|
a = "suka,*&&**caonimade,_&^shabi"
|
||||||
|
# 这时候关键词检测就暴毙了
|
||||||
|
b = a.split(",")
|
||||||
|
print(b)
|
||||||
|
|
||||||
|
# 但是聪明的审核会用re库来解决这个问题:
|
||||||
|
import re
|
||||||
|
# re库中的split函数接受多个输入,可以同时干掉多种干扰:
|
||||||
|
b = re.split(r'[,*&^_]', a)
|
||||||
|
print(b)
|
||||||
|
|
||||||
|
# 如果要保留分隔符, 记得使用正则闭包
|
||||||
|
b = re.split(r'(,|&|^|_)', a)
|
||||||
|
print(b)
|
||||||
11
2.字符串和文本/10.用正则处理unicode.py
Normal file
11
2.字符串和文本/10.用正则处理unicode.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 默认情况下,re模块已经认识了某些unicode字符,比如\d现在已经可以匹配unicode的数字
|
||||||
|
num = re.compile(r'\d+')
|
||||||
|
# match会从第一个字符开始匹配,如果不匹配就返回None
|
||||||
|
print(num.match('123'))
|
||||||
|
|
||||||
|
print(num.match('\u0661\u0662\u0663'))
|
||||||
|
|
||||||
|
# 我的评价是别这样干,在处理东西之前先将输入标准化为ascii编码是一个程序员的基本素养
|
||||||
24
2.字符串和文本/11.从字符串中去掉不需要的字符.py
Normal file
24
2.字符串和文本/11.从字符串中去掉不需要的字符.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 使用strip方法可以删除不要的字符串,默认是删除空格
|
||||||
|
s = ' hello world \n'
|
||||||
|
print(s.strip())
|
||||||
|
|
||||||
|
# 同样的,这个方法还有进阶版本,lstrip和rstrip可以从左右开始检测指定符号进行删除
|
||||||
|
# 如果左边开头或右边开头没有指定符号,则不会起作用
|
||||||
|
s2 = '---hello world==='
|
||||||
|
print(s2.lstrip('-'))
|
||||||
|
print(s2.rstrip('='))
|
||||||
|
print(s2.strip('-='))
|
||||||
|
|
||||||
|
# 在这个例子中,hello world中间的空格不会被strip系列函数删除,因为这个方法不会管字符串中间的字符
|
||||||
|
# 如果需要删除hello world中间的空格,请使用replace方法
|
||||||
|
|
||||||
|
# 在一个文件中,我们可以这样进行每一行的信息过滤:
|
||||||
|
|
||||||
|
# with open(file) as f:
|
||||||
|
# lines = (line.strip() for line in f)
|
||||||
|
# for line in lines:
|
||||||
|
# print(line)
|
||||||
|
|
||||||
|
|
||||||
26
2.字符串和文本/12.文本过滤和清理.py
Normal file
26
2.字符串和文本/12.文本过滤和清理.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 总有脑瘫喜欢输入一些奇奇怪怪的东西,这个时候我们就需要做一些过滤操作
|
||||||
|
s = 'pyth\u0303on\fis\tawesome\r\n'
|
||||||
|
# 这个字符串看起来就非常恶心了,这时候我们可以用translate方法对一些特殊符号进行迭代:
|
||||||
|
remap = {
|
||||||
|
ord('\t'): ' ',
|
||||||
|
ord('\f'): ' ',
|
||||||
|
ord('\r'): None,
|
||||||
|
}
|
||||||
|
s = s.translate(remap)
|
||||||
|
print(s)
|
||||||
|
|
||||||
|
# 这个时候我们就过滤掉了各种空格符和回车符
|
||||||
|
# 我们也可以构建更大的order_dict去把unicode过滤掉
|
||||||
|
b = unicodedata.normalize("NFD", s)
|
||||||
|
# 对每一个unicode建立一个None的映射,
|
||||||
|
cmb_dict = dict.fromkeys(c for c in range(sys.maxunicode) if unicodedata.combining(chr(c)))
|
||||||
|
b = b.translate(cmb_dict)
|
||||||
|
print(b)
|
||||||
|
|
||||||
|
# 如果只是简单的替换操作,那么replace函数已经很好了,速度也很快;
|
||||||
|
# 在做字符映射操作的时候可以使用translate方法并构建一个映射字典
|
||||||
22
2.字符串和文本/13.对齐文本.py
Normal file
22
2.字符串和文本/13.对齐文本.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
text = 'Hello World'
|
||||||
|
# 如果想要对齐字符串,可以使用ljest、rjust和center方法
|
||||||
|
print(text.ljust(20))
|
||||||
|
print(text.rjust(20))
|
||||||
|
print(text.center(20))
|
||||||
|
|
||||||
|
# 当然,这个轮椅也支持填充操作
|
||||||
|
print(text.ljust(20, '='))
|
||||||
|
print(text.rjust(20, '-'))
|
||||||
|
print(text.center(20, '='))
|
||||||
|
|
||||||
|
# 除了轮椅函数,建议使用format函数,这个东西更加泛用,还能进行格式转换
|
||||||
|
print(format(text, '-<20')) # <表示宽度20居左
|
||||||
|
print(format(text, '=>20')) # >表示宽度20居右
|
||||||
|
print(format(text, '+^20')) # ^表示宽度20居中
|
||||||
|
# <>^前面的符号表示使用该符号填充
|
||||||
|
# format的好处主要在于,它的处理对象不仅是字符串,可以对任何数据形式进行格式化
|
||||||
|
num = 1.23456
|
||||||
|
print(format(num, '=^20.2f')) # 这表示宽度20,保留两位小数,居中,使用=填充
|
||||||
15
2.字符串和文本/14.字符串连接与合并.py
Normal file
15
2.字符串和文本/14.字符串连接与合并.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 如果有一堆字符串, 那我想join函数是一个好选择, 但如果就几个那用+就可以了
|
||||||
|
parts = ["Is", "Chicago", "Not", "Chicago?"]
|
||||||
|
str_c = ' '.join(parts)
|
||||||
|
print(str_c)
|
||||||
|
|
||||||
|
# 如果我有密钥的两部分,想把a和b连接在一起,那可以这样
|
||||||
|
keyword = 'su' 'ka'
|
||||||
|
print(keyword)
|
||||||
|
|
||||||
|
# 在实际运用中,如果遇到一堆字符串拼接,千万别用+=,这样会产生大量内存垃圾
|
||||||
|
# 如果有大量的片段需要拼接,则应该考虑使用生成器函数(字段确定的情况下)
|
||||||
|
|
||||||
43
2.字符串和文本/15.给字符串中的变量做插值处理.py
Normal file
43
2.字符串和文本/15.给字符串中的变量做插值处理.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 一般来说,我们用{}和format函数对字符串进行插值
|
||||||
|
s = "{name} has {n} messages"
|
||||||
|
s1 = s.format(name="Sam", n=10)
|
||||||
|
print(s1)
|
||||||
|
|
||||||
|
# 当然如果你想偷懒,也可以这样:
|
||||||
|
name = "Sam"
|
||||||
|
n = 10
|
||||||
|
s2 = s.format_map(vars())
|
||||||
|
print(s2)
|
||||||
|
# 在上面这段代码中,vars函数会从堆栈里自动寻找变量名称的值
|
||||||
|
# vars还能解析类实例:
|
||||||
|
class INFO:
|
||||||
|
def __init__(self, name, n):
|
||||||
|
self.name = name
|
||||||
|
self.n = n
|
||||||
|
info = INFO(name, n)
|
||||||
|
s3 = s.format_map(vars(info))
|
||||||
|
print(s3)
|
||||||
|
|
||||||
|
# 爽吗?确实爽,但是有一个缺点,在填充值缺失的时候这玩意儿会报错
|
||||||
|
try:
|
||||||
|
s4 = s.format(name=name)
|
||||||
|
except KeyError:
|
||||||
|
print("少参数")
|
||||||
|
|
||||||
|
# 我们可以定义一个类的__missing__方法来防止出现这种情况
|
||||||
|
class Safe_Sub(dict):
|
||||||
|
def __missing__(self, key):
|
||||||
|
return '{'+ key +'}'
|
||||||
|
|
||||||
|
del n
|
||||||
|
print(s.format_map(Safe_Sub(vars())))
|
||||||
|
|
||||||
|
# 如果经常操作,那么可以把这个功能藏在函数里,同时从当前调用的堆栈里找到变量(我反正不建议这么干,不过得知道)
|
||||||
|
def sub(text):
|
||||||
|
# 不!要!在!代码里!碰!tmd!堆栈!这是手贱行为,可能导致各种bug,除非有必要的理由否则不要这么做
|
||||||
|
return text.format_map(Safe_Sub(sys._getframe(1).f_locals))
|
||||||
|
name = "Suka"
|
||||||
|
print(sub(s))
|
||||||
20
2.字符串和文本/16.以固定列数重新格式化文本.py
Normal file
20
2.字符串和文本/16.以固定列数重新格式化文本.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import textwrap
|
||||||
|
import os
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
words = ' '.join(["look", "into", "my", "eyes", "look", "into", "my", "eyes",
|
||||||
|
"the", "eyes", "the", "eyes", "the", "eyes", "not", "around", "the",
|
||||||
|
"eyes", "don't", "look", "around", "the", "eyes", "look", "into",
|
||||||
|
"my", "eyes", "you're", "under"])
|
||||||
|
print(words)
|
||||||
|
# 可以看到这样打印出来的字符串巨长无比,如果想要控制行宽度,就用textwrap模块
|
||||||
|
print(textwrap.fill(words, 70))
|
||||||
|
print(textwrap.fill(words, 40))
|
||||||
|
# 第二个参数width可以控制显示宽度,如果是控制台输出,你还能去找os.get_terminal_size()
|
||||||
|
# 注意,由于未知原因这在pycharm中不好使(2024.8.27)
|
||||||
|
t_width = os.get_terminal_size().columns
|
||||||
|
print(t_width)
|
||||||
|
print(textwrap.fill(words, t_width))
|
||||||
|
|
||||||
|
# 用的应该不多,在需要显示长字符串的时候去查textwrap库就行
|
||||||
|
|
||||||
4
2.字符串和文本/17.处理HTML和XML文件.py
Normal file
4
2.字符串和文本/17.处理HTML和XML文件.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
# 这本书的年代有些久远,当时HTML和XML解析器还不是特别完善
|
||||||
|
|
||||||
|
# 在现在的工具中,想要处理HTML和XML文件,只需要寻找HTML Parser和XML解析库就行
|
||||||
37
2.字符串和文本/18.文本分词.py
Normal file
37
2.字符串和文本/18.文本分词.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import re
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
text = 'foo = 23 + 42 * 10'
|
||||||
|
# 我们想要将这个字符串解析成这个样子:
|
||||||
|
# tokens = [('NAME', 'foo'), ('EQ', '='), ('NUMS', '23'), ('PLUS', '+'),
|
||||||
|
# ('NUMS', '42'), ('TIMES', '*'), ('NUMS', '10')]
|
||||||
|
# 首先,我们要对每种情况编写一个捕获组
|
||||||
|
NAME = r'(?P<NAME>[a-zA-Z_][a-zA-Z_0-9]*)'
|
||||||
|
NUM = r'(?P<NUM>\d+)'
|
||||||
|
PLUS = r'(?P<PLUS>\+)'
|
||||||
|
TIMES = r'(?P<TIMES>\*)'
|
||||||
|
EQ = r'(?P<EQ>=)'
|
||||||
|
WS = r'(?P<WS>\s+)'
|
||||||
|
# 把这些捕获组静态编译给re模块
|
||||||
|
master_pat = re.compile('|'.join([NAME,NUM,PLUS,TIMES,EQ,WS]))
|
||||||
|
|
||||||
|
# 定义一个数据结构来存储每个部分
|
||||||
|
Token = namedtuple('Token', ['type', 'value'])
|
||||||
|
|
||||||
|
def generate_tokens(pat, text):
|
||||||
|
# 调用re.compile的scanner模块来扫描,并用iter模块将其变成可迭代对象进行迭代
|
||||||
|
scanner = pat.scanner(text)
|
||||||
|
for m in iter(scanner.match, None):
|
||||||
|
# 生成结构来存储分词
|
||||||
|
yield Token(m.lastgroup, m.group())
|
||||||
|
|
||||||
|
# 对分词类型进行过滤,去掉空格
|
||||||
|
tokens = (tok for tok in generate_tokens(master_pat, text) if tok.type != 'WS')
|
||||||
|
for tok in tokens:
|
||||||
|
print(tok)
|
||||||
|
|
||||||
|
|
||||||
|
# 看起来很简单是吧,唯一的难点就在compile的时候,
|
||||||
|
# 要把大词放前面先搜完再搜小词,保证较长的模式先匹配;比如<=的顺序就应该比<靠前
|
||||||
|
|
||||||
3
2.字符串和文本/19.递归下降解析器.py
Normal file
3
2.字符串和文本/19.递归下降解析器.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
我的评价是去看编译原理的文本解析部分
|
||||||
|
"""
|
||||||
21
2.字符串和文本/2.字符串首尾文字匹配.py
Normal file
21
2.字符串和文本/2.字符串首尾文字匹配.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from numpy.core.defchararray import startswith
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
str = "https://www.baidu.com"
|
||||||
|
str2 = "http://www.baidu.cn"
|
||||||
|
str3 = "suka"
|
||||||
|
# 如果想要在字符串首进行匹配,可以使用:
|
||||||
|
is_start_with_https = str.startswith("https")
|
||||||
|
print(is_start_with_https)
|
||||||
|
# 这会输出一个布尔值,
|
||||||
|
## 比如上面匹配成功,就会输出True
|
||||||
|
|
||||||
|
# 同理,可以设置endswith
|
||||||
|
is_end_with_dotcom = str.endswith(".com")
|
||||||
|
print(is_end_with_dotcom)
|
||||||
|
|
||||||
|
# 如果想要匹配多种开头和结尾,可以将函数输入改成元组()
|
||||||
|
is_url = [url for url in [str, str2, str3] if url.startswith(('http', 'https'))]
|
||||||
|
print(is_url)
|
||||||
|
|
||||||
|
# 当然,复杂的操作还请使用正则表达式进行匹配,不过简单的检查用这个方法那是又快又好
|
||||||
33
2.字符串和文本/20.在字节串上执行文本操作.py
Normal file
33
2.字符串和文本/20.在字节串上执行文本操作.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
from pyasn1.codec.ber.decoder import decode
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
data = b'Hello World'
|
||||||
|
# 目前字节串已经支持大部分和字符串相同的操作
|
||||||
|
data05 = data[0:5]
|
||||||
|
print(data05)
|
||||||
|
|
||||||
|
is_startwith_hello = data.startswith(b'Hello')
|
||||||
|
print(is_startwith_hello)
|
||||||
|
|
||||||
|
data_sp = data.split()
|
||||||
|
print(data_sp)
|
||||||
|
|
||||||
|
data_suka = data.replace(b'Hello', b'Hello Suka')
|
||||||
|
print(data_suka)
|
||||||
|
|
||||||
|
# 唯一需要注意的是,如果用re做了匹配,那记得要用字节串的形式来匹配
|
||||||
|
data = b'foo:bar,spam'
|
||||||
|
try:
|
||||||
|
re.split('[:,]', data)
|
||||||
|
except:
|
||||||
|
print("匹配错误,换字节串匹配")
|
||||||
|
data_bsp = re.split(b'[:,]', data)
|
||||||
|
print(data_bsp)
|
||||||
|
|
||||||
|
# 字节串上的字符不能直接被识别,需要先用ascii解码
|
||||||
|
print(data[0])
|
||||||
|
print(data.decode("ascii")[0])
|
||||||
|
|
||||||
|
# 我的建议是除非万不得已,请不要用字节串处理文本,统一使用ascii解码成字符串后再进行操作
|
||||||
26
2.字符串和文本/3.shell通配符做字符串匹配.py
Normal file
26
2.字符串和文本/3.shell通配符做字符串匹配.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
from fnmatch import fnmatch, fnmatchcase
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 如果想用shell通配符做字符串匹配,可以用上面的这两个函数:
|
||||||
|
is_txt = fnmatch("suka.txt", "*.txt")
|
||||||
|
print(is_txt)
|
||||||
|
is_txt = fnmatchcase("suka.txt", "??ka.txt")
|
||||||
|
print(is_txt)
|
||||||
|
|
||||||
|
# 注意,fnmatchcase不会对输入的name进行大小写标准化,而fnmatch会对输入标准化后再进行匹配
|
||||||
|
# 需要小心的是,大小写标准化的模式与底层文件系统相同,比如windows不需要区分大小写,但是mac要
|
||||||
|
|
||||||
|
address = [
|
||||||
|
'5412 N CLARK ST',
|
||||||
|
'1060 W ADDISON ST',
|
||||||
|
'1039 W GRANVILLE AVE',
|
||||||
|
'2122 N CLARK ST',
|
||||||
|
'4802 N BROADWAY'
|
||||||
|
]
|
||||||
|
|
||||||
|
# 可以用正则进行筛选
|
||||||
|
ST = [addr for addr in address if fnmatchcase(addr, "* ST")]
|
||||||
|
print(ST)
|
||||||
|
NUM_S = [addr for addr in address if fnmatchcase(addr, '54[0-9][0-9] *CLARK*')]
|
||||||
|
print(NUM_S)
|
||||||
|
|
||||||
49
2.字符串和文本/4.使用正则对文本进行匹配和查找.py
Normal file
49
2.字符串和文本/4.使用正则对文本进行匹配和查找.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 如果只是简单的文字匹配或者查找,下面三个方法足以解决问题:
|
||||||
|
url = "http://www.baidu.com"
|
||||||
|
url.startswith("http")
|
||||||
|
url.endswith(".com")
|
||||||
|
url.find("baidu")
|
||||||
|
|
||||||
|
# 但如果是更加复杂的匹配,就要用到re库的正则了
|
||||||
|
text1 = '11/27/2012'
|
||||||
|
text2 = 'Nov 27, 2012'
|
||||||
|
|
||||||
|
if re.match(r'\d+/\d+/\d+', text1):
|
||||||
|
print("yes")
|
||||||
|
else:
|
||||||
|
print("no")
|
||||||
|
|
||||||
|
if re.match(r'\d+/\d+/\d+', text2):
|
||||||
|
print("yes")
|
||||||
|
else:
|
||||||
|
print("no")
|
||||||
|
|
||||||
|
# match可以被一次性消费,但是如果想要多次匹配,就要先把正则编译
|
||||||
|
datepat = re.compile(r'\d+/\d+/\d+')
|
||||||
|
|
||||||
|
if datepat.match(text1):
|
||||||
|
print("yes")
|
||||||
|
else:
|
||||||
|
print("no")
|
||||||
|
|
||||||
|
if datepat.match(text2):
|
||||||
|
print("yes")
|
||||||
|
else:
|
||||||
|
print("no")
|
||||||
|
|
||||||
|
# 这里要注意的是,match方法是从头匹配,如果要匹配的内容在一堆垃圾里面,请使用findall
|
||||||
|
|
||||||
|
# 我们还会使用捕获组,这样可以把每个组单独提取出来
|
||||||
|
datepat = re.compile(r'(\d+)/(\d+)/(\d+)')
|
||||||
|
m = datepat.match(text1)
|
||||||
|
print(m.group(0))
|
||||||
|
print(m.group(1))
|
||||||
|
print(m.group(2))
|
||||||
|
print(m.group(3))
|
||||||
|
|
||||||
|
# match只能匹配开头,它不管结尾,如果想要精确匹配需要加休止符$
|
||||||
|
datepat = re.compile(r'(\d+)/(\d+)/(\d+)$')
|
||||||
|
|
||||||
22
2.字符串和文本/5.查找和替换文本.py
Normal file
22
2.字符串和文本/5.查找和替换文本.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
text = "yeah, but no, but yeah, but no, but yeah"
|
||||||
|
|
||||||
|
# 简单的替换可以使用replace函数来完成
|
||||||
|
text_change = text.replace('yeah', 'yep')
|
||||||
|
print(text_change)
|
||||||
|
|
||||||
|
# 对复杂的替换,我们可以使用re.sub模块
|
||||||
|
text = "Today is 11/27/2012. PyCon starts 3/12/2013"
|
||||||
|
text_change = re.sub(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text)
|
||||||
|
print(text_change)
|
||||||
|
|
||||||
|
# 如果需要多次替换,记得先编译再sub
|
||||||
|
datepat = re.compile(r'(\d+)/(\d+)/(\d+)')
|
||||||
|
print(datepat.sub(r'\3-\1-\2', text))
|
||||||
|
|
||||||
|
# 想要知道完成了几次替换,可以使用subn
|
||||||
|
# subn返回一个元组,结构为(替换后的字符串, 替换次数)
|
||||||
|
print(datepat.subn(r'\3-\1-\2', text))
|
||||||
|
|
||||||
35
2.字符串和文本/6.不区分大小写的对文本进行匹配和区分大小写的替换.py
Normal file
35
2.字符串和文本/6.不区分大小写的对文本进行匹配和区分大小写的替换.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
text = "UPPER PYTHON, lower python, mixed Python"
|
||||||
|
|
||||||
|
# 想要找到所有的python,可以在函数里面找一下有没有flag关键字,并设为re.IGNORECASE
|
||||||
|
|
||||||
|
pythons = re.findall('python', text, flags=re.IGNORECASE)
|
||||||
|
print(pythons)
|
||||||
|
|
||||||
|
# 但是这样在替换的时候就有问题发生,不能把替换文本的大小写设置成和原文本一样
|
||||||
|
python_replace = re.sub('python', 'snake', text, flags=re.IGNORECASE)
|
||||||
|
print(python_replace)
|
||||||
|
# 这个时候我们需要一个辅助函数
|
||||||
|
def matchcase(word):
|
||||||
|
def replace(m):
|
||||||
|
text = m.group()
|
||||||
|
if text.isupper():
|
||||||
|
return word.upper()
|
||||||
|
elif text.islower():
|
||||||
|
return word.lower()
|
||||||
|
elif text[0].isupper():
|
||||||
|
# 这个函数将字符串的首字母大写,其余部分转成小写
|
||||||
|
return word.capitalize()
|
||||||
|
else:
|
||||||
|
return word
|
||||||
|
return replace
|
||||||
|
python_replace_with_func = re.sub('python', matchcase('snake'), text, flags=re.IGNORECASE)
|
||||||
|
print(python_replace_with_func)
|
||||||
|
# 这个辅助函数结构陌生,这里记个笔记备注一下防止以后再看忘记:
|
||||||
|
# 1.首先,sub函数检测到要调用matchcase函数,进入函数体返回replace函数 sub->matchcsae
|
||||||
|
# 2.此时matchcase函数的输入word仍在堆栈中等待调用 matchcase -replace-> sub
|
||||||
|
# 3.在替换的时候,sub函数将识别到的python替换成replace(被识别到的部分) text -> (a1,a2,a3,...)
|
||||||
|
# 4.replace函数返回大小写处理结果 replace(a1), replace(a2), ...
|
||||||
|
# 5.函数返回被替换字符串,re.sub函数进行替换 将识别到的关键词替换成处理过的word
|
||||||
18
2.字符串和文本/7.定义实现最短匹配的正则表达式.py
Normal file
18
2.字符串和文本/7.定义实现最短匹配的正则表达式.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 在进行文本匹配的时候,re库使用的是贪心算法,即找最长匹配字符串
|
||||||
|
text1 = 'Computer says "no".'
|
||||||
|
text2 = 'Computer says "yes" and "no".'
|
||||||
|
# 这样的算法在简单的环境中不会出现问题,但如果遇到闭包匹配(最典型的是双引号)就会出现问题
|
||||||
|
said = re.compile(r'\"(.*)\"')
|
||||||
|
print(said.findall(text1))
|
||||||
|
|
||||||
|
# 看,这里就输出了距离最长的两个引号中间的内容
|
||||||
|
print(said.findall(text2))
|
||||||
|
|
||||||
|
# 问题就出现在.匹配字符上,默认.*会匹配除了\n以外的所有字符,其中也包括“”
|
||||||
|
# 解决方案是强制取消正则的贪心算法,进行最短匹配
|
||||||
|
# 技术上我们在.*后界面加一个?来强制取消贪心
|
||||||
|
said_shot = re.compile(r'\"(.*?)\"')
|
||||||
|
print(said_shot.findall(text2))
|
||||||
20
2.字符串和文本/8.编写多行模式的正则表达式.py
Normal file
20
2.字符串和文本/8.编写多行模式的正则表达式.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 正常情况下,我们会想用.来匹配所有字符串,但这个东西不能匹配换行符
|
||||||
|
text1 = '/*This is a comment*/'
|
||||||
|
text2 = '''/*This is a
|
||||||
|
multiline comment */'''
|
||||||
|
|
||||||
|
comment = re.compile(r'/\*(.*?)\*/')
|
||||||
|
print(comment.findall(text1))
|
||||||
|
print(comment.findall(text2))
|
||||||
|
# 你会发现欸我靠咋匹配不到了,那是因为由于没办法识别\n,第二行被抛弃了
|
||||||
|
# 想要识别出\n,需要使用(?:.|\n)指定一个非捕获组,意思是在识别到/n的时候只做匹配但不触发捕获退出
|
||||||
|
comment_pro = re.compile(r'\*((?:.|\n)*?)\*/')
|
||||||
|
print(comment_pro.findall(text2))
|
||||||
|
|
||||||
|
# 对于这种简单的情况,可以在compile函数中加入一个参数re.DOTALL来让.匹配包括\n在内的所有字符串
|
||||||
|
# PS:复杂情况请另请高明
|
||||||
|
comment_pro_se = re.compile(r'\*(.*?)\*/', re.DOTALL)
|
||||||
|
print(comment_pro_se.findall(text2))
|
||||||
37
2.字符串和文本/9.将unicode文本进行统一规范表示.py
Normal file
37
2.字符串和文本/9.将unicode文本进行统一规范表示.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import unicodedata
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
s1 = 'Spicy Jalape\u00f1o'
|
||||||
|
s2 = 'Spicy Jalapen\u0303o'
|
||||||
|
|
||||||
|
# 可以看到,某些字符串的表示在unicode下有多种选项
|
||||||
|
print(s1,s2)
|
||||||
|
print(s1==s2)
|
||||||
|
|
||||||
|
# 显然,这种情况在我们判断字符串的时候极其不利,这时候我们就需要把unicode编码进行规范
|
||||||
|
|
||||||
|
# 有两种规范方式,NFC全组成和NFD组合字符
|
||||||
|
t1 = unicodedata.normalize('NFD', s1)
|
||||||
|
t2 = unicodedata.normalize('NFD', s2)
|
||||||
|
print(t1, t2)
|
||||||
|
print(t1==t2)
|
||||||
|
print(ascii(t1))
|
||||||
|
|
||||||
|
t3 = unicodedata.normalize('NFC', s1)
|
||||||
|
t4 = unicodedata.normalize('NFC', s2)
|
||||||
|
print(ascii(t3))
|
||||||
|
|
||||||
|
# unicodedata同时还提供NFKC和NFKD编码,这种编码提供了额外的兼容功能,能把下面这种字符分开
|
||||||
|
s = '\ufb01'
|
||||||
|
print(s)
|
||||||
|
print(unicodedata.normalize('NFD', s))
|
||||||
|
print(unicodedata.normalize('NFKC', s))
|
||||||
|
print(unicodedata.normalize('NFKD', s))
|
||||||
|
|
||||||
|
# 如果要去除音符标记~,那么我们可以先用组合字符NFD标准化,再进行去除
|
||||||
|
suka = unicodedata.normalize('NFD', s1)
|
||||||
|
print(suka)
|
||||||
|
suka = ''.join(c for c in unicodedata.normalize('NFD', suka) if not unicodedata.combining(c))
|
||||||
|
print(suka)
|
||||||
|
|
||||||
|
# 在上面的例子里,我们使用了unicodedata.combining()函数来判断字符是否属于组合型字符
|
||||||
16
3.数字日期和时间/1.对数值进行取整.py
Normal file
16
3.数字日期和时间/1.对数值进行取整.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 很多时候,我们需要对浮点数进行取整操作,通常情况下,使用自带的round函数就能解决问题
|
||||||
|
f = 1.23456
|
||||||
|
print(round(f, 2))
|
||||||
|
|
||||||
|
# 虽然round函数可以很方便的对数值进行取整,但一般情况下如果需要规范输出还是用format(.2f)来做
|
||||||
|
ff2 = format(f, '.2f')
|
||||||
|
print(ff2)
|
||||||
|
# 因为浮点数计算因为算法的局限性可能带来一些奇妙的误差
|
||||||
|
a = 2.1
|
||||||
|
b = 4.2
|
||||||
|
print(a+b)
|
||||||
|
# 所以一般来说我们就是算完直接按照需要取小数位数规整完格式就行了
|
||||||
|
# 除非我们的程序对数据精度要求非常高,那这时候就要用decimal库了
|
||||||
16
3.数字日期和时间/10.矩阵和线性代数的计算.py
Normal file
16
3.数字日期和时间/10.矩阵和线性代数的计算.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# np在线性代数处理上有一个子包叫matrix,专门使用线性代数规则来处理数据,比如矩阵乘法、求行列式和解线性方程
|
||||||
|
m = np.matrix([[1,-2,3],[0,4,5],[7,8,-9]])
|
||||||
|
print(m)
|
||||||
|
|
||||||
|
# 做一个矩阵转置
|
||||||
|
print(m.T)
|
||||||
|
# 求可逆矩阵
|
||||||
|
print(m.I)
|
||||||
|
|
||||||
|
v = np.matrix([[2], [3], [4]])
|
||||||
|
print(m * v)
|
||||||
|
|
||||||
|
# 更多线代操作在np.linalg模块下可以找到,随用随查就行
|
||||||
34
3.数字日期和时间/11.随机选择.py
Normal file
34
3.数字日期和时间/11.随机选择.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
from gevent.subprocess import value
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 如果想要进行随机数操作,那么random库是我们最好的选择
|
||||||
|
values = [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||||
|
# 使用random.choice进行抽卡
|
||||||
|
random_num = random.choice(values)
|
||||||
|
print(random_num)
|
||||||
|
|
||||||
|
# 使用random.choices进行多次抽卡(k次),每次抽一张
|
||||||
|
random_nums = random.choices(values, k=2)
|
||||||
|
print(random_nums)
|
||||||
|
|
||||||
|
# 使用random.sample进行抽卡并剔除,这个函数只抽取一次,每次抽k张,所以k不能大于数组长度
|
||||||
|
random_num = random.sample(values, k=3)
|
||||||
|
print(random_num)
|
||||||
|
|
||||||
|
# 如果只是想洗牌,那用shuffle就行了
|
||||||
|
random.shuffle(values) # 注意,这个函数不返回任何东西,它直接在原数组上进行操作
|
||||||
|
print(values)
|
||||||
|
|
||||||
|
# 生成随机数,可以使用random.randint, 它接受两个参数,生成a和b之间的随机数
|
||||||
|
print(random.randint(0, 10))
|
||||||
|
|
||||||
|
# 如果要0-1之间的float,可以用random.random
|
||||||
|
print(random.random())
|
||||||
|
|
||||||
|
# 在配密钥的时候,我们有的时候会想要得到N比特位的随机整数,这时候可以用random.getrandbits(n)
|
||||||
|
print(random.getrandbits(12))
|
||||||
|
|
||||||
|
# 切记,random虽然可以产生随机数,但其使用的梅森旋转算法是确定算法,只要破解seed就能计算出固定值,
|
||||||
|
# 如果需要在密钥中搞到随机数,请使用ssl模块产生随机加密字节
|
||||||
38
3.数字日期和时间/12.时间换算.py
Normal file
38
3.数字日期和时间/12.时间换算.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
from datetime import timedelta, datetime
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 使用timedelta表示时间间隔
|
||||||
|
a = timedelta(days=2, hours=6)
|
||||||
|
b = timedelta(hours=4.5)
|
||||||
|
c = a + b
|
||||||
|
print(c.days)
|
||||||
|
# 这是计算小时的秒数
|
||||||
|
print(c.seconds)
|
||||||
|
# 这是计算天+小时的秒数
|
||||||
|
print(c.total_seconds())
|
||||||
|
|
||||||
|
# 如果要创建特定的日期和时间,可以用datetime来表示
|
||||||
|
time1 = datetime(2024, 8, 28)
|
||||||
|
birthday = datetime(2001, 4, 29)
|
||||||
|
life_time = time1 - birthday
|
||||||
|
print(life_time.days)
|
||||||
|
|
||||||
|
now = datetime.now()
|
||||||
|
print(now)
|
||||||
|
|
||||||
|
# 值得注意的是,datetime模块可以正确处理闰年的二月并计算出正确的天数,我的评价是我直接不管
|
||||||
|
|
||||||
|
# 对绝大部分的问题,datetime模块已经足够使用,但是如果需要处理复杂时间问题,比如时区、模糊时间范围、计算节日日期等
|
||||||
|
# 请他娘的使用dateutil模块
|
||||||
|
|
||||||
|
a = datetime(2012, 9, 23)
|
||||||
|
# 很明显,因为timedelta没有month这个key,所以我们不能直接加一个月,它的精度最大只到days
|
||||||
|
|
||||||
|
# 这时候dateutil下的relativedelta就派上用场
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
b = a + relativedelta(years=10, months=2)
|
||||||
|
print(b)
|
||||||
|
|
||||||
|
# 包括计算日期差
|
||||||
|
d = relativedelta(b, a)
|
||||||
|
print(d.years, d.months, d.days)
|
||||||
31
3.数字日期和时间/13.计算上周五的日期.py
Normal file
31
3.数字日期和时间/13.计算上周五的日期.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
|
||||||
|
|
||||||
|
def get_previous_by_day(day_name, start_date=None):
|
||||||
|
if start_date is None:
|
||||||
|
start_date = datetime.today()
|
||||||
|
|
||||||
|
day_num = start_date.weekday()
|
||||||
|
day_num_target = weekdays.index(day_name)
|
||||||
|
|
||||||
|
days_ago = (7 + day_num - day_num_target) % 7
|
||||||
|
|
||||||
|
if days_ago == 0:
|
||||||
|
days_ago = 7
|
||||||
|
target_date = start_date - timedelta(days=days_ago)
|
||||||
|
return target_date
|
||||||
|
|
||||||
|
get_previous_by_day("Friday")
|
||||||
|
|
||||||
|
# 但是如果要经常这么干,请使用dateutil包
|
||||||
|
d = datetime.now()
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
from dateutil.rrule import *
|
||||||
|
|
||||||
|
# 找最近的周五,FR是rrule里的函数
|
||||||
|
print(d + relativedelta(weekday=FR))
|
||||||
|
|
||||||
|
# 找上一个周五
|
||||||
|
print(d + relativedelta(weekday=FR(-1)))
|
||||||
33
3.数字日期和时间/14.找出当月日期范围.py
Normal file
33
3.数字日期和时间/14.找出当月日期范围.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
from datetime import datetime, date, timedelta
|
||||||
|
import calendar
|
||||||
|
|
||||||
|
from fontTools.misc.plistlib import end_date
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
def get_month_range(start_date=None):
|
||||||
|
if start_date is None:
|
||||||
|
start_date = date.today().replace(day=1)
|
||||||
|
_, days_in_month = calendar.monthrange(start_date.year, start_date.month)
|
||||||
|
|
||||||
|
end_date = start_date + timedelta(days = days_in_month)
|
||||||
|
|
||||||
|
return start_date, end_date
|
||||||
|
|
||||||
|
a_day = timedelta(days=1)
|
||||||
|
first_day, last_day = get_month_range()
|
||||||
|
|
||||||
|
while first_day < last_day:
|
||||||
|
print(first_day)
|
||||||
|
first_day = first_day + a_day
|
||||||
|
|
||||||
|
# 在获取一个月天数的时候,calendar库会非常好用,monthrange会返回第一个工作日的日期和当月的天数
|
||||||
|
|
||||||
|
# 如果想要实现和Python内建的range一样的遍历效果,可以写一个生成器
|
||||||
|
def date_range(start_date, end_date, step=timedelta(days=1)):
|
||||||
|
while start_date < end_date:
|
||||||
|
yield start_date
|
||||||
|
start_date += step
|
||||||
|
|
||||||
|
for d in date_range(first_day, last_day):
|
||||||
|
print(d)
|
||||||
22
3.数字日期和时间/15.将字符串转成日期.py
Normal file
22
3.数字日期和时间/15.将字符串转成日期.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from datetime import datetime, date
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
text = "2012-09-20"
|
||||||
|
|
||||||
|
# str -> time
|
||||||
|
y = datetime.strptime(text, "%Y-%m-%d")
|
||||||
|
z = datetime.now()
|
||||||
|
diff = z - y
|
||||||
|
print(diff)
|
||||||
|
|
||||||
|
# 如果你觉得不够美观,那就格式化一下
|
||||||
|
# time -> str
|
||||||
|
struct_time_str = datetime.strftime(y, "%A %B %d, %Y")
|
||||||
|
print(struct_time_str)
|
||||||
|
|
||||||
|
# 当然,strptime这个函数的性能相当糟糕,大量使用时如果考虑到效率问题还请自己动手
|
||||||
|
def parse_date(date_str):
|
||||||
|
y, m, d = date_str.split("-")
|
||||||
|
return datetime(int(y), int(m), int(d))
|
||||||
|
|
||||||
|
print(parse_date(text))
|
||||||
14
3.数字日期和时间/16.处理涉及时区的日期问题.py
Normal file
14
3.数字日期和时间/16.处理涉及时区的日期问题.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from pytz import timezone
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
d = datetime(2012, 12, 21, 9, 30, 0)
|
||||||
|
print(d)
|
||||||
|
|
||||||
|
central = timezone('US/Central')
|
||||||
|
loc_d = central.localize(d)
|
||||||
|
print(loc_d)
|
||||||
|
|
||||||
|
bang_d = loc_d.astimezone(timezone("Asia/Kolkata"))
|
||||||
|
print(bang_d)
|
||||||
|
|
||||||
12
3.数字日期和时间/2.执行精确小数计算.py
Normal file
12
3.数字日期和时间/2.执行精确小数计算.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 可以看到,使用float的ieee754浮点标准进行计算会产生误差
|
||||||
|
a = 4.2
|
||||||
|
b = 2.1
|
||||||
|
print(a+b)
|
||||||
|
# 如果需要高精度小数,我们的处理方法是将数字字符串转成decimal类型
|
||||||
|
a = Decimal(str(4.2))
|
||||||
|
b = Decimal(str(2.1))
|
||||||
|
print(a+b)
|
||||||
14
3.数字日期和时间/3.对数值做格式化输出.py
Normal file
14
3.数字日期和时间/3.对数值做格式化输出.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
x = 1234.56789
|
||||||
|
# 对数值格式化输出,我们使用经典format函数
|
||||||
|
print(format(x, '.2f'))
|
||||||
|
print(format(x, '+<10.2f'))
|
||||||
|
print(format(x, '=>10.2f'))
|
||||||
|
print(format(x, '-^10.2f'))
|
||||||
|
# 有一些特殊的,比如数值特有的千位逗号1,000
|
||||||
|
print(format(x, ',.2f'))
|
||||||
|
# 如果想用科学计数法,把f改成e就行
|
||||||
|
print(format(x, '.2e'))
|
||||||
|
print(format(x, 'e'))
|
||||||
25
3.数字日期和时间/4.二进制、八进制和十六进制.py
Normal file
25
3.数字日期和时间/4.二进制、八进制和十六进制.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 想要将十进制数字转换成二进制、八进制和十六进制,可以使用内建函数
|
||||||
|
a = 1234
|
||||||
|
a_bin = bin(a)
|
||||||
|
print(a_bin)
|
||||||
|
a_oct = oct(a)
|
||||||
|
print(a_oct)
|
||||||
|
a_hex = hex(a)
|
||||||
|
print(a_hex)
|
||||||
|
|
||||||
|
# 如果不想要出现0b、0o、0x这样的前缀,可以使用format函数格式化做转换
|
||||||
|
print(format(a, 'b'))
|
||||||
|
print(format(a, 'o'))
|
||||||
|
print(format(a, 'x'))
|
||||||
|
|
||||||
|
# 如果我们需要一个32位无符号整数,可以这样干
|
||||||
|
x = 1234
|
||||||
|
x = format(2**32 + x, 'b')
|
||||||
|
print(x)
|
||||||
|
# 要转回10进制的时候用int函数+字符串进制就可以了
|
||||||
|
x = int(a_bin, 2)
|
||||||
|
print(x)
|
||||||
|
|
||||||
35
3.数字日期和时间/5.从字符串中打包和解包大整数.py
Normal file
35
3.数字日期和时间/5.从字符串中打包和解包大整数.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import struct
|
||||||
|
import math
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 假如我们有一个用字节串存储的大整数
|
||||||
|
data = b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'
|
||||||
|
# 要将其还原成整数,我们需要:
|
||||||
|
print(int.from_bytes(data, byteorder="little"))
|
||||||
|
print(int.from_bytes(data, byteorder="big"))
|
||||||
|
|
||||||
|
# 而要将整数转成字节串,我们可以做一个逆向操作
|
||||||
|
int_to_b = int.from_bytes(data, byteorder="little")
|
||||||
|
print(int.to_bytes(int_to_b, byteorder="little", length=16))
|
||||||
|
|
||||||
|
# 在IPV6中,网络地址以一个128位的整数表示,这时候我们可以使用struct模块来完成解包
|
||||||
|
hi, lo = struct.unpack('>QQ', data)
|
||||||
|
print((hi << 64) + lo)
|
||||||
|
|
||||||
|
# 通过byteorder,我们可以指定在编成字节码的时候使用大端排序还是小端排序
|
||||||
|
byte_num = 0x01020304
|
||||||
|
|
||||||
|
print(byte_num.to_bytes(4, 'big'))
|
||||||
|
print(byte_num.to_bytes(4, 'little'))
|
||||||
|
|
||||||
|
# 但是请务必使用合适的字节串大小来保存字节串,否则python会因为防止信息丢失报错
|
||||||
|
x = 523**23
|
||||||
|
try:
|
||||||
|
x.to_bytes(4, 'little')
|
||||||
|
except OverflowError:
|
||||||
|
print("数据溢出")
|
||||||
|
# 这时候就要先算一下字节长度再行转换
|
||||||
|
bit_length = x.bit_length()
|
||||||
|
# 为了让内存结构更加严整,我们要将字节串补全成8的整数倍长度
|
||||||
|
bytes_num = math.ceil(bit_length / 8)
|
||||||
|
print(x.to_bytes(bytes_num, 'little'))
|
||||||
34
3.数字日期和时间/6.复数运算.py
Normal file
34
3.数字日期和时间/6.复数运算.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 在python中,我们使用complex函数或者在浮点数后面加j的方式来定义一个复数
|
||||||
|
a = complex(2,4)
|
||||||
|
b = 2 + 4j
|
||||||
|
print(a, b)
|
||||||
|
|
||||||
|
# 我们可以提取复数的实部、虚部和共轭复数
|
||||||
|
print(a.real)
|
||||||
|
print(a.imag)
|
||||||
|
print(a.conjugate())
|
||||||
|
|
||||||
|
# 同样的,复数可以进行所有常见的算术操作
|
||||||
|
print(a + b)
|
||||||
|
print(a - b)
|
||||||
|
print(a * b)
|
||||||
|
print(a / b)
|
||||||
|
print(abs(a))
|
||||||
|
|
||||||
|
# 但如果需要对复数求正弦余弦或平方根这种操作,请:
|
||||||
|
import cmath
|
||||||
|
cmath.sin(a)
|
||||||
|
cmath.cos(a)
|
||||||
|
cmath.sqrt(a)
|
||||||
|
|
||||||
|
# 使用numpy可以生成复数数组并对它进行操作
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
complex_array = np.array([1+2j, 2+3j, 3+4j])
|
||||||
|
print(complex_array + 2)
|
||||||
|
print(np.sin(complex_array))
|
||||||
|
|
||||||
|
# 在标准的math库中,所有的操作都不会产生复数结果,如果想要产出复数结果,请使用支持复数感知的库,比如cmath
|
||||||
20
3.数字日期和时间/7.处理无穷大和NAN.py
Normal file
20
3.数字日期和时间/7.处理无穷大和NAN.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# python没有原生的东西来表示这些值,但他们可以被创建
|
||||||
|
a = float('inf')
|
||||||
|
b = float('-inf')
|
||||||
|
c = float('nan')
|
||||||
|
print(a, b, c)
|
||||||
|
|
||||||
|
# 如果想要感知这些值,请使用isnan或isinf函数
|
||||||
|
print(math.isnan(c))
|
||||||
|
print(math.isinf(a))
|
||||||
|
|
||||||
|
# 要尤其注意,inf在计算中会被传播
|
||||||
|
print(a+45)
|
||||||
|
# 但是一些神奇操作会产生nan,比如:
|
||||||
|
print(a + b)
|
||||||
|
print(a / a)
|
||||||
|
|
||||||
|
# 尤其注意,nan会在所有的操作中传播,且不会引起任何报错,请务必在计算前进行使用isnan函数进行安全检测
|
||||||
24
3.数字日期和时间/8.分数的计算.py
Normal file
24
3.数字日期和时间/8.分数的计算.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
from fractions import Fraction
|
||||||
|
|
||||||
|
from unicodedata import decimal
|
||||||
|
from xlrd.formula import oMSNG
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 如果遇到分数问题,那么我们就需要使用fractions模块
|
||||||
|
a = Fraction(1, 2)
|
||||||
|
print("这是二分之一:{}".format(a))
|
||||||
|
|
||||||
|
# 同样的,分数可以进行所有的算术运算
|
||||||
|
print(a * a)
|
||||||
|
|
||||||
|
# 我们可以通过一些方法得到分子和分母
|
||||||
|
print("分子是:{}".format(a.numerator))
|
||||||
|
print("分母是:{}".format(a.denominator))
|
||||||
|
|
||||||
|
# 或者我们可以把分数转成浮点数
|
||||||
|
print(float(a))
|
||||||
|
|
||||||
|
# 下面展示如何正确计算高精度的pi/8
|
||||||
|
import numpy as np
|
||||||
|
from decimal import Decimal
|
||||||
|
print(np.pi * Fraction(1, 8))
|
||||||
39
3.数字日期和时间/9.处理大型数组的计算.py
Normal file
39
3.数字日期和时间/9.处理大型数组的计算.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 终于到了最喜欢的环节,没有np我真的会原地爆炸,np万岁
|
||||||
|
|
||||||
|
# 如果是普通的python list, 想要处理是很麻烦的
|
||||||
|
li = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
|
# print(li * 2)
|
||||||
|
|
||||||
|
# 但如果转成nparray,事情就变得简单了起来
|
||||||
|
np_arr = np.array(li)
|
||||||
|
# print(np_arr * 2)
|
||||||
|
|
||||||
|
# np对一维矩阵的运算也做了加强:
|
||||||
|
li2 = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
||||||
|
np_arr2 = np.array(li2)
|
||||||
|
# print(np_arr * np_arr2)
|
||||||
|
|
||||||
|
# 你甚至可以计算多项式的值
|
||||||
|
# print(3 * np_arr**2 + 2 * np_arr + 1)
|
||||||
|
|
||||||
|
# numpy提供了很多通用函数来对nparray进行操作,比如:
|
||||||
|
np.sin(np_arr)
|
||||||
|
np.sqrt(np_arr)
|
||||||
|
# 这些操作的效率很高,平时要多用
|
||||||
|
|
||||||
|
# 在底层,numpy使用和C一样的连续内存空间,所以只要内存够大,你可以为所欲为
|
||||||
|
grid = np.zeros(shape=(10000, 10000), dtype=float)
|
||||||
|
# print(np.sin(np.sqrt(grid + 10)))
|
||||||
|
|
||||||
|
# numpy对多维数组也提供了很好的支持
|
||||||
|
nd_array = np.array([[1,2,3,4,5], [6,7,8,9,10], [11,12,13,14,15]])
|
||||||
|
print(nd_array)
|
||||||
|
print(nd_array[1])
|
||||||
|
print(nd_array[:, 1])
|
||||||
|
print(nd_array[1:3, 1:3])
|
||||||
|
print(nd_array + [1,2,3,4,5])
|
||||||
|
# np.where,没见过,第一个参数是条件,满足就输出第二个参数x,不满足就输出第三个参数y
|
||||||
|
print(np.where(nd_array < 10, nd_array, np.nan))
|
||||||
24
4.迭代器与生成器/1.手动访问迭代器中的元素.py
Normal file
24
4.迭代器与生成器/1.手动访问迭代器中的元素.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
from scipy.stats import trim1
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
items = [1, 2, 3]
|
||||||
|
items = iter(items)
|
||||||
|
# 如果需要访问可迭代对象中的元素,可以使用next函数
|
||||||
|
while True:
|
||||||
|
item = next(items, None)
|
||||||
|
|
||||||
|
if item is not None:
|
||||||
|
print(item)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
# next函数的第一个参数是一个可迭代对象,第二个参数是None,是没有元素的时候的返回值
|
||||||
|
|
||||||
|
# 如果不限制迭代器在没有元素后的返回值,迭代器在迭代结束后会抛出一个StopIteration的错误
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
item = next(items)
|
||||||
|
except StopIteration:
|
||||||
|
print("没东西了")
|
||||||
|
break
|
||||||
|
|
||||||
13
4.迭代器与生成器/10.以键值对的方式迭代索引.py
Normal file
13
4.迭代器与生成器/10.以键值对的方式迭代索引.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
my_list = [1,2,3,4,5,6,7,8,9]
|
||||||
|
# 有时候,我们想要在迭代的时候知道元素的下标,有些傻子(比如我)就很傻逼的做了个计数器
|
||||||
|
# 但是,其实直接使用enumerate就行
|
||||||
|
for index, number in enumerate(my_list, start=0):
|
||||||
|
print(index, number)
|
||||||
|
|
||||||
|
# 但是如果可迭代对象的子元素是元组,那请务必别把元组的标识拆开
|
||||||
|
my_list = [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
|
||||||
|
for index, (x, y) in enumerate(my_list, start=1):
|
||||||
|
print(index, x, y)
|
||||||
30
4.迭代器与生成器/11.迭代多个序列.py
Normal file
30
4.迭代器与生成器/11.迭代多个序列.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 如果想要同时迭代多个序列,那么可以使用zip函数
|
||||||
|
list1 = [1, 2, 3, 4, 5]
|
||||||
|
list2 = ['a', 'b', 'c', 'd', 'e']
|
||||||
|
|
||||||
|
for i, j in zip(list1, list2):
|
||||||
|
print(i, j)
|
||||||
|
|
||||||
|
# zip遵循最短木板原则,迭代长度等于最短的可迭代对象长度,如果想要打破这种限制,请使用itertools.zip_longest()
|
||||||
|
|
||||||
|
from itertools import zip_longest
|
||||||
|
short_list = [1, 2, 3]
|
||||||
|
# fillvalue字段用来填充短列表的缺失值,默认为None
|
||||||
|
for i, j in zip_longest(short_list, list1, fillvalue=0):
|
||||||
|
print(i, j)
|
||||||
|
|
||||||
|
# zip函数会构建一个元组,它的长度与输入的可迭代对象数量有关
|
||||||
|
for i in zip(short_list, list2, list1):
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
# 双对象元组可以直接视作键值对转成字典
|
||||||
|
dic = dict(zip(list1, list2))
|
||||||
|
print(dic)
|
||||||
|
|
||||||
|
# 重点!!!zip函数返回的是一个一次性迭代器,如果需要长久保存,请转成列表在内存里持久化
|
||||||
|
storage_list = list(zip(list1, list2))
|
||||||
|
|
||||||
|
|
||||||
12
4.迭代器与生成器/12.在不同的容器中迭代.py
Normal file
12
4.迭代器与生成器/12.在不同的容器中迭代.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from itertools import chain
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
a = [1,2,3,4,5]
|
||||||
|
b = ['a','b','c','d','e']
|
||||||
|
|
||||||
|
# 正常来说,我们想要迭代这两个东西需要写两个循环构建两个迭代器,但这样很操蛋,不如直接用chain把他俩作链表连起来
|
||||||
|
for i in chain(a, b):
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
# 使用chain可以避开 a + b 要求a和b类型相同的限制,同时这种操作更加快速,因为其底层是用指针完成,而不会创建一个新的东西
|
||||||
|
|
||||||
41
4.迭代器与生成器/13.创建处理数据的管道.py
Normal file
41
4.迭代器与生成器/13.创建处理数据的管道.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import os
|
||||||
|
from fnmatch import fnmatchcase
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
base_path = "D:\Code\Learn\practice"
|
||||||
|
|
||||||
|
# 想要建立一个处理管道,需要将几个生成器函数堆叠,比如,我现在要输出所有practice文件夹下的注释行内容
|
||||||
|
# 1.首先生成文件夹路径
|
||||||
|
def gen_dir_path(fpath):
|
||||||
|
for path, dir, files in os.walk(fpath):
|
||||||
|
for file in files:
|
||||||
|
if (not fnmatchcase(path, "*.[git][idea]*")) and (not fnmatchcase(file, "*.md")):
|
||||||
|
yield os.path.join(path, file)
|
||||||
|
|
||||||
|
# 2.打开文件
|
||||||
|
def gen_file(paths):
|
||||||
|
for path in paths:
|
||||||
|
f = open(path, 'rt', encoding='utf-8')
|
||||||
|
yield f
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
# 3.读取文件内容
|
||||||
|
def gen_file_txt(opened_files):
|
||||||
|
for file in opened_files:
|
||||||
|
yield from file
|
||||||
|
print("done")
|
||||||
|
|
||||||
|
# 4.匹配符合条件的文件行
|
||||||
|
def read_file_lines(file_lines):
|
||||||
|
for line in file_lines:
|
||||||
|
if "#" in line:
|
||||||
|
print(line)
|
||||||
|
|
||||||
|
# 将这些函数堆在一起就组成了一条管道,每次生成一个对象进行流水线处理,
|
||||||
|
# 这样避免了for循环的嵌套
|
||||||
|
paths = gen_dir_path(base_path)
|
||||||
|
files = gen_file(paths)
|
||||||
|
lines = gen_file_txt(files)
|
||||||
|
read_file_lines(lines)
|
||||||
|
|
||||||
|
|
||||||
18
4.迭代器与生成器/14.扁平化处理嵌套型的序列.py
Normal file
18
4.迭代器与生成器/14.扁平化处理嵌套型的序列.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from collections import Iterable
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
# 将复杂的序列扁平化,使用递归来脱壳
|
||||||
|
def flatten(items, ignore=(str, bytes)):
|
||||||
|
for item in items:
|
||||||
|
# 要进行递归,首先这个元素要是可迭代的,其次它不能是字符串或二进制串
|
||||||
|
if isinstance(item, Iterable) and not isinstance(item, ignore):
|
||||||
|
# yield from 将请求转发到flatten函数返回的迭代器进行嵌套,避免再写for循环
|
||||||
|
# yield from 在后面的协程和基于生成器的并发程序里有重要作用
|
||||||
|
yield from flatten(item, ignore)
|
||||||
|
else:
|
||||||
|
yield item
|
||||||
|
|
||||||
|
a = [1, 2, [3, 4, [5, 6], 7], 8]
|
||||||
|
for item in flatten(a):
|
||||||
|
print(item)
|
||||||
17
4.迭代器与生成器/15.合并多个有序序列,再对整个有序序列进行迭代.py
Normal file
17
4.迭代器与生成器/15.合并多个有序序列,再对整个有序序列进行迭代.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import heapq
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
a = [1, 4, 7, 10]
|
||||||
|
b = [2, 5, 6, 11]
|
||||||
|
|
||||||
|
# 首先复习一下前面的知识,如果不考虑合并后也是有序的,用chain
|
||||||
|
for item in chain(a, b):
|
||||||
|
print(item)
|
||||||
|
|
||||||
|
# 但如果我们想要得到有序合并序列,那就要用堆自带的merge功能了,利用小根堆的特殊性来排序
|
||||||
|
new_arr = heapq.merge(a, b)
|
||||||
|
for item in new_arr:
|
||||||
|
print(item)
|
||||||
|
|
||||||
|
# 记住,heapq的merge事先不会对输入可迭代对象的有序性进行检查,而是每次从可迭代对象序列里取出最小的第一个进堆
|
||||||
1
4.迭代器与生成器/16.test.txt
Normal file
1
4.迭代器与生成器/16.test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Hello PythonCookBook
|
||||||
33
4.迭代器与生成器/16.用迭代器取代while循环.py
Normal file
33
4.迭代器与生成器/16.用迭代器取代while循环.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
CHUNKSIZE = 10
|
||||||
|
|
||||||
|
def render(s):
|
||||||
|
# 军火展示
|
||||||
|
print(s.read())
|
||||||
|
s.seek(0)
|
||||||
|
|
||||||
|
# 在处理文件的时候,我们习惯用while循环来迭代数据
|
||||||
|
print("一般while处理:")
|
||||||
|
while True:
|
||||||
|
data = s.read(CHUNKSIZE)
|
||||||
|
if data == b'':
|
||||||
|
break
|
||||||
|
print(data)
|
||||||
|
|
||||||
|
s.seek(0)
|
||||||
|
|
||||||
|
|
||||||
|
# 但是可以使用迭代器来升级一下
|
||||||
|
print("进化成迭代器:")
|
||||||
|
for chunk in iter(lambda: s.read(CHUNKSIZE), b''):
|
||||||
|
print(chunk)
|
||||||
|
|
||||||
|
path = "4.迭代器与生成器/16.test.txt"
|
||||||
|
# 二进制打开文件记得用rb,下一章进化一下文件读取方面的知识
|
||||||
|
f = open(path, 'rb')
|
||||||
|
|
||||||
|
render(f)
|
||||||
|
print("Done")
|
||||||
|
|
||||||
28
4.迭代器与生成器/2.委托迭代.py
Normal file
28
4.迭代器与生成器/2.委托迭代.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
class Node:
|
||||||
|
def __init__(self, value):
|
||||||
|
self._value = value
|
||||||
|
self._children = []
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'Node({!r})'.format(self._value)
|
||||||
|
|
||||||
|
def add_child(self, node):
|
||||||
|
self._children.append(node)
|
||||||
|
|
||||||
|
# 对象的内置iter方法,在迭代对象的时候将迭代请求转发,返回children的迭代器对象
|
||||||
|
# iter函数和len函数一样,底层原理都是返回对象的底层方法,iter返回对象的__iter__()
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._children)
|
||||||
|
|
||||||
|
root = Node(0)
|
||||||
|
child1 = Node(1)
|
||||||
|
child2 = Node(2)
|
||||||
|
root.add_child(child1)
|
||||||
|
root.add_child(child2)
|
||||||
|
|
||||||
|
for ch in root:
|
||||||
|
print(ch)
|
||||||
|
|
||||||
52
4.迭代器与生成器/3.使用生成器创建新的迭代模式.py
Normal file
52
4.迭代器与生成器/3.使用生成器创建新的迭代模式.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
def fbb(start, stop, step):
|
||||||
|
x = start
|
||||||
|
while x <= stop:
|
||||||
|
yield x
|
||||||
|
x += step
|
||||||
|
# 上面是一个生成器函数,它只会在相应迭代时运行
|
||||||
|
|
||||||
|
# 在调用生成器函数的时候,因为直接生成了数字,所以有返回值
|
||||||
|
for i in fbb(0, 10, 1):
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
# 尝试使用fbb生成金字塔
|
||||||
|
|
||||||
|
# 先用生成器整一个斐波那契数列生成函数
|
||||||
|
def fbb_arr(n):
|
||||||
|
index = 0
|
||||||
|
a0 = 1
|
||||||
|
a1 = 1
|
||||||
|
while index < n:
|
||||||
|
yield a0
|
||||||
|
a0, a1 = a1, a0 + a1
|
||||||
|
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
# 按照n行打印出来
|
||||||
|
def print_num_delta(n):
|
||||||
|
# 计算需要生成多少个数字
|
||||||
|
fbb_number_num = sum(range(1, n + 1))
|
||||||
|
# 生成数字并保存
|
||||||
|
fbb_list = list(fbb_arr(fbb_number_num))
|
||||||
|
# 数字下标,用于遍历中保存位置
|
||||||
|
start_index = 0
|
||||||
|
|
||||||
|
# 每一行的数字数量
|
||||||
|
for number_num in range(1, n + 1):
|
||||||
|
# 切片用空格分隔后转成字符串
|
||||||
|
nums = ' '.join(str(item) for item in fbb_list[start_index: start_index + number_num])
|
||||||
|
# 格式化打印
|
||||||
|
print(format(nums, r' ^{}'.format(len(str(fbb_list[-1])) * n)))
|
||||||
|
# 更新下一行的数字下标
|
||||||
|
start_index = start_index + number_num
|
||||||
|
|
||||||
|
print_num_delta(5)
|
||||||
|
|
||||||
|
# 生成器里为什么没有return: 如果一个函数里包含yield语句,那就会被识别为生成器函数,
|
||||||
|
# 函数里的return此时自动失效被替换成生成器
|
||||||
|
# 生成器函数在识别到return时,意识到生成结束,返回的StopIteration,交给for识别
|
||||||
|
|
||||||
31
4.迭代器与生成器/4.迭代协议.py
Normal file
31
4.迭代器与生成器/4.迭代协议.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# 这是标准的
|
||||||
|
class Node:
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
self._children = []
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "Node({})".format(self.value)
|
||||||
|
|
||||||
|
def add_child(self, node):
|
||||||
|
self._children.append(node)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._children)
|
||||||
|
|
||||||
|
def depth_first(self):
|
||||||
|
yield self
|
||||||
|
for child in self:
|
||||||
|
yield from child.depth_first()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
root = Node(0)
|
||||||
|
child1 = Node(1)
|
||||||
|
child2 = Node(2)
|
||||||
|
root.add_child(child1)
|
||||||
|
root.add_child(child2)
|
||||||
|
child1.add_child(Node(3))
|
||||||
|
child2.add_child(Node(4))
|
||||||
|
|
||||||
|
for ch in root.depth_first():
|
||||||
|
print(ch)
|
||||||
31
4.迭代器与生成器/5.反向迭代.py
Normal file
31
4.迭代器与生成器/5.反向迭代.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
a = [1, 2, 3, 4]
|
||||||
|
# python自带的reversed函数搞定了反向迭代,其本质时调用了__reversed__方法
|
||||||
|
for i in reversed(a):
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
for i in a.__reversed__():
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
# 如果想要实现类的反向迭代,可以在实现__iter__()方法的同时搞一个__reversed__()方法
|
||||||
|
class Countdown:
|
||||||
|
def __init__(self, start):
|
||||||
|
self.start = start
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
n = self.start
|
||||||
|
while n > 1:
|
||||||
|
yield n
|
||||||
|
n -= 1
|
||||||
|
|
||||||
|
def __reversed__(self):
|
||||||
|
n = 1
|
||||||
|
while n <= self.start:
|
||||||
|
yield n
|
||||||
|
n += 1
|
||||||
|
|
||||||
|
a = Countdown(10)
|
||||||
|
for i in reversed(a):
|
||||||
|
print(i)
|
||||||
38
4.迭代器与生成器/6.定义带有额外状态的生成器函数.py
Normal file
38
4.迭代器与生成器/6.定义带有额外状态的生成器函数.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
from collections import deque
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 在学到了生成器函数以后,有一个巨大陷阱那就是尝试用生成器函数解决一切问题
|
||||||
|
# 当生成器函数需要与外界交互的时候,就会让它非常复杂
|
||||||
|
# 所以生成器我们就让它安心生成,在需要记录或者与外界交互的时候,写一个生成器类
|
||||||
|
class FbbIterClass:
|
||||||
|
def __init__(self,nums, history_len=3):
|
||||||
|
self.nums = nums
|
||||||
|
self.history = deque(maxlen=history_len)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
a0, a1 = 1, 1
|
||||||
|
index = 0
|
||||||
|
while index < self.nums:
|
||||||
|
self.history.append(a0)
|
||||||
|
yield a0
|
||||||
|
a0, a1 = a1, a0 + a1
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
fbbs = FbbIterClass(5)
|
||||||
|
|
||||||
|
# 先用生成器生成,结果在生成的过程中会存入历史队列
|
||||||
|
for i in fbbs:
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
# 当然,在使用for之外的方法进行迭代时,需要额外套一层iter来转发请求到__iter__()
|
||||||
|
fbb_iter = iter(fbbs)
|
||||||
|
print(fbb_iter.__next__())
|
||||||
|
print(fbb_iter.__next__())
|
||||||
|
print(fbb_iter.__next__())
|
||||||
|
print(fbb_iter.__next__())
|
||||||
|
print(fbb_iter.__next__())
|
||||||
|
|
||||||
|
# 这种方法的好处是,可以在迭代时随时暴露一些东西给外部程序,比如属性和状态
|
||||||
|
for line in fbbs.history:
|
||||||
|
print("history{}".format(line))
|
||||||
|
|
||||||
26
4.迭代器与生成器/7.对迭代器做切片操作.py
Normal file
26
4.迭代器与生成器/7.对迭代器做切片操作.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
from itertools import islice
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 在程序生成过程中,我们有时候需要做一些无限生成器,这些生成器往往需要切片使用
|
||||||
|
def infinity_iterator(start):
|
||||||
|
while True:
|
||||||
|
yield start
|
||||||
|
start += 1
|
||||||
|
|
||||||
|
# 无限生成器
|
||||||
|
for i in infinity_iterator(0):
|
||||||
|
print(i)
|
||||||
|
break
|
||||||
|
|
||||||
|
# 想要对无限生成器进行切片,我们就需要使用itertools里的islice函数,记得这个函数也是左闭右开
|
||||||
|
iterator= infinity_iterator(0)
|
||||||
|
iter_10_20 = islice(infinity_iterator(0), 10, 21)
|
||||||
|
for i in iter_10_20:
|
||||||
|
print(i)
|
||||||
|
# 注意!!!islice操作是通过丢弃索引序列前的元素实现的,返回的是一个一次性迭代器,被迭代过后元素就被消耗掉无了
|
||||||
|
print("再次使用迭代")
|
||||||
|
for i in iter_10_20:
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
22
4.迭代器与生成器/8.跳过可迭代对象中的前一部分元素.py
Normal file
22
4.迭代器与生成器/8.跳过可迭代对象中的前一部分元素.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from itertools import dropwhile, islice
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
words = ["#look", "#into", "#my", "eyes", "look", "into", "my", "eyes",
|
||||||
|
"the", "eyes", "the", "eyes", "the", "eyes", "not", "around", "the",
|
||||||
|
"eyes", "don't", "look", "around", "the", "eyes", "look", "into",
|
||||||
|
"my", "eyes", "you're", "under"]
|
||||||
|
|
||||||
|
# 想要对可迭代对象进行过滤,也可以使用dropwhile函数,比如这里我想过滤掉前几行有#的东西
|
||||||
|
for i in dropwhile(lambda x : x.startswith('#'), words):
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
# dropwhile会在迭代时对函数进行匹配,将符合条件的值丢弃,直到出现第一个符合条件的值后停止工作返回所有剩余内容
|
||||||
|
|
||||||
|
# 如果你知道需要跳过几个元素,那可以直接用islice进行切片操作来过滤
|
||||||
|
|
||||||
|
items = ['a', 'b', 'c', 1, 2, 3]
|
||||||
|
# None表示[3:]
|
||||||
|
for i in islice(items, 3, None):
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
# 当然,使用列表的过滤方法也可以删除一些不要的东西,但是这样的删除是全局的,而不是仅在开头生效
|
||||||
23
4.迭代器与生成器/9.迭代所有的排列组合.py
Normal file
23
4.迭代器与生成器/9.迭代所有的排列组合.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
from itertools import permutations, combinations
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 有时候我们想对可迭代对象中的元素进行排列组合,这个时候我们就不得不靠for循环写出很不python的代码
|
||||||
|
# itertools库帮助我们解决了这个困扰
|
||||||
|
items= ['a', 'b', 'c', 'd', 'e']
|
||||||
|
|
||||||
|
# permutations会列出所有的排列,如果指定排列长度,那就会输出该长度下所有的排列
|
||||||
|
for p in permutations(items):
|
||||||
|
print(p)
|
||||||
|
|
||||||
|
for p in permutations(items, 3):
|
||||||
|
print(p)
|
||||||
|
|
||||||
|
# 如果想要得到特定长度的组合,那就可以使用combination函数
|
||||||
|
for c in combinations(items, 3):
|
||||||
|
print(c)
|
||||||
|
|
||||||
|
# 上面这种组合是不放回的组合,如果想产生放回元素的组合,可以使用combinations_with_replacement函数
|
||||||
|
from itertools import combinations_with_replacement as cwr
|
||||||
|
for c in cwr(items, 3):
|
||||||
|
print(c)
|
||||||
|
|
||||||
1
5.文件与IO/1.somefile.txt
Normal file
1
5.文件与IO/1.somefile.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Hello Python File
|
||||||
47
5.文件与IO/1.读写文本数据.py
Normal file
47
5.文件与IO/1.读写文本数据.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
path = "5.文件与IO/1.somefile.txt"
|
||||||
|
|
||||||
|
# 如果想要读取文件,那open函数是唯一的选择,它有很多模式:
|
||||||
|
# # 只读组, 一般用于配置信息
|
||||||
|
# open(path, 'rb')
|
||||||
|
# open(path, 'rt')
|
||||||
|
#
|
||||||
|
# # 只写覆盖模式,将指针指向文件头,一般用于文件输出或覆盖旧内容
|
||||||
|
# open(path, 'wb')
|
||||||
|
# open(path, 'wt')
|
||||||
|
#
|
||||||
|
# # 只写追加模式,将指针从默认的文件头指向文件尾
|
||||||
|
# open(path, 'ab')
|
||||||
|
# open(path, 'at')
|
||||||
|
|
||||||
|
# 如果上面的任何一种模式后面有+,那么将会变成读写模式
|
||||||
|
|
||||||
|
# 在打开文件时,可以指定文件的编码格式,默认编码模式可以使用sys库进行查询
|
||||||
|
import sys
|
||||||
|
|
||||||
|
print(sys.getdefaultencoding())
|
||||||
|
|
||||||
|
# 当我们想打开文件的时候,不建议直接使用上面的open函数,因为这样需要每次记住手动关闭打开的文件,像这样
|
||||||
|
# 当然,二进制写入不需要指定encoding, 正常来说需要指定encoding='utf-8'
|
||||||
|
a = open(path, 'wb')
|
||||||
|
a.write(b'Hello Python File\n')
|
||||||
|
a.close()
|
||||||
|
|
||||||
|
# 我们可以使用with语句来为文件创建一个上下文环境,在程序离开with的程序段以后,会自动关闭文件
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
# 记住,如果是二进制写入,解出来的时候要用utf-8解码一下
|
||||||
|
print(f.read())
|
||||||
|
|
||||||
|
# 可以看到文件已经被关闭了
|
||||||
|
print(f.closed)
|
||||||
|
|
||||||
|
# 在UNIX和Windows上,换行符不太相同,UNIX是\n,而windows是\r\n
|
||||||
|
# Python在读取文件时做了内置的转换,统一换行符为\n,如果不需要这种转换,可以设置newline=''
|
||||||
|
open(path, 'rb', newline='')
|
||||||
|
|
||||||
|
# 如果遇见解码错误,只需要更换encoding就行, 也可以设置错误解决方案字段errors
|
||||||
|
open(path, 'rb', encoding='utf-8', errors='replace')
|
||||||
|
|
||||||
|
|
||||||
BIN
5.文件与IO/10.data
Normal file
BIN
5.文件与IO/10.data
Normal file
Binary file not shown.
44
5.文件与IO/10.对二进制文件做内存映射.py
Normal file
44
5.文件与IO/10.对二进制文件做内存映射.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import os
|
||||||
|
import mmap
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
def memory_map(file_path, access=mmap.ACCESS_WRITE):
|
||||||
|
size = os.path.getsize(file_path)
|
||||||
|
fild_data = os.open(file_path, os.O_RDWR)
|
||||||
|
|
||||||
|
return mmap.mmap(fild_data, size, access=access)
|
||||||
|
|
||||||
|
SIZE = 1000000
|
||||||
|
# with open(r'5.文件与IO/10.data', 'wb') as f:
|
||||||
|
# f.seek(SIZE - 1)
|
||||||
|
# f.write(b'\x00')
|
||||||
|
|
||||||
|
m = memory_map('5.文件与IO/10.data')
|
||||||
|
print(len(m))
|
||||||
|
m[0:11] = b'Hello World'
|
||||||
|
m.close()
|
||||||
|
|
||||||
|
with open(r'5.文件与IO/10.data', 'rb') as f:
|
||||||
|
print(f.read(11))
|
||||||
|
|
||||||
|
# 当然,这个函数得益于他的open内嵌,也能在with的上下文中打开并自动关闭
|
||||||
|
with memory_map('5.文件与IO/10.data') as m:
|
||||||
|
print(len(m))
|
||||||
|
print(m.closed)
|
||||||
|
|
||||||
|
# 当然,如果你想只读,可以在access里设置成mmp.ACCESS_READ,
|
||||||
|
# 如果只想把文件拷贝到内存而不是直接在文件里修改,mmp.ACCESS_COPY非常有用
|
||||||
|
# mmap和memoryview又有所不同
|
||||||
|
m = memory_map('5.文件与IO/10.data')
|
||||||
|
v = memoryview(m).cast('I')
|
||||||
|
v[0] = 7
|
||||||
|
m[0:4] = b'\x07\x01\x00\x00'
|
||||||
|
|
||||||
|
print(m[0:4])
|
||||||
|
print(v[0])
|
||||||
|
# 可以看出来,memoryview在做转换的时候用的是小端存储
|
||||||
|
print(int.from_bytes(m[0:4], byteorder='little'))
|
||||||
|
print(int.from_bytes(m[0:4], byteorder='big'))
|
||||||
|
v.release()
|
||||||
|
m.close()
|
||||||
20
5.文件与IO/11.处理路径.py
Normal file
20
5.文件与IO/11.处理路径.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
path = '/Users/beazley/Data/data.csv'
|
||||||
|
|
||||||
|
# 如果我们想要得到地址的最后一部分,可以使用basename字段
|
||||||
|
print(os.path.basename(path))
|
||||||
|
|
||||||
|
# 前面的部分作为文件夹,可以使用dirname读出来
|
||||||
|
print(os.path.dirname(path))
|
||||||
|
|
||||||
|
# 如果想要拼接多个路径,则可以使用join
|
||||||
|
print(os.path.join('suka', 'blyet', os.path.basename(path)))
|
||||||
|
|
||||||
|
# 想要提取出文件名,可以使用splitext
|
||||||
|
print(os.path.splitext(os.path.basename(path)))
|
||||||
|
|
||||||
|
# 也可以使用expanduser展开上级目录,默认接控制台下的路径
|
||||||
|
path = "~\practice"
|
||||||
|
print(os.path.expanduser(path))
|
||||||
22
5.文件与IO/12.检测文件是否存在.py
Normal file
22
5.文件与IO/12.检测文件是否存在.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 如果想知道文件是否存在,可以使用os.path.exists
|
||||||
|
# 这个函数返回一个布尔值,如果存在就返回T,否则返回F
|
||||||
|
print(os.path.exists("file_not_exists.txt"))
|
||||||
|
print(os.path.exists("5.文件与IO/1.somefile.txt"))
|
||||||
|
|
||||||
|
# 判断是不是一个文件,可以使用isfile方法
|
||||||
|
print(os.path.isfile("5.文件与IO/1.somefile.txt"))
|
||||||
|
# 判断是不是一个文件夹,可以使用isdir方法
|
||||||
|
print(os.path.isdir("5.文件与IO"))
|
||||||
|
# 判断是不是一个软连接,可以使用islink方法
|
||||||
|
print(os.path.isdir("5.文件与IO"))
|
||||||
|
# 想要知道islink的快捷方式指向的地址,可以使用realpath方法
|
||||||
|
print(os.path.realpath("5.文件与IO"))
|
||||||
|
|
||||||
|
# os.path模块可以解决大部分路径问题,包括切片、判断、拼接等,唯一注意的就是记得给python程序访问路径的权限
|
||||||
|
|
||||||
|
# 如果要得到大小和修改日期,也有如下解决方案:
|
||||||
|
os.path.getsize("5.文件与IO/1.somefile.txt")
|
||||||
|
os.path.getmtime("5.文件与IO/1.somefile.txt")
|
||||||
9
5.文件与IO/13.获取目录内容的列表.py
Normal file
9
5.文件与IO/13.获取目录内容的列表.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from os import listdir
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 如果想要得到路径下所有文件的列表,那么我们可以使用listdir方法
|
||||||
|
print(listdir("5.文件与IO"))
|
||||||
|
|
||||||
|
# listdir函数返回一个文件列表
|
||||||
|
print(type(listdir()))
|
||||||
|
|
||||||
9
5.文件与IO/14.绕过文件名编码.py
Normal file
9
5.文件与IO/14.绕过文件名编码.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 默认情况下,文件名是根据系统编码来进行编码的
|
||||||
|
print(sys.getdefaultencoding())
|
||||||
|
|
||||||
|
# 如果你想要无视这种编码规则,可以使用字节串来指定文件名
|
||||||
|
with open(b'test.txt', 'w') as f:
|
||||||
|
print(f.read())
|
||||||
14
5.文件与IO/15.打印无法解码的文件名.py
Normal file
14
5.文件与IO/15.打印无法解码的文件名.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 有时候,可能会出现一些标准情况下打印会出现问题的文件名,他们不遵循系统的默认编码方式
|
||||||
|
# 这时候如果print这些文件名,那么py会因为无法解码而将字符映射到一个代理编码表,显然print无法处理这些代理为空的新建编码
|
||||||
|
# 解决的方案是建一个转换函数
|
||||||
|
def bad_filename(filename):
|
||||||
|
return repr(filename)[1: -1]
|
||||||
|
|
||||||
|
name = ''
|
||||||
|
try:
|
||||||
|
print(name)
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
print(bad_filename(name))
|
||||||
40
5.文件与IO/16.为已经打开的文件修改编码方式.py
Normal file
40
5.文件与IO/16.为已经打开的文件修改编码方式.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import urllib.request
|
||||||
|
import io
|
||||||
|
import gzip
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
req = urllib.request.Request('http://www.python.org/')
|
||||||
|
# 网页返回有时候会被压缩,用这句话让服务器帮我们解压完发过来,别给.gz
|
||||||
|
req.add_header('Accept-Encoding', 'gzip,deflate')
|
||||||
|
req.AutomaticDecompression='DecompressionMethods.GZip'
|
||||||
|
u = urllib.request.urlopen(req)
|
||||||
|
# 如果想将u以utf-8的方式添加编码,可以使用io.TextIOWrapper对它进行封装
|
||||||
|
text = io.TextIOWrapper(u, encoding='utf-8')
|
||||||
|
print(text.read(10))
|
||||||
|
|
||||||
|
# 如果文件已经以文本形式打开,想要更换编码层,可以先将原有的编码层移除并替换
|
||||||
|
text = io.TextIOWrapper(text.detach(), encoding='latin-1')
|
||||||
|
print(text.read(10))
|
||||||
|
|
||||||
|
# Python打开文件时,将文件分为三层,源文件、源文件的二进制缓存buffer和解码器
|
||||||
|
f = open("5.文件与IO/1.somefile.txt", 'rt')
|
||||||
|
# 解码器层
|
||||||
|
print(f)
|
||||||
|
# 二进制缓存
|
||||||
|
print(f.buffer)
|
||||||
|
# 源文件
|
||||||
|
print(f.buffer.raw)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
# 如果想要改变decoder,最好的办法就是使用detach()函数返回上一层的buffer,然后再用io.TextIOWraper封装
|
||||||
|
f = open("5.文件与IO/1.somefile.txt", 'rt')
|
||||||
|
print(f)
|
||||||
|
b = f.detach()
|
||||||
|
print(b)
|
||||||
|
# 返回buffer层后再对buffer重编码
|
||||||
|
new = io.TextIOWrapper(b, encoding='latin-1', errors='xmlcharrefreplace')
|
||||||
|
print(new)
|
||||||
|
|
||||||
|
|
||||||
1
5.文件与IO/17.try.txt
Normal file
1
5.文件与IO/17.try.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
cakae
|
||||||
12
5.文件与IO/17.将字节数据写入文本文件.py
Normal file
12
5.文件与IO/17.将字节数据写入文本文件.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import io
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
file = "5.文件与IO/17.try.txt"
|
||||||
|
|
||||||
|
# 上一章我们知道了文件打开后都长什么样子,想要往文件里写字节数据怎么办?直接往buffer里怼就完了、
|
||||||
|
# 这样可以绕过文件的编码层
|
||||||
|
f = open(file, "w")
|
||||||
|
f.buffer.write(b"write")
|
||||||
|
|
||||||
|
|
||||||
|
f.close()
|
||||||
10
5.文件与IO/18.将已有的文件描述符包装为文件对象.pyi
Normal file
10
5.文件与IO/18.将已有的文件描述符包装为文件对象.pyi
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# open函数除了可以对文件对象进行操作以外,也可以打开一些由系统创建的文件描述符
|
||||||
|
fd = os.open("5.文件与IO/17.try.txt", os.O_WRONLY | os.O_CREAT)
|
||||||
|
|
||||||
|
# 当文件被关闭时,这些由系统创建的IO管道也会随之关闭
|
||||||
|
f = open(fd, 'wt')
|
||||||
|
f.write("ca")
|
||||||
|
f.close()
|
||||||
27
5.文件与IO/19.创建临时文件和目录.py
Normal file
27
5.文件与IO/19.创建临时文件和目录.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from tempfile import TemporaryFile, NamedTemporaryFile, TemporaryDirectory
|
||||||
|
|
||||||
|
from bottle import delete
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
with TemporaryFile('w+t') as f:
|
||||||
|
f.write("hello")
|
||||||
|
f.write("temp_file")
|
||||||
|
|
||||||
|
f.seek(0)
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
print(data)
|
||||||
|
print(f.closed)
|
||||||
|
|
||||||
|
# 如果想要对临时文件命名,使用NamedTemporaryFile,如果不想文件或文件夹在关闭后自动删除,请使用delete=False
|
||||||
|
# 跑完记得手动干掉它
|
||||||
|
with NamedTemporaryFile('w+t', delete=False) as f:
|
||||||
|
print("file name is : " + f.name)
|
||||||
|
|
||||||
|
# 你甚至可以创建一个临时文件夹
|
||||||
|
with TemporaryDirectory() as d:
|
||||||
|
print(d)
|
||||||
|
|
||||||
|
# 对临时文件,可以指定前缀后缀和文件夹,记住,文件夹一定要真的存在
|
||||||
|
with NamedTemporaryFile(prefix="PPP", suffix=".txt", dir=d) as f:
|
||||||
|
print("file name is : " + f.name)
|
||||||
1
5.文件与IO/2.somefile.txt
Normal file
1
5.文件与IO/2.somefile.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
|
||||||
10
5.文件与IO/2.将输出重定向到文件中.py
Normal file
10
5.文件与IO/2.将输出重定向到文件中.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 如果想要将东西输出到文件中,可以在print函数中指定file字段
|
||||||
|
# print函数的默认结尾是\n,如果想要替换这个默认结尾,可以在end字段设置
|
||||||
|
path = r"5.文件与IO/2.somefile.txt"
|
||||||
|
with open(path, "at") as f:
|
||||||
|
for i in range(10):
|
||||||
|
print(i, file=f, end=' ')
|
||||||
|
print("Done")
|
||||||
1
5.文件与IO/20.与串口进行通信.py
Normal file
1
5.文件与IO/20.与串口进行通信.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# 这部分是硬件通信相关内容,先不学
|
||||||
BIN
5.文件与IO/21.test.bin
Normal file
BIN
5.文件与IO/21.test.bin
Normal file
Binary file not shown.
23
5.文件与IO/21.序列化Python对象.py
Normal file
23
5.文件与IO/21.序列化Python对象.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import pickle
|
||||||
|
|
||||||
|
class Person:
|
||||||
|
def __init__(self, name, age):
|
||||||
|
self.name = name
|
||||||
|
self.age = age
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 对于一般的程序来说,可以使用pickle作为对象的序列化器
|
||||||
|
a = Person('a', 20)
|
||||||
|
file = open("5.文件与IO/21.test.bin", 'wb')
|
||||||
|
# 将序列化对象a写入file
|
||||||
|
pickle.dump(a, file)
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
file = open("5.文件与IO/21.test.bin", 'rb')
|
||||||
|
# 从file读出序列化对象进行反序列化
|
||||||
|
a = pickle.load(file)
|
||||||
|
print(a.name, a.age)
|
||||||
|
|
||||||
|
# 值得一提的是,正在运行的线程被pickle打断后,会保存当前的状态,
|
||||||
|
# 当线程再次从文件中被读取,会继续之前的进度
|
||||||
8
5.文件与IO/3.以不同的分隔符或行结尾符完成打印.py
Normal file
8
5.文件与IO/3.以不同的分隔符或行结尾符完成打印.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 这是普通的打印
|
||||||
|
print(1,2,3)
|
||||||
|
# 想要使用分隔符来隔开数字,可以在sep字段设置分隔符
|
||||||
|
print(1,2,3, sep="..")
|
||||||
|
|
||||||
BIN
5.文件与IO/4.bin_file.bin
Normal file
BIN
5.文件与IO/4.bin_file.bin
Normal file
Binary file not shown.
43
5.文件与IO/4.读写二进制数据.py
Normal file
43
5.文件与IO/4.读写二进制数据.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
from base64 import decode
|
||||||
|
|
||||||
|
|
||||||
|
def write_bin_file(bin_path):
|
||||||
|
with open(bin_path, 'wb') as f:
|
||||||
|
f.write(b'Hello BinFile')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
path = "5.文件与IO/4.bin_file.bin"
|
||||||
|
|
||||||
|
write_bin_file(path)
|
||||||
|
# 如果想要打开二进制文件,那就需要做'rb'模式
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
print(f.read())
|
||||||
|
|
||||||
|
# 在打印的时候也有差异,要记得先utf-8转码,不然出来的是ascii
|
||||||
|
a = b'Hello BinFile'
|
||||||
|
for i in a:
|
||||||
|
print(i, end=' ')
|
||||||
|
print()
|
||||||
|
|
||||||
|
for i in a.decode('utf-8'):
|
||||||
|
print(i, end='')
|
||||||
|
print()
|
||||||
|
|
||||||
|
# C语言结构体和数组这种自带缓冲区接口的东西,可以直接读写文件,而不用先用encoder编码
|
||||||
|
import array
|
||||||
|
a = array.array('i', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||||
|
# 往文件里写array
|
||||||
|
with open(path, 'wb') as f:
|
||||||
|
f.write(a)
|
||||||
|
|
||||||
|
# 直接把bin array从文件里薅出来塞到b的缓冲区
|
||||||
|
b = array.array('i', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
f.readinto(b)
|
||||||
|
|
||||||
|
print(b)
|
||||||
|
|
||||||
|
# 但是这么干有风险,要谨慎应对
|
||||||
|
|
||||||
|
|
||||||
1
5.文件与IO/5.missing_file.bin
Normal file
1
5.文件与IO/5.missing_file.bin
Normal file
@@ -0,0 +1 @@
|
|||||||
|
missing file
|
||||||
17
5.文件与IO/5.对已不存在的文件执行写入操作.py
Normal file
17
5.文件与IO/5.对已不存在的文件执行写入操作.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 如果想要将数据写入一个已经不在文件系统中的文件,可以使用open函数的x模式
|
||||||
|
|
||||||
|
try:
|
||||||
|
path = "5.文件与IO/4.bin_file.bin"
|
||||||
|
with open(path, 'xt') as f:
|
||||||
|
f.write("missing file")
|
||||||
|
# 可以看到,这个写入报了一个文件已存在的错误,如果我们用一个不存在的文件,那就会创建一个新文件成功写入
|
||||||
|
except FileExistsError:
|
||||||
|
path = "5.文件与IO/5.missing_file.bin"
|
||||||
|
with open(path, 'xt') as f:
|
||||||
|
f.write("missing file")
|
||||||
|
|
||||||
|
# 如果需要写入二进制文件,同理使用xb就行
|
||||||
|
|
||||||
26
5.文件与IO/6.在字符串上执行IO操作.py
Normal file
26
5.文件与IO/6.在字符串上执行IO操作.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import io
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# io库里的StringIO和BytesIO提供了两个模拟文件的方法,本质应该是在内存里整了块缓冲区
|
||||||
|
# StringIO提供了对字符串的虚拟IO,BytesIO则对应字节串
|
||||||
|
# 这两个东西的操作和文件操作别无二致,就是不需要打开
|
||||||
|
s = io.StringIO()
|
||||||
|
b = io.BytesIO()
|
||||||
|
|
||||||
|
s.write("Hello_String_IO\n")
|
||||||
|
b.write(b'Hello BytesIO')
|
||||||
|
|
||||||
|
print("This is POWER!!!!!", file=s, end='')
|
||||||
|
|
||||||
|
print(s.getvalue())
|
||||||
|
s.seek(0)
|
||||||
|
|
||||||
|
print(s.read(4))
|
||||||
|
|
||||||
|
s.seek(0)
|
||||||
|
print(s.read())
|
||||||
|
|
||||||
|
print(b.getvalue())
|
||||||
|
b.seek(0)
|
||||||
|
print(b.read(4))
|
||||||
|
print(b.read())
|
||||||
18
5.文件与IO/7.读写压缩的数据文件.py
Normal file
18
5.文件与IO/7.读写压缩的数据文件.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import gzip, bz2
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 如果想要读写.gz和.bz2后缀的压缩文件,请使用上述两个包
|
||||||
|
with gzip.open("file.gz", 'rt') as f:
|
||||||
|
text = f.read()
|
||||||
|
|
||||||
|
|
||||||
|
with bz2.open("file.bz2", 'rt') as f:
|
||||||
|
text2 = f.read()
|
||||||
|
|
||||||
|
# 同样的,如果写入可以使用wt和wb来进行,这些包里的open函数支持和open函数相同的操作
|
||||||
|
# 如果想要增加压缩比,那就需要调整compresslevel参数,最高是默认9,调低它以获取更高的性能
|
||||||
|
|
||||||
|
# 这些函数还支持对二进制打开的压缩文件做类似的管道操作
|
||||||
|
f = open("somefile.gz", 'rb')
|
||||||
|
with gzip.open(f, 'rt') as g:
|
||||||
|
text3 = g.read()
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user