Python编程方面的一些技巧


有补充的可以联系我

如果有更好的技巧或者是一些心得也可以分享给我~

  • Author: Leo
  • Contributor: Leo
  • Wechat: Leo-sunhailin
  • E-mail: 379978424@qq.com
  • 会一直维护下去,待补充...


1. list切片的技巧 somelist[start:end:stride]

test = [1, 2, 3, 4, 5]

# 从索引最开始到结束,每隔两个取出一个
# 实际上就是肉眼数的奇数位,索引的偶数位
odds = test[::2]
print(odds) # 结果 -> [1, 3, 5]

# 从索引第一位到结束,每隔两个取出一个
# 实际上就是肉眼数的偶数位,索引的奇数位
evens = test[1::2]
print(evens) # 结果 -> [2, 4]

# 对于byte的字符串来说还有神奇的特效(only byte)
byte_str = b'abcd'
print(byte_str[::-1]) # 结果 -> dcba

# 尽量不要很复杂的切片方式,尽量能够多次解决复杂
# 不要同时出现start end stride三个参数复制代码
2. 多使用列表表达式
# 1. 例子没有
# list, dict, set都有对列表表达式的支持
# 列表表达式代替使用map和filter,可以避免写lambda函数

# 2. 例子
# 列表表达式处理多重for循环
martix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat_list = [x for row in martix for x in row]
print(flat_list) # 结果 -> [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 拆解代码大致就是:
new_list = []
for row in martix:
    for x in row:
        new_list.append(x)

# 3.例子
# 从一个列表中找出一个数字大于4且是偶数的
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 第一种
b = [x for x in a if x > 4 if x % 2 ==0]
# 第二种
c = [x for x in a if x > 4 and x % 2 ==0]
# 结果都是一样的,只是判断上的区别

# Tips:
# 列表表达式能用就尽量用,可以缩减一些代码量,但是不要写的过于复杂
# 太复杂的表达式,查bug更难找,而且也不利于别人进行维护复制代码
3. 数据量大的时候尽量使用生成器表达式代替列表表达式
# 原因很简单,列表表达式需要开辟较大的内存空间进行存储
"""
官方解释: 生成器表达式,它是对推导和生成器的一种泛化。
生成器在运行时不会将整个输出序列呈现出来,而是会估值为迭代器,
这个迭代器每次可以根据生成器表达式产生一项数据。
"""

# 1. 例子
# 读取一个多行文本,统计每一行的长度
it = (len(x) for x in open('<文件路径>/<文件名>.<文件后缀>')
print(it) # 结果 -> <generator object <genexpr> at 某个内存地址>

# 需要输出时就用next
print(next(it))复制代码
4. 使用enumerate代替range
# 原因很简单,封装的比range好
test = ['vannila', 'chocolate', 'pecan', 'strawberry']
# 第一种
for i , flavor in enumerate(test):
    print('%d: %s' % (i + 1, flavor))
# 结果如下
>>>
1: vannila
2: chcolate
3: pecan
4: strawberry
# 解释一下: i + 1 实际上就是为了更好看,如果不i + 1,实际上就是索引的位置.

# 第二种
for i , flavor in enumerate(test, 1):
    print('%d: %s' % (i, flavor))

# 结果一样, 实际上就是在enumerate的函数中已经封装了
# 显得更简便,而且同时能输出索引位置或输出实际中的计数位复制代码
5. 合理利用try/except/else/finally
# except的例子就不说了,用过都知道
# 直接上else的例子

# 函数的功能就是: 加载一个json,返回对应key的值
def load_json_key(data, key)
    try:
        result_dict = json.loads(data)
    except ValueError as err:
        raise KeyError from err
    else:
        return result_dict[key]

"""
解释:
    实际上这个else可要可不要,因为写在try里面也是可以的
    但是如果为了代码的可阅读性,else是一个很必要的东西
    代码阅读上就知道try里面是代码中可能存在错误的的地方
    如果写在一堆的话,还有错误,那你的except就要增加多几个了
    而且写代码也并不建议嵌套try-except,毕竟那不服合代码的风格.
"""

# finally的话,实际上就一个代码清理的过程
# 一般用在IO或者数据库读写上,用来关闭流, 例子就不写了.复制代码
6. 线程方面的---使用concurrent.futures,实现并行计算
# coding: utf-8

from concurrent.futures import ThreadPoolExecutor as Pool
# from multiprocessing import Pool
import requests
import time

urls = ["http://www.gzcc.cn", "http://jwxw.gzcc.cn",
        "http://www.baidu.com", "http://www.qq.com",
        "http://www.163.com", "http://www.sohu.com"]


def task(url, timeout=10):
    return requests.get(url=url, timeout=timeout)


if __name__ == '__main__':
    start_1 = time.time()
    for each_url in urls:
        response = task(url=each_url)
        print('%s, %s' % (response.url, response.status_code))
    end_1 = time.time()
    print("顺序执行的时长: %f" % (end_1 - start_1))

    start_2 = time.time()
    pool = Pool(max_workers=4)
    # pool = Pool(processes=4)
    processes = pool.map(task, urls)
    for each_process in processes:
        print('%s, %s' % (each_process.url, each_process.status_code))
    end_2 = time.time()
    print("并行执行的时长: %f" % (end_2 - start_2))

# 第一种的结果: 1.4s
# 第二种的结果: 0.4s
# 结果的提升是肯定有的,但是和网络情况有关系。

# 关于导入的包 concurrent.future
# 对于这个包里面的ThreadPoolExecutor和multiprocessing的Pool对比,作用实际上差不多,具体时间差异我还没怎么测试过.
# 但是如果你认真看源码的话会发现,实际上future的包在process的那一块也是调用multiprocessing的
# 按照源码的意思就是在子线程中运行多个python的解释器,从而实现并行.
# 但是一般的代码或者多线程爬虫上基本体会不出,因为爬虫的核心还是在网络速度上,而一般的代码也没必要
# 除非计算矩阵或者其他的需要巨大计算量的时候再考虑使用.复制代码
7. 与分析方面有关的---重视精度时使用decimal
# 例子
rate = 1.45
seconds = 3 * 60 + 42
cost = rate * seconds / 60
print(cost) # 结果很奇怪: 5.364999999999999

# 这时候可能会想到用round的函数
# 1、如果这时你的需求是不足一分也当一分的计算
#    类似于向上取整round的方法会把结果变成5.36而不是5.37
# 2、如果没有要求的时候使用round就可以了
# 
# 针对第一种问题,就引出一个decimal的方法了,改写一下
from decimal import Decimal

rate = Decimal('1.45')
seconds = Decimal(3 * 60 + 42)
cost = rate * seconds / Decimal(60)
print(cost) # 结果 -> 5.365

# 重点说下这里。
# 有个很奇怪的地方,有兴趣的可以研究下为什么。
# 把rate的那个1.45去掉单引号包围,再运行就明白为什么奇怪了

# 反观结果, 5.365貌似也不是我们想要的,这里就引入一个quantize方法了
# 在代码顶部加上 
from decimal import ROUND_UP

# cost还是刚刚的cost
rounded = cost.quantize(Decimal('0.01'), rounding=ROUND_UP)
print(rounded) # 结果 -> 5.37

# 兜兜转转就到结果这里了.一般这些情况都是对精度要求很高才需要,一般情况就当看不见好了.复制代码
8. 协作开发的时候尽量不要写import *
你的代码在导包的时候写了import *,你自己开发是很明白有什么方法的.
但是在协作开发或者开源项目的时候尽量避免.
因为其他开发者并不知道里面的方法是干啥的.复制代码
9. 配置文件独立化
# 例如一些数据库的配置,selenium的webdriver的配置,甚至开发的模式配置可以通过一些json格式的配置文件进行维护.
# 好处1: 在于这样管理项目不用"东奔西跑",为了一个全局变量找半天
# 好处2: 在协同开发的时候,可以不用变动的代码的情况下,根据自己的开发环境确定一些全局配置
# 缺点的话,实际上也算不上缺点.就是每次都要读取一次配置文件,代码的速度会减慢一点点,但是并不碍事.复制代码

回到顶部: 传送门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值