学了这些你的 python 代码将会改善, 你的技巧将会提高.
1. 路径操作
比起 os 模块的 path 方法, python3 标准库的 pathlib 模块的 Path 处理起路径更加的容易.
获取当前文件路径
前提导入 os 和 pathlib 包..
os 版:
- print(os.path.dirname(__file__))
- print(os.getcwd())
pathlib 版:
- (adsbygoogle = Windows.adsbygoogle || []).push({
- });
- print(pathlib.Path.cwd())
看着好像没啥区别, 然后看下面这个.
获取上两级文件目录
os 版
print(os.path.dirname(os.path.dirname(os.getcwd())))
pathlib 版
print(pathlib.Path.cwd().parent.parent)
拼接路径
os 版
print(os.path.join(os.path.dirname(os.path.dirname(os.getcwd())),"yamls","a.yaml"))
pathlib 版
- parts=["yamls","a.yaml"]
- print(pathlib.Path.cwd().parent.parent.joinpath(*parts))
运行时拼接路径
os 版
os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'yamls',f'{site_name}.yaml')
pathlib 版
- parts=["yamls","a.yaml"]
- print(pathlib.Path(__file__).resolve().parent.parent.joinpath(*parts))
另外 pathlib 生成的是个对象, 在 open 文件操作中可以直接运行的但是如果当作字符串操作会出现错误, 此时需要对其进行转换, 使用 os.fspath() 即可, 不过一般很少有操作路径字符串的习惯.
综合起来, 还是 pathlib 拼接路径方便.
2. 保存标准格式的 YAML 文件
编程免不了要写配置文件, 怎么写配置也是一门学问.
YAML 是专门用来写配置文件的语言, 非常简洁和强大, 远比 JSON 格式方便.
YAML 在 python 语言中有 PyYAML 安装包.
前提安装第三方库
- pip install pyaml
- pip install ruamel.YAML
关于 YAML 的读取知识网上一堆了我就不说了, 这里主要说写入.
- from ruamel import YAML
- data={"age":23,"sex":"男","name":"牛皮"}
- with open(conf_file, "w", encoding='utf-8') as fs:
- YAML.dump(data, fs, Dumper=YAML.RoundTripDumper, allow_unicode=True)
YAML 写文件和 JSON 一样也是使用 dump.
3. 同时迭代两个列表
以前的时候我是这么解决的
- a = ["a", "b", "c", "d"]
- b = [1, 2, 3] # 空的补充 None
- for index, a_item in enumerate(a):
- b_item = None
- if len(b) - 1 <= index:
- pass
- else:
- b_item = b[index]
- print({a_item:b_item})
现在我通过 itertools 标准库的 zip 升级版 zip_longest 解决, 可以通过 fillvalue 参数补充缺失值. 当然如果比较的元素个数相同可以直接用 zip.
- from itertools import zip_longest
- a = ["a", "b", "c", "d","e"]
- b = [1, 2, 3] # 空的补充 None
- for a_item, b_item in zip_longest(a,b,fillvalue=0):
- print({a_item:b_item})
4. 三元表达式还能这么用?
一般的我们这样写
- a="hello" if 2>1 else "bye"
- print(a)
我们知道 python 中 false 实际式 0,true 是 1, 所以对于上面的式子我们就可以这么写了.
- a=["hello","bye"][2<1]
- print(a)
因为 2<1 是 false 也就是 0, 所以输出了第一个元素 hello.
5. 简单的类使用 namedtuple 代替
先来一个简单的例子
- import collections
- # Person=collections.namedtuple('Person','name age')
- # 如果使用 python 中的关键字会出现错误, 此时使用 rename 字段.
- # 按照元素在元组中的下标赋值. class 就是_2,def 是_3
- Person = collections.namedtuple('Person', ['name', 'age', 'class', 'def', 'name', 'name'], rename=True)
- p = Person(name='lisa', age='12', _2="class2", _3="def", _4="name2", _5="name3")
- print(p)
- # 如果出现相同的字段第二次出现的时候也是用其下标, 参考上面的例子.
- # _fields 查看字段名, 可以发现内置模块和重复的字段标记为_加下标的形式
- print(p._fields)
- # 使用_asdict 将 namedtuple 转为 OrderedDict.
- od = p._asdict()
- print(od)
- # 然后可以转为字典
- print(dict(od))
- # _replace() 方法构建一个新实例, 因为 namedtuple 是不可变类型所以这个方法可以返回一个新的对象.
- new_p = p._replace(name="samJ")
- print(new_p)
- print(new_p is p) # 可以看到不是同一个对象.
一个实用的例子 pyppeteer 的例子感受下
- import asyncio
- import pyppeteer
- from collections import namedtuple
- Response = namedtuple("rs", "title url html cookies headers history status")
- async def get_html(url, timeout=30):
- # 默认 30s
- browser = await pyppeteer.launch(headless=True, args=['--no-sandbox'])
- page = await browser.newPage()
- res = await page.goto(url, options={'timeout': int(timeout * 1000)})
- data = await page.content()
- title = await page.title()
- resp_cookies = await page.cookies()
- resp_headers = res.headers
- resp_history = None
- resp_status = res.status
- response = Response(title=title, url=url,
- HTML=data,
- cookies=resp_cookies,
- headers=resp_headers,
- history=resp_history,
- status=resp_status)
- return response
- if __name__ == '__main__':
- url_list = ["http://www.10086.cn/index/tj/index_220_220.html", "http://www.10010.com/net5/011/",
- "http://python.jobbole.com/87541/"]
- task = (get_html(url) for url in url_list)
- loop = asyncio.get_event_loop()
- results = loop.run_until_complete(asyncio.gather(*task))
- for res in results:
- print(res.title)
6 使用枚举让数字变得更易懂.
- import enum
- # 枚举
- @enum.unique
- class Sex(enum.Enum):
- man = 12
- woman = 13
- # 因为加了唯一值的装饰器所以下面添加属性会报错
- # boy=12
- print(Sex.man.name)
- print(Sex.woman.value)
- # 遍历
- for item in Sex:
- print(item.name)
- print(item.value)
- print("-" * 40)
- # 其他使用方式
- words = enum.Enum(
- value='item',
- names=('a b c d e f'),
- )
- # 输出元素 c, 必须是上面 names 里含有的值
- print(words.c)
- print(words.f)
- # 因为 names 不含有 w 所以报错
- try:
- print(words.w)
- except AttributeError as e:
- print(e.args)
- print("-" * 40)
- for Word in words:
- print(Word.name, Word.value) # 默认赋值为, 从 1 开始自增.
- print("-" * 40)
- # 如果自定义元素的值啧改为一下元组的形式
- words2 = enum.Enum(
- value='item2',
- names=[('a', 23), ('b', 56), ("c", 12), ("d", 333)]
- )
- for word2 in words2:
- print(word2.name, word2.value)
7 链式合并字典 chainmap 的使用
- from collections import ChainMap
- # ChainMap
- d1 = {'a': 1, 'b': 2}
- d2 = {'a2': 3, 'b2': 4}
- d3 = {'a3': 5, 'b3': 6}
- d4 = {'a4': 7, 'b4': 8}
- c = ChainMap(d1, d2, d3, d4) # 多个字典合并为一个
- for k, v in c.items():
- print(k, v)
- print(c.maps) # 要搜索的索引列表
- c.maps = list(reversed(c.maps)) # 逆转映射列表
- print(c)
- # 因为 c 和 d1-d4 对应的索引位置实际是一个所以, 修改 c 的时候会影响到 d1 到 d4 其中饿的一个值, 同理修改
- # d1-d4 的时候也会影响到 c.
- # 所以使用 new_child 创建一个新的映射. 再修改就影响不到底层的数据了.
- c2 = c.new_child()
- c2["a4"] = 100
- print(c)
- print(c2)
- # 输出发现 c 的值没有发生变化, 只要 c2 变化.
- d5 = {"a5": 34, "b5": 78}
- c2 = c2.new_child(d5) # 可以在原来的映射基础上添加新的映射
- print(c2)
8 在不打乱列表顺序的基础上插入元素
- import bisect
- """
- bisect 模块, 用于维护有序列表.
- bisect 模块实现了一个算法用于插入元素到有序列表.
- 在一些情况下, 这比反复排序列表或构造一个大的列表再排序的效率更高.
- Bisect 是二分法的意思, 这里使用二分法来排序, 它会将一个元素插入到一个有序列表的合适位置,
- 这使得不需要每次调用 sort 的方式维护有序列表.
- """
- values = [14, 85, 77, 26, 50, 45, 66, 79, 10, 3, 84, 77, 1]
- print("New Pos Content")
- print("--- --- -------")
- l = []
- for i in values:
- postion = bisect.bisect(l, i) # 返回插入的位置
- bisect.insort(l, i) # 等于 insort_right
- print('{:3}{:3}'.format(i, postion), l)
- """
- Bisect 模块提供的函数有:
- bisect.bisect_left(a,x, lo=0, hi=len(a)) :
- 查找在有序列表 a 中插入 x 的 index.lo 和 hi 用于指定列表的区间, 默认是使用整个列表. 如果 x 已经存在, 在其左边插入. 返回值为 index.
- bisect.bisect_right(a,x, lo=0, hi=len(a))
- bisect.bisect(a, x,lo=0, hi=len(a)) :
- 这 2 个函数和 bisect_left 类似, 但如果 x 已经存在, 在其右边插入.
- bisect.insort_left(a,x, lo=0, hi=len(a)) :
- 在有序列表 a 中插入 x. 和 a.insert(bisect.bisect_left(a,x, lo, hi), x) 的效果相同.
- bisect.insort_right(a,x, lo=0, hi=len(a))
- bisect.insort(a, x,lo=0, hi=len(a)) :
- 和 insort_left 类似, 但如果 x 已经存在, 在其右边插入.
- Bisect 模块提供的函数可以分两类: bisect* 只用于查找 index, 不进行实际的插入;
- 而 insort* 则用于实际插入. 该模块比较典型的应用是计算分数等级:
- """
8 关于字典的逻辑运算你了解多少
- # 使用 & 操作符查看字典的相同之处
- # 字典键支持常见的集合操作, 并集交集差集.
- a = {
- 'x': 1, 'y': 2, 'z': 3
- }
- b = {
- 'w': 2, 'z': 4, 'x': 3, 'z': 3
- }
- # 获取相同的键
- c = a.keys() & b.keys()
- print(c)
- # 获取相同的键值对
- d = a.items() & b.items()
- print(d)
- # 创建一个新的字典并删除某些键
- e = {
- k: a[k] for k in a.keys() - {
- 'z', 'x'
- }
- }
- print(e)
9 给切片起个名字
- a="safr3.14"
- print(a[-4:])
- # 上面可以改为
- pie=slice(len(a)-4,len(a))
- print(a)
10 获取出现频率高的元素
- from collections import Counter
- text = "abcdfegtehto;grgtgjri" # 可迭代对象
- lis = ["a", "c", "d", "t", "b"]
- dic = {
- "a": 1, "b": 4, "c": 2, "d": 9
- } # 字典也可以
- c = Counter() # 可以定义空容器然后 update
- c.update(text)
- c2 = Counter()
- c2.update(dic)
- c3 = Counter(lis) # 也可以直接传入对象
- print(c)
- print(c2)
- print(c3)
- # 使用 c.most_comman(n) 获取前 n 出现频率最高的元素, 列表元组类型
- print(c.most_common(4))
更多工具使用以及 python 技巧, 请关注公众号: python 学习开发.
来源: https://juejin.im/post/5bf7704f5188252098021670