V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
cxa
V2EX  ›  Python

Python 的技巧和方法你了解多少?

  •  
  •   cxa · 2018-11-23 10:58:09 +08:00 · 2427 次点击
    这是一个创建于 2196 天前的主题,其中的信息可能已经有所发展或是发生改变。

    学了这些你的 python 代码将会改善与你的技巧将会提高。

    1. 路径操作

    比起 os 模块的 path 方法,python3 标准库的 pathlib 模块的 Path 处理起路径更加的容易。 ####获取当前文件路径 前提导入 os 和 pathlib 包。。 os 版:

        print(os.path.dirname(__file__))
        print(os.getcwd())
    

    pathlib 版:

         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 生成的是个对象<class 'pathlib.PosixPath'>,在 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 学习开发。

    5 条回复    2018-11-26 19:43:40 +08:00
    putin541
        1
    putin541  
       2018-11-24 10:22:38 +08:00
    a=["hello","bye"][2<1]

    你到底是经历了什么,为什么会写出这种代码
    Nick2VIPUser
        2
    Nick2VIPUser  
       2018-11-24 12:35:57 +08:00
    字典的逻辑运算挺有用
    cxa
        3
    cxa  
    OP
       2018-11-25 18:57:28 +08:00
    ["hello","bye"][2<1] 不建议用 为了了解 false 和 true 实际等同于 0,1 就好了
    juoyi
        4
    juoyi  
       2018-11-26 12:54:32 +08:00
    python cookbook
    largecat
        5
    largecat  
       2018-11-26 19:43:40 +08:00
    @putin541 他的意思应该是可以继续引申 2>1 的地方可以用其他东西代替..
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1035 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 22:06 · PVG 06:06 · LAX 14:06 · JFK 17:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.