当前位置: 首页 > 科技观察

使用这10个技巧加快Python编程的速度_0

时间:2023-03-13 20:30:51 科技观察

编码很有趣,而Python编码更有趣,因为有很多不同的方法可以实现相同的功能。然而,大多数时候有一些首选的方式来做这件事,有些人称之为Pythonic。这些Pythonics的共同特点是实现的代码简洁明了。使用Python或任何编码语言进行编程并不是火箭科学,它主要是关于技能。如果有意使用Pythonic编码进行实验,这些技术将很快成为我们工具包的一部分,我们会发现在我们的项目中越来越自然地使用它们。因此,让我们探索其中的一些简单技巧。1.NegativeIndex人们喜欢用sequences,因为当我们知道了元素的顺序,我们就可以按顺序对这些元素进行操作。在Python中,字符串、元组和列表是最常见的序列数据类型。我们可以使用索引访问单个项目。与其他主流编程语言一样,Python支持基于0的索引,我们在一对方括号中使用零访问第一个元素。此外,我们还可以使用切片对象来检索序列的特定元素,如以下代码示例所示。>>>#PositiveIndexing...numbers=[1,2,3,4,5,6,7,8]...print("FirstNumber:",numbers[0])...print("FirstFourNumbers:",numbers[:4])...print("OddNumbers:",numbers[::2])...FirstNumber:1FirstFourNumbers:[1,2,3,4]OddNumbers:[1,3,5,7]然而,Python通过支持负索引更进一步。具体来说,我们可以用-1来指代序列中的最后一个元素,向后数。例如,最后一个元素的索引为-2,依此类推。重要的是,负索引也可以与切片对象中的正索引一起使用。>>>#NegativeIndexing...data_shape=(100,50,4)...names=["John","Aaron","Mike","Danny"]...hello="HelloWorld!"。....print(data_shape[-1])...print(names[-3:-1])...print(hello[1:-1:2])...4['Aaron','Mike']elol2.检查容器是否为空容器是那些可以存储其他数据的容器数据类型。一些经常使用的内置容器是元组、列表、字典和集合。在处理这些容器时,我们经常需要在执行其他操作之前检查它们是否包含任何元素。事实上,我们可以检查这些容器的长度,它对应于存储物品的数量。当长度为零时,容器为空。一个简单的例子如下所示。iflen(some_list)>0:#dosomethingherewhenthelistisnotemptyelse:#dosomethingelsewhenthelistisempty然而,这不是最好的Pythonic方式。相反,我们可以简单地检查容器本身,如果容器包含元素,它将评估为True。尽管以下代码向您展示了主要的容器数据类型,但这种用法也可以应用于字符串(即,任何非空字符串都为True)。>>>defcheck_container_empty(container):...ifcontainer:...print(f"{container}haselements.")...else:...print(f"{container}does'thaveelements.")......check_container_empty([1,2,3])...check_container_empty(set())...check_container_empty({"zero":0,"one":1})...check_container_empty(tuple())...[1,2,3]haselements.set()doesn'tthaveelements.{'zero':0,'one':1}haselements.()doesn'thaveelements.3.使用Split()创建字符串列表我们经常使用字符串作为特定对象的标识符。例如,我们可以使用字符串作为字典中的键。在数据科学项目中,字符串通常是数据的列名。选择多列时,不可避免地要创建一个字符串列表。实际上,我们可以使用列表中的文字来创建字符串。但是,我们必须在每个字符串周围写成对的引号,这对于“懒惰”的人来说有点乏味。因此,我更喜欢使用字符串的split()方法来创建字符串列表,如以下代码片段所示。>>>#Listofstrings...#Thetypicalway...columns=['name','age','gender','address','account_type']...print("*Literals:",columns)......#Dothisinstead...columns='nameagegenderaddressaccount_type'.split()...print("*Splitwithspaces:",columns)...#Ifthestringscontainspaces,youcanusecommasinstead...columns='name,age,gender,address,accounttype'.split(',')...print("*Splitwithcommas:",columns)...*Literals:['name','age','gender','address','account_type']*Splitwithspaces:['name','age','gender','address','account_type']*Splitwithcommas:['name','age','gender','address','accounttype']如上所示,split()方法默认使用空格作为分隔符,并根据字符串创建一个字符串列表。值得注意的是,当您创建包含某些包含空格的元素的字符串列表时,您可以选择使用其他类型的分隔符(例如,逗号)。这种用法受到一些内置函数的启发。例如,当您创建一个元组类时,我们可以这样做:Student=namedtuple("Student",["name","gender","age"])。指定元组“属性”的字符串列表。但是,也可以通过按以下方式定义类来原生支持它:Student=namedtuple("Student","namegenderage")。对于另一个实例,创建一个支持相同替代解决方案的Enum类。4.三元表达式在很多用例中,我们需要根据条件定义具有特定值的变量,我们可以简单地使用if...else语句来检查条件。但是,它需要几行代码。如果您只处理一个变量的赋值,您可能希望使用三元表达式,它检查条件并仅用一行代码完成赋值。此外,它的格式更短,代码更简洁。考虑以下示例。#Thetypicalwayifscore>90:reward="1000dollars"else:reward="500dollars"#Dothisinsteadreward="1000dollars"ifscore>90else"500dollars"有时我们可以从定义的函数中获取一些数据,我们可以利用它编写简单的操作三元表达式如下所示。#Anotherpossiblescenario#Yougotarewardamountfromsomewhereelse,butdon'tknowifNone/0ornotreward=reward_knownor"500dollars"#Theabovelineofcodeisequivalenttobelowreward=reward_knownifreward_knownelse"500dollars"5.文件对象语句我们经常需要从文件读取数据和向文件写入数据。最常见的方法是使用内置的open()函数简单地打开文件,该函数创建一个我们可以操作的文件对象。>>>#Createatextfilethathasthetext:HelloWorld!......#Openthefileandappendsomenewdata...text_file0=open("hello_world.txt","a")...text_file0.write("HelloPython!").....#Openthefileagainforsomethingelse...text_file1=open("hello_world.txt")...print(text_file1.read())...HelloWorld!在前面的代码片段中,我们从一个文本文件开始,其文本为“HelloWorld!”。然后,我们将一些新数据附加到文件中。但是,过了一会儿,我们想再次处理该文件。当我们读取文本文件时,它仍然有旧数据。换句话说,附加文本不包含在文本文件中。这是因为我们一开始就没有关闭文件对象。不关闭文件就无法保存更改。实际上,我们可以在文件对象上显式调用close()方法。然而,我们可以使用“with”语句来做到这一点,它会自动为我们关闭文件对象,如下所示。处理完文件后,我们可以通过访问文件对象的closed属性来验证文件是否已关闭。>>>withopen("hello_world.txt","a")asfile:...file.write("HelloPython!")...withopen("hello_world.txt")asfile:...print(file.read())...print("Isfileclose?",file.closed)...HelloWorld!HelloPython!HelloPython!Isfileclose?True用更一般的术语来说,with语句在上下文管理器的Python语法中使用。前面的例子涉及文件操作,由于这些文件是共享资源,我们有责任释放它们。上下文管理器帮助我们完成工作。如前所示,with语句用于在文件操作完成后自动关闭文件。6.评估多个条件通常,我们需要评估多个条件。有几种可能的情况。对于数值,我们可以对同一个变量进行多次比较。在这种情况下,我们可以链接这些比较。#MultipleComparisons#Thetypicalwayifa<4anda>1:#dosomethinghere#Dothisinsteadif15andc==4:#dosomethingifa<10orb>5orc==4:#dosomething#Dotheseinsteadifall([a<10,b>5,c==4]):#dosomethingifany([a<10,b>5,c==4]):#dosomething7.在函数声明中使用默认值在几乎所有的Python项目中,大部分代码都涉及创建和调用函数。换句话说,我们一直在处理函数声明和重构。在很多情况下,我们需要多次调用一个函数。根据参数集,功能会略有不同。但是,有时一组参数可能比其他参数更常用,在这种情况下我们应该考虑在声明函数时设置默认值。考虑下面的简单示例。#原始形式:defgenerate_plot(data,image_name):"""这个函数为数据创建散点图"""#createtheplotbasedonthedata...ifimage_name:#savetheimage...#Inmanycases,wedon'tneedtosavetheimagegenerate_plot(data,None)#Theonewithdefaultvaluedefgenerate_plot=name(data,pass#Now,wecanomitthesecondparametergenerate_plot(data)需要注意的一件事是,如果您在设置默认值时处理可变数据类型(例如列表,集合),请确保使用None而不是构造函数(例如arg_name=[])。由于Python在定义它们的地方创建函数对象,因此提供的空列表将被函数对象“卡住”。换句话说,函数对象不会在调用时立即创建。相反,我们将处理在内存中使用相同的函数对象,包括最初创建它的默认可变对象,这可能会导致意外行为。8.使用计数器对元素进行计数当我们在列表中有多个项目(例如,多个字符)时t、元组或字符串,我们经常想统计每一项有多少个元素。为此,可以为此功能编写一些繁琐的代码。>>>words=['一个','男孩','女孩','一个','男孩','狗','猫','狗','猫','一个','女孩','AN','dog','cat','cat','bag','BAG','BOY','boy','an']...unique_words={x.lower()forxinset(words)}...forwordinunique_words:...print(f"*Countof{word}:{words.count(word)}")...*Countofcat:3*Countofbag:1*Countofboy:3*Countofdog:2*Countofan:5*Countofgirl:1如上所示,我们首先必须创建一个仅包含唯一单词的集合。然后我们遍历单词集并使用count()方法找出每个单词的出现。但是,有一种更好的方法可以使用Counter类来完成此计数任务。>>>fromcollectionsimportCounter......word_counter=Counter(x.lower()forxinwords)...print("WordCounts:",word_counter)...WordCounts:Counter({'an':5,'boy':4,'cat':4,'dog':3,'girl':2,'bag':2})这个计数器类在集合模块中可用。要使用该类,我们只需创建一个生成器:x.lower()forxinwords每个项目都将被计算在内。正如我们所见,Counter对象是类似字典的映射对象,其中每个键对应于单词列表中的唯一项,值是这些项的计数。此外,如果我们有兴趣找出单词列表中出现频率最高的项目,我们可以使用Counter对象的most_common()方法。下面的代码演示了这种用法。我们只需要指定一个整数(N)就可以从列表中找到出现频率最高的N项。作为旁注,该对象还可以与其他序列数据一起使用,例如字符串和元组。>>>#Findoutthemostcommonitem...print("MostFrequent:",word_counter.most_common(1))MostFrequent:[('an',5)]>>>#Findoutthemostcommon2items...print("MostFrequent:",word_counter.most_common(2))MostFrequent:[('an',5),('boy',4)]9.根据不同的顺序要求排序在众多项目中,对列表中的项目进行排序是一项常见的任务。最基本的排序是基于数字或字母顺序,我们可以使用内置的sorted()函数。默认情况下,sorted()函数将按升序对列表进行排序(实际上,它可以是一个可迭代对象)。如果将reverse参数指定为True,则可以按降序获取项目。一些简单的用法如下所示。>>>#Alistofnumbersandstrings...numbers=[1,3,7,2,5,4]...words=['yay','bill','zen','del']...#Sortthem...打印(排序(数字))...打印(排序(单词))...[1,2,3,4,5,7]['bill','del','yay','zen']>>>#Sortthemindescendingorder...print(sorted(numbers,reverse=True))...print(sorted(words,reverse=True))...[7,5,4,3,2,1]['zen','yay','del','bill']除了这些基本的用法,我们还可以指定key参数,这样就可以对复杂的项进行排序,比如元组列表。请考虑以下这种情况的示例。>>>#Createalistoftuples...成绩=[('John',95),('Aaron',99),('Zack',97),('Don',92),('Jennifer',100),('Abby',94),('Zoe',99),('Dee',93)]>>>#Sortbythegrades,descending...sorted(grades,key=lambdax:x[1],reverse=真)[('詹妮弗',100),('亚伦',99),('佐伊',99),('扎克',97),('约翰',95),('艾比',94),('Dee',93),('Don',92)]>>>#Sortbythename'sinitialletter,ascending...sorted(grades,key=lambdax:x[0][0])[('Aaron',99),('艾比',94),('唐',92),('迪伊',93),('约翰',95),('詹妮弗',100),('扎克',97),('Zoe',99)]上面的代码向我们展示了两个使用传递给key参数的lambda函数进行高级排序的示例。第一个使用降序对项目进行排序,第二个使用默认的升序对项目进行排序。我们是结合这两个需求,如果你考虑使用reverse参数,你可能会得到一个错误的排序树,因为如果你尝试按多个条件排序,reverse参数将适用于所有参数。请参阅下面的代码片段。>>>#Requirement:sortbynameinitialascending,andbygrades,descending...#两者都行不通...sorted(grades,key=lambdax:(x[0][0],x[1]),reverse=True)[('Zoe',99),('Zack',97),('Jennifer',100),('John',95),('Dee',93),('Don',92),('Aaron',99),('Abby',94)]>>>sorted(等级,key=lambdax:(x[0][0],x[1]),reverse=False)[('Abby',94),('Aaron',99),('Don',92),('Dee',93),('John',95),('Jennifer',100),('Zack',97),('Zoe',99)]>>>#Thiswilldothetrick...sorted(grades,key=lambdax:(x[0][0],-x[1]))[('Aaron',99),('Abby',94),('Dee',93),('Don',92),('Jennifer',100),('John',95),('Zoe',99),('Zack',97)]如您所见,通过将reverse参数设置为True或False,两者都没有任何效果。相反,诀窍是取反分数,因此当您按默认升序排序时,由于这些值取反,分数将被反转。但是,此方法有一个警告,因为否定只能用于数值,不能用于字符串。10.不要忘记defaultdict字典是一种高效的数据类型,它允许我们以键值对的形式存储数据。它要求所有键都是可散列的,并且存储此数据可能涉及散列表的使用。这种方法允许以O(1)效率进行数据检索和插入。但是,应该注意的是,除了内置的dict类型之外,我们还有其他可用的词典。其中,我想讨论一下defaultdict类型。与内置的dict类型不同,defaultdict允许我们设置一个默认的工厂函数,如果键不存在则创建元素。>>>student={'name':"John",'age':18}...student['gender']...Traceback(mostrecentcallast):File"",line2,inKeyError:'gender'假设我们正在处理单词并希望对与列表相同的字符进行分组,并且这些列表与作为键的字符相关联。这是使用内置dict类型的简单实现。值得注意的是,检查dict对象是否有字母键很关键,因为如果键不存在,调用append()方法将引发KeyError异常。>>>letters=["a","a","c","d","d","c","a","b"]...final_dict={}...forlettersinletters:...ifletternotinfinal_dict:...final_dict[letter]=[]...final_dict[letter].append(letter)...print("FinalDict:",final_dict)...FinalDict:{'a':['a','a','a'],'c':['c','c'],'d':['d','d'],'b':['b']让我们看看如何使用defaultdict来编写更简洁的代码。虽然这个例子很简单,但它只是给了我们一些关于defaultdict类的想法,这使我们不必处理字典对象中不存在的键。>>>fromcollectionsimportdefaultdict...final_defaultdict=defaultdict(list)...forlettersinletters:...final_defaultdict[letter].append(letter)...print("FinalDefaultDict:",final_defaultdict)..FinalDefaultDict:defaultdict(,{'a':['a','a','a'],'c':['c','c'],'d':['d','d'],'b':['b']})结论在阅读本文之前,我们可能已经了解了一些技巧,但我们希望仍然能够很好地理解这些技巧。在您的项目中练习这些习语将使您的Python代码更具可读性和性能。