Python编程技巧和需要注意的地方——Pro

2023-11-15

目录

前言

1.多个大数字相加

2.同时访问多个相互关联的列表

3.访问列表的同时输出对应下标

4.熟练使用*和_

5.关于类的get和set方法

6.熟练使用help和dir方法

7.什么时候使用set或dict而不是list?

8.默认参数需要用list时需谨慎

9.运行代码尽量使用main函数

11. 正确创建二(多)维列表

12.完结撒花

前言

           --11.08更新,又增加了一条。小伙伴动动手指点个关注吧!非常感谢你的支持!

        这篇文章应该是这个系列最后一篇了,部分内容较前两篇来说算比较进阶吧,没看过前两篇的小伙伴可以先看看前两篇哦,说不定也会对你有帮助呢。传送门:

python的一些小技巧和知识点(一)

python的一些小技巧和知识点(二)

        正文开始,本篇主要记录一些写代码时可能有用的技巧和可能会出错的一些情况。

1.多个大数字相加

        当程序里需要用到很大的数字时,可以用下划线_来分隔每3位数字,这样能方便人阅读,而程序会忽略这些下划线,得到正确结果,例如一个亿级数和一个百万级数相加时,就可以用下划线来分隔,帮助我们更好的阅读。例如:

num1 = 23_333_333_333
num2 = 23_333_333                    
result = num1 + num2
print(result)
# 23356666666

        输出结果没有下划线分隔符,如果我们想要输出也便于我们阅读,那就可以用格式化的输出,来让输出也更好看,方便我们调试:

print(f'{total:,}')
# 23,356,666,666
print(f'{result:_}')
# 23_356_666_666

2.同时访问多个相互关联的列表

        设想一下,有多个列表,它们的内容相互联系,你想在一个循环里每次同时遍历并列的几个内容,或者叫它们item,例如下面这几个列表:

heros = ['Peter Parker', 'Tony Stark', 'Steven Rogers', 'Natasha', 'Clint Barton', 'Stephen Strange']
make_up_names = ['Spider Man', 'Iron Man', 'Captain American', 'Black Widow', 'Hawk Eye', 'Dr. Strange']
    

         现在,我们想在一个循环里依次遍历他们,输出名字和对应的英雄称号,如果你对C很熟悉的话,你可能首先想到的时用一个下标index来记录位置,每次用这个下标来访问元素,就像这样:

index = 0
for index in range(len(heros)):
    print('{} is {}'.format(heros[index], make_up_names[index]))
'''
Peter Parker is Spider Man
Tony Stark is Iron Man
Steven Rogers is Captain American
Natasha is Black Widow
Clint Barton is Hawk Eye
Stephen Strange is Dr. Strange
'''

         虽然这样也能实现功能,但看起来还是略显麻烦,还记得前面文章里提到的zip吗?当遇到这种情况时,它能帮助你更加优雅的访问,就像这样:

for hero, name in zip(heros, make_up_names):
    print('{} is {}'.format(hero, name))

'''
Peter Parker is Spider Man
Tony Stark is Iron Man
Steven Rogers is Captain American
Natasha is Black Widow
Clint Barton is Hawk Eye
Stephen Strange is Dr. Strange
'''

        优雅的同时,效率也更高,当需要访问的列表更多的时候,也可以再接着往zip里加,还是上面这个例子,假如我们再多一个列表记录他们每个角色所拥有的个人电影数,访问名字的同时输出他们的电影数。

individual_movies = [9, 3, 3, 1, 0, 2]
for hero, name, movie in zip(heros, make_up_names, individual_movies):
    print('{} is {}, he/she has {} individual movies'.format(hero, name, movie))

'''
Peter Parker is Spider Man, he/she has 9 individual movies
Tony Stark is Iron Man, he/she has 3 individual movies
Steven Rogers is Captain American, he/she has 3 individual movies
Natasha is Black Widow, he/she has 1 individual movies
Clint Barton is Hawk Eye, he/she has 0 individual movies
Stephen Strange is Dr. Strange, he/she has 2 individual movies
'''

         需要注意的是,zip函数只会取多个列表里最短的长度作为公共长度进行合并并返回,如果想要最长的长度或指定其他长度,得另外指定参数,且比较麻烦,这里就不说了(其实是我也没用过),而且返回的元素组成的也是一个元组tuple类型,再依次将元组里的内容赋给hero,name,movie这三个变量,这三个变量也只是指向元组内容的引用。还记得前面说的元组类型是不可变对象吗?因此在循环体里不能对元组本身进行修改,例如像下面这样的话就会报错:

for pair in zip(heros, make_up_names, individual_movies):
    pair[0] += 'a'

3.访问列表的同时输出对应下标

        还是上面的heros列表,现在我们想一行输出一个名字同时输出下标,同样,熟练C的小伙伴可能还是习惯用下标来访问,比如

for(int i = 0; i < length; i++){
    printf("balala\n");
}

         或者用python就是:

index = 0
for h in heros:
    print(index, h)
    index += 1
'''
0 Peter Parker
1 Tony Stark
2 Steven Rogers
3 Natasha
4 Clint Barton
5 Stephen Strange
'''

        这看起来不够“python”,有人可能会说,“同时输出下标和内容,这不是可以用pandas库的Series类型来完成吗?”别扛,你都会用pandas了,还看我这个文章干嘛。让我们用python的方式来解决这个问题:

for index, h in enumerate(heros):
    print(index, h)

        enumerate关键字将所给参数组合成一个带索引的序列,默认下标从0开始,可以指定索引开始的位置,

for index, h in enumerate(heros, start = 1):
    print(index, h)

4.熟练使用*和_

        聪明的你说不定已经发现,上面的zip操作有点像是将参数都打包(packing)然后再分给hero, name, movie这三个变量(unpacking),这就是python的机制之一,假如有一个元组,我们只需要用里面的元素,而不对元素本身进行修改,更甚,我们只需要其中的一部分。例如下面这个:

heros = ('Spider Man', 'Iron Man', 'Captain American', 'Super Man', 'Bat Man')

        可以看到前面3个都是漫威英雄,后面两个是DC英雄,现在我们在unpacking时只想要前3个,不要后两个,那就可以

spider, iron, cap, *_ = heros

         其中,*表示从这个位置到后面的都作为一个整体,下划线 表示我们不需要用到这个变量,如果我们需要前后的,不要中间的,那还可以这样:

spider, iron, *_, bat = heros
print(spider)
print(iron)
print(bat)
'''
Spider Man
Iron Man
Bat Man
'''

        总之,在这种情况下可以灵活运用*和_,可以很大程度的方便我们编程,使用_能让python在unpacking时不为对应变量分配额外空间,也就不用考虑垃圾回收等。而*还可以用到函数参数里,假如我们定义一个函数,已知至少需要用到一个参数,还需要多少其他参数我们暂时不知道,即函数可以容纳任意数量的参数,那么这时就可以用*来表示其他参数,python将会把多的参数解析为一个列表并传给函数体使用,例如:

def multiPara(a, *others):
    # do something with a
    print(others)

if __name__ == '__main__':
    a = 1
    multiPara(a, 2, 3, 4, 5)
    multiPara(a)

'''
[2, 3, 4, 5]
[]
'''

5.关于类的get和set方法

        熟悉C++/Java的小伙伴肯定对这两个语言的get和set方法不陌生,别忘了python也是一个面向对象的语言,它的类也有get和set方法,还是以上面的heros和make_up_names为例,假设我们要创建一个类,以名字作为属性,称号作为属性值,python的getattrsetattr就能帮助我们动态的创建类的属性,让我们先试试,先set一个蜘蛛侠:

class MarvelHeros:
    pass

if __name__ == '__main__':
    marverHeros = MarvelHeros()
    name = 'peter_parker'
    make_up = 'Spider Man'
    setattr(marverHeros, name, make_up)    # 方法一
    setattr(marverHeros, 'peter_parker', 'Spider Man')    # 方法二
    print(marverHeros.peter_parker)

'''
Spider Man
'''

        一个一个设置太麻烦了,还记得前面说的zip方法吗,我们先用zip方法创建一个字典,名字为键,称号为值,然后一次性设置所有英雄,然后再读他们的称号:

marvelHeros = MarvelHeros()
marvel = dict(zip(heros, make_up_names))
for key, value in marvel.items():
    setattr(marvelHeros, key, value)

for key in marvel.keys():
    print(key, 'is', getattr(marvelHeros, key))

'''
Peter Parker is Spider Man
Tony Stark is Iron Man
Steven Rogers is Captain American
Natasha is Black Widow
Clint Barton is Hawk Eye
Stephen Strange is Dr. Strange
'''

6.熟练使用help和dir方法

        相信大多数小伙伴遇到一些新库的时候肯定都会比较困惑,不知道怎么使用,这时候除了百度以外,还可以使用help和方法和dir方法,help方法会列出想要了解的那个库的所有用法等信息,其中还有每个方法的使用例子,dir方法会列出这个库可用的所有方法名,比如非常好用的进度条库tqdm:

from tqdm import tqdm
help(tqdm)
dir(tqdm)
'''
这里会列出很多,读者自己可以试一试
'''

7.什么时候使用set或dict而不是list?

        list肯定是大家一开始用的最多的数据结构,但有时候往往list会是制约代码性能的时候,考虑下面这段代码:

from time import perf_counter
set_nums = set([x for x in range(int(1e6))])
list_nums = [x for x in range(int(1e6))]

start = perf_counter()
for i in range(int(1e6)):        # 注意这里的范围
    res.append(i in set_nums)
end = perf_counter() - start
print("set, cost:", end)
# set, cost: 0.1499949999997625

start = perf_counter()
for i in range(int(1e4)):        # 注意这里的范围
    res.append(i in list_nums)
end = perf_counter() - start
print("list, cost:", end)
# list, cost: 0.47885179999866523

        注释里的输出是以我的机器为例,可以看到,在列表里查询时,查询的数量相比在集合里查询少了两个数量级,而所花费的时间比集合里多了将近0.3秒,感兴趣的同学可以试试将列表的数量级也换成和集合的一样,看看耗时会是多少。造成这一现象的原因是,对于集合set类型,python会为其维护一个哈希表,以确保所有元素唯一,因此查询时的时间复杂度就是O(1),而对于列表,因为没有维护哈希表,每次都是遍历的去找,因此其时间复杂度是O(N),在这个例子中,两个循环的时间复杂度是O(N)O(N^{2}),显然集合要更快,因此,下次遇到类似情况时,尽量选择集合/字典,而不是列表。

8.默认参数需要用list时需谨慎

        又双叒叕是list,上回书说到,list是可变对象类型,而某个变量使用到list时只是这个列表的引用,如果有一个函数,你需要一个列表作为它的默认参数,就像下面这段代码这样:

def testListAsPara(nums, default_para = []):
    for n in nums:
        default_para.append(n)
    
    return default_para

if __name__ == '__main__':
    print(testListAsPara([1, 2, 3, 4]))
    print(testListAsPara([1, 2, 3, 4]))

        运行上面这段代码,会输出什么?两行1,2,3,4?其实是这样:

[1, 2, 3, 4]
[1, 2, 3, 4, 1, 2, 3, 4]

         为什么呢?前面说到,列表时可变对象类型,python在加载这个函数到内存时,就会为default_para分配一部分空间,而default_para只不过是一个指向这块地址空间的引用(指针),之后所有的修改都会在这块内存空间上进行,所以第二次调用时,default_para还是指向的一开始分配的那块空间,在那基础上又接着append。于是这个函数正确的写法应该是下面这样:

def testListAsPara(nums, default_para = None):
    if isinstance(default_para, type(None)):
        default_para = []
    for n in nums:
        default_para.append(n)

    return default_para

9.运行代码尽量使用main函数

        鉴于python又被称为脚本语言,很多同学可能初学的时候直接进去就开始写,就像我上面有几块代码块那样,或者在一个函数下面直接写,然后直接在命令行里python 文件名.py 这样运行,或者直接点击开发环境的运行按钮,这样的写法是个不好的习惯,比如下面这个example1.py文件:

import numpy as np

def example():
    for i in range(3):
        arrs = np.array([i] * 5)
        print(arrs)
        print("created a array")

example()

        虽然这样直接运行不会有问题,但是设想这个py里是你自己写的某个将来会用到的库,而python在执行import这个库时,会执行这个py文件里的代码,以确保所有的内容都被import进来,于是,你在下面的测试代码就会被执行,不信的话你可以自己试试,在example1.py所在目录下import example函数,看看会发生什么。

from example1 import example

        正确的做法应该是像上面几点中我所写的使用main来进行局部的测试,重写example,像下面这样,此时再试试看,你发现了什么?

import numpy as np

def example():
    for i in range(3):
        arrs = np.array([i] * 5)
        print(arrs)
        print("created a array")

if __name__ == '__main__':
    example()

11. 正确创建二(多)维列表

        怎么快速又正确的创建一个二维列表?在别的语言比如C语言里,我们需要首先创建一个指针的指针,再为这个指针的指针创建一个指针数组,再为这个指针数组分别分配内存空间,很麻烦,而且指针对新手也不友好,更别提指针的指针了。比如像下面这段代码,生成一个4行5列的全1矩阵:

int i, j, row = 4, col = 5;
int **array = (int**)malloc(row * sizeof(int*));
for(i = 0; i < row; i++){
    array[i] = (int*)malloc(col * sizeof(int));
    for(j = 0; j < col; j++){
        array[i][j] = 1;
    }
}
for(i = 0; i < row; i++){
    for(j = 0; j < col; j++){
        printf("%d ", array[i][j]);
    }
    printf("\n");
}

        再比如,使用C++的话,需要用new来分配内存,虽然比malloc要简单,但也没简单多少。

        那在python里,怎么创建一个二维列表呢?例如我们要生成一个4行5列的二维列表,如果你对列表的生成式比较熟悉的话, 可能你会这样:

a = [[1] * 4] * 5
print(a)
'''
[[1, 1, 1, 1], 
 [1, 1, 1, 1], 
 [1, 1, 1, 1], 
 [1, 1, 1, 1], 
 [1, 1, 1, 1]]
'''

        从结果上看起来确实起到了效果,但是我们再试下修改其中一个元素,比如将a[1][1]修改为10,看看会发生什么:

a[1][1] = 10
print(a)
'''
[[1, 10, 1, 1], 
[1, 10, 1, 1], 
[1, 10, 1, 1], 
[1, 10, 1, 1], 
[1, 10, 1, 1]]
'''

       可以看到每一行的第二个元素都被修改了,这是因为如果我们用前面那种方法来生成一个二维列表的话,python解释器只是会把每一行进行一个浅拷贝,每一行都共享同一块内存,可以用id方法来验证一下:

         

        不出所料,正是共享内存的。于是我们需要换种方式来生成,像下面的语句:

b = [['x'] * 4 for _ in range(5)]

         这样就能任意修改了。

 ​​​​

12.完结撒花

   

        到这里,这个系列差不多该结束了,想多叨叨几句,又好像叨叨不出来,写这篇花了我5个小时左右,如果对你有帮助的话,点个赞再走吧,谢谢老板!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python编程技巧和需要注意的地方——Pro 的相关文章

随机推荐

  • Qt颜色选择器(HSV)

    目录 引言 HSV颜色空间 实现思路 代码实现 引言 基于Qt实现的HSV颜色选择器 效果如下 HSV颜色空间 HSV Hue Saturation Value 是根据颜色的直观特征创建的颜色模型 如下图所示 Hue为色调 通过角度表示不同
  • 3_Nginx重载、热部署、日志切割

    文章目录 常用命令 重载配置文件 热部署 流程 日志切割 常用命令 获取帮助 nginx h 使用指定的配置文件 nginx c conf file 在命令行中覆盖配置文件中的指令 nginx g 指定运行目录 nginx p 发送信号 n
  • 【Vue学习笔记3】使用Vite开启一个Vue3工程项目

    1 什么是Vite Vite是一个web开发构建工具 Vite 的竞品是 Webpack 而且按照现在的趋势看 使用率超过 Webpack 也是早晚的事 Vite 主要提升的是开发的体验 Webpack启动调试环境需要 3 分钟都很常见 V
  • linux环境下的Qt编程问题解决

    1 gcc编译器无法使用 原因可能是没有安装g 终端输入命令 sudo apt get install g 2 编译程序报错Could not start process make qmake all 解决办法 终端输入命令 sudo ap
  • 机器学习(第二章)—— 模型评估

    目录 一 评估指标的局限性 二 ROC曲线 三 余弦距离的应用 四 A B测试的陷阱 五 模型的评估方法 六 超参数优化 七 过拟合与欠拟合 准确率的局限性 精确率与召回率的权衡 平方根误差的 意外 什么是ROC曲线 为什么要进行在线A B
  • include过滤器

    include过滤器分为core gui io scene video几个文件夹以及 IEventReceiver h Irrlicht h Irrlicht h IrrlichtDevice h Irrlicht h IrrlichtDe
  • 【Selenium】webdriver.ChromeOptions()官方文档参数

    Google官方Chrome文档 在此记录一下 Chrome Flags for Tooling Many tools maintain a list of runtime flags for Chrome to configure the
  • CLIP与Chinese-CLIP:多模态预训练模型解读和图文检索体验

    欢迎关注公众号 AICV与前沿 一起学习最新技术吧 欢迎关注公众号 AICV与前沿 一起学习最新技术吧 欢迎关注公众号 AICV与前沿 一起学习最新技术吧 1 CLIP回顾 CLIP是2021年OpenAI提出的基于图文对比学习的多模态预训
  • MyBatis-Plus 使用拦截器实现数据权限控制

    前言背景 平时开发中遇到根据当前用户的角色 只能查看数据权限范围的数据需求 列表实现方案有两种 一是在开发初期就做好判断赛选 但如果这个需求是中途加的 或不希望每个接口都加一遍 就可以方案二加拦截器的方式 在mybatis执行sql前修改语
  • C++中类静态方法与实例方法的区别

    生成时机 静态方法在编译的时候就已经加载了并分配了内存 而实例方法只有在对象创建之后才会为实例方法分配内存 因此调用静态方法速度快 但是会占用内存 调用方式 静态方法归整个类所有 因此调用它不需要实例化 可以直接调用 类 静态方法 实例方法
  • stock趋势交易系统

    进场步骤 1 趋势 2 做单区间 回调区间就是做单区间 比如回调至最长红柱的1 3以内开始 变红或拉长不再开始 做单区间级别比趋势级别低一个级别 MACD绿柱缩短 3 决策点 比做单区间第一个级别 4 筛选 比如 看这个决策点是怎么得来的
  • js函数重载

    function createOverload var callMap new Map function overload args const key args map args gt typeof args join 得到形如 stri
  • 雷达中和计算机视觉中的目标识别检测及跟踪区别在哪里?

    作者 梦里寻梦 苦瓜 编辑 汽车人 原文链接 https www zhihu com question 58615878 answer 2100196576 https www zhihu com question 58615878 ans
  • 漏洞复现- - -CVE-2016-5195 Dirty Cow脏牛提权漏洞

    目录 一 漏洞分析 二 形成原因 三 漏洞检测复现 1 编译poc 文件 2 复现漏洞 四 漏洞修复 方法一 方法二 一 漏洞分析 脏牛 Dirty COW 编号 CVE 2016 5195 是2016年10月18日被曝出的存在于Linux
  • VScode中js文件代码没有高亮问题

    问题 VScode中js文件js代码没有高亮 显示白色 解决方法 文件 gt 首选项 gt 设置 找到setting json 然后注释掉这段代码 重新打开文件 JS代码就有高亮了 白色看着实在别扭
  • python元组 列表 字符串最后一个下标_python字符串列表元组序列操作

    Table of Contents generated with DocToc python系列 字符串 列表 元组的操作 序列的访问及运算符 序列是为满足程序中复杂的数据表示 python支持组合数据类型 可以将一批数据作为一个整体进行数
  • 数据湖:设计更好的架构、存储、安全和数据治理

    问题导读 1 对数据湖有哪些预期 2 数据湖架构如何设计 3 如何做好数据治理工作 4 如何保障数据湖的安全 前言对任何业务来说 数据驱动的结果 预告和对趋势的预测都是必不可少的 今天 在我们所做的每件事中 都能看到某种分析的逻辑在背后 从
  • C语言输出3个整数最大值的案例教程

    思路分析 先比出2个整数的最大值 然后用最大值和第三个整数比较从而求出3个整数的最大值 我们今天用几种办法输出3个整数最大值 方法一 使用if else语句以及输入输出语句来编写 方法二 使用三元运算符以及输入输出语句来编写 方法三 使用自
  • JAVA 泛型的相关知识点

    泛型分为 1 泛型类 2 泛型接口 3 泛型方法 1 泛型类 定义 class Test
  • Python编程技巧和需要注意的地方——Pro

    目录 前言 1 多个大数字相加 2 同时访问多个相互关联的列表 3 访问列表的同时输出对应下标 4 熟练使用 和 5 关于类的get和set方法 6 熟练使用help和dir方法 7 什么时候使用set或dict而不是list 8 默认参数