编码颇有趣,而Python编码更有趣,由于有不少不一样的方法能够实现相同的功能。可是,大多数时候都有一些首选的实现方法,有些人将其称为Pythonic。这些Pythonic的共同特征是实现的代码简洁明了。算法
用Python或任何编码语言进行编程不是像火箭同样的科学,而主要是关于技巧。 若是有意尝试使用Pythonic编码,那么这些技术将很快成为咱们工具包的一部分,而且咱们会发如今项目中使用它们变得愈来愈天然。 所以,让咱们探索其中的一些简单技巧。编程
1.负索引
人们喜欢使用序列,由于当咱们知道元素的顺序,咱们就能够按顺序操做这些元素。在Python中,字符串、元组和列表是最多见的序列数据类型。咱们可使用索引访问单个项目。与其余主流编程语言同样,Python支持基于0的索引,在该索引中,咱们在一对方括号内使用零访问第一个元素。此外,咱们还可使用切片对象来检索序列的特定元素,以下面的代码示例所示。微信
>>> # Positive Indexing... numbers = [1, 2, 3, 4, 5, 6, 7, 8]... print("First Number:", numbers[0])... print("First Four Numbers:", numbers[:4])... print("Odd Numbers:", numbers[::2])...First Number: 1First Four Numbers: [1, 2, 3, 4]Odd Numbers: [1, 3, 5, 7]
可是,Python经过支持负索引而进一步走了一步。具体来讲,咱们可使用-1来引用序列中的最后一个元素,并向后计数。例如,最后一个元素的索引为-2,依此类推。重要的是,负索引也能够与切片对象中的正索引一块儿使用。app
>>> # Negative Indexing... data_shape = (100, 50, 4)... names = ["John", "Aaron", "Mike", "Danny"]... hello = "Hello World!"...... print(data_shape[-1])... print(names[-3:-1])... print(hello[1:-1:2])...4['Aaron', 'Mike']el ol
2.检查容器是否为空
容器是指能够存储其余数据的那些容器数据类型。一些常用的内置容器是元组,列表,字典和集合。在处理这些容器时,咱们常常须要在执行其余操做以前检查它们是否包含任何元素。确实,咱们能够检查这些容器的长度,该长度与已存储项目的数量相对应。当长度为零时,容器为空。下面显示了一个简单的示例。编程语言
if len(some_list) > 0: # do something here when the list is not emptyelse: # do something else when the list is empty
可是,这不是最好的Pythonic方式。相反,咱们能够简单地检查容器自己,它将在容器 True 包含元素时进行评估。尽管如下代码向您展现了主要的容器数据类型,但这种用法也能够应用于字符串(即,任何非空字符串都是 True )。函数
>>> def check_container_empty(container):... if container:... print(f"{container} has elements.")... else:... print(f"{container} doesn't have elements.")...... check_container_empty([1, 2, 3])... check_container_empty(set())... check_container_empty({"zero": 0, "one": 1})... check_container_empty(tuple())...[1, 2, 3] has elements.set() doesn't have elements.{'zero': 0, 'one': 1} has elements.() doesn't have elements.
3.使用Split()建立字符串列表
咱们常用字符串做为特定对象的标识符。例如,咱们可使用字符串做为字典中的键。在数据科学项目中,字符串一般是数据的列名。选择多个列时,不可避免地须要建立一个字符串列表。确实,咱们可使用列表中的文字建立字符串。可是,咱们必须编写成对的引号将每一个字符串括起来,这对于“懒惰”的人来讲有点繁琐。所以,我更喜欢利用字符串的 split() 方法来建立字符串列表,以下面的代码片断所示。工具
>>> # List of strings... # The typical way... columns = ['name', 'age', 'gender', 'address', 'account_type']... print("* Literals:", columns)...... # Do this instead... columns = 'name age gender address account_type'.split()... print("* Split with spaces:", columns)...... # If the strings contain spaces, you can use commas instead... columns = 'name, age, gender, address, account type'.split(', ')... print("* Split with commas:", columns)...* Literals: ['name', 'age', 'gender', 'address', 'account_type']* Split with spaces: ['name', 'age', 'gender', 'address', 'account_type']* Split with commas: ['name', 'age', 'gender', 'address', 'account type']
如上所示, split() 默认状况下,该方法使用空格做为分隔符,并根据字符串建立字符串列表。值得注意的是,当您建立包含某些包含空格的元素的字符串列表时,能够选择使用其余类型的分隔符(例如,逗号)。性能
这种用法受到一些内置功能的启发。例如,当你建立一个元组类,咱们能够这样作: Student = namedtuple(“Student”, [“name”, “gender”, “age”]) 。字符串列表指定了元组的“属性”。可是,也能够经过如下方式定义该类来本地支持它: Student = namedtuple(“Student”, “name gender age”) 。对于另外一个实例,建立一个Enum类支持相同的替代解决方案。测试
4.三元表达
在许多用例中,咱们须要根据条件定义具备特定值的变量,而且咱们能够简单地使用if ... else语句来检查条件。可是,它须要几行代码。若是仅处理一个变量的赋值,则可能须要使用三元表达式,该表达式检查条件并仅用一行代码便可完成赋值。此外,它的格式更短,从而使代码更加简洁。考虑如下示例。ui
# The typical wayif score > 90: reward = "1000 dollars"else: reward = "500 dollars"# Do this insteadreward = "1000 dollars" if score > 90 else "500 dollars"
有时,咱们能够从已定义的函数中获取一些数据,而且能够利用这一点并编写三元表达式的简单操做,以下所示。
# Another possible scenario# You got a reward amount from somewhere else, but don't know if None/0 or notreward = reward_known or "500 dollars"# The above line of code is equivalent to belowreward = reward_known if reward_known else "500 dollars"
5.带文件对象的语句
咱们常常须要从文件读取数据并将数据写入文件。最多见的方法是使用内置 open() 函数简单地打开文件,该函数会建立一个咱们能够操做的文件对象。
>>> # Create a text file that has the text: Hello World!...... # Open the file and append some new data... text_file0 = open("hello_world.txt", "a")... text_file0.write("Hello Python!")...... # Open the file again for something else... text_file1 = open("hello_world.txt")... print(text_file1.read())...Hello World!
在前面的代码片断中,咱们从一个文本文件开始,该文件的文本为“ Hello World!”。而后,咱们将一些新数据附加到文件中。可是,过了一下子,咱们想再次处理该文件。当咱们读取文本文件时,它仍然具备旧数据。换句话说,附加的文本不包括在文本文件中。
这是由于咱们首先没有关闭文件对象。若是不关闭文件,则没法保存更改。确实,咱们能够 close() 在文件对象上显式调用该方法。可是,咱们可使用“ with”语句执行此操做,该语句将自动为咱们关闭文件对象,以下所示。完成对文件的操做后,咱们能够经过访问文件对象的 closed 属性来验证文件已关闭。
>>> with open("hello_world.txt", "a") as file:... file.write("Hello Python!")...... with open("hello_world.txt") as file:... print(file.read())...... print("Is file close?", file.closed)...Hello World!Hello Python!Hello Python!Is file close? True
用更笼统的术语来讲,with语句是在Python中使用上下文管理器的语法。上一个示例涉及文件操做,由于这些文件是共享资源,咱们负责释放这些资源。上下文管理器能够帮助咱们完成工做。如前所示,文件操做结束后,将使用with语句自动关闭文件。
6.评估多个条件
一般,咱们须要评估多个条件。有几种可能的方案。对于数值,咱们能够对同一变量进行屡次比较。在这种状况下,咱们能够连接这些比较。
# Multiple Comparisons# The typical wayif a < 4 and a > 1: # do something here# Do this insteadif 1 < a < 4: # do somerthing here
在其余一些状况下,咱们能够进行多个相等比较,而且可使用如下in关键字进行成员测试。
# The typical wayif b == "Mon" or b == "Wed" or b == "Fri" or b == "Sun": # do something here# Do this instead, you can also specify a tuple ("Mon", "Wed", "Fri", "Sun")if b in "Mon Wed Fri Sun".split(): # do something here
另外一种技术是使用内置的 all() 和 any()函数 用于评估多个条件的功能。具体而言,该 all() 函数将评估 什么时候迭代 中的元素所有为 True ,所以该函数适合于替换一系列AND逻辑比较。另外一方面,该 any() 函数的计算结果为True 当迭代中的 任何元素为 True ,所以适合替换一系列OR逻辑运算。相关示例以下所示。
# The typical waysif a < 10 and b > 5 and c == 4: # do somethingif a < 10 or b > 5 or c == 4: # do something# Do these insteadif all([a < 10, b > 5, c == 4]): # do somethingif any([a < 10, b > 5, c == 4]): # do something
7.在函数声明中使用默认值
在几乎全部的Python项目中,大多数代码都涉及建立和调用函数。换句话说,咱们不断处理函数声明和重构。在许多状况下,咱们须要屡次调用一个函数。根据不一样的参数集,该功能将略有不一样。可是,有时一组参数可能比其余一组更经常使用,在这种状况下,咱们在声明函数时应考虑设置默认值。考虑下面的简单示例。
# The original form:def generate_plot(data, image_name): """This function creates a scatter plot for the data""" # create the plot based on the data ... if image_name: # save the image ...# In many cases, we don't need to save the imagegenerate_plot(data, None)# The one with a default valuedef generate_plot(data, image_name=None): pass# Now, we can omit the second parametergenerate_plot(data)
要注意的一件事是,若是在设置默认值时要处理可变数据类型(例如列表,集合),请确保使用None而不是构造函数(例如arg_name = [])。因为Python在定义的位置建立函数对象,所以提供的空白列表将被函数对象“卡住”。换句话说,调用函数对象时不会当即建立它。相反,咱们将在内存中处理相同的函数对象,包括其最初建立的默承认变对象,这可能会致使意外行为。
8.使用计数器进行元素计数
当咱们在列表、元组或字符串中有多个项目时(例如,多个字符),咱们常常想计算每项中有多少个元素。为此,能够为此功能编写一些乏味的代码。
>>> words = ['an', 'boy', 'girl', 'an', 'boy', 'dog', 'cat', 'Dog', 'CAT', 'an','GIRL', 'AN', 'dog', 'cat', 'cat', 'bag', 'BAG', 'BOY', 'boy', 'an']... unique_words = {x.lower() for x in set(words)}... for word in unique_words:... print(f"* Count of {word}: {words.count(word)}")...* Count of cat: 3* Count of bag: 1* Count of boy: 3* Count of dog: 2* Count of an: 5* Count of girl: 1
如上所示,咱们首先必须建立一个仅包含惟一单词的集合。而后,咱们迭代单词集,并使用该 count() 方法找出每一个单词的出现状况。可是,有一种更好的方法可使用 Counter 类来完成此计数任务。
>>> from collections import Counter...... word_counter = Counter(x.lower() for x in words)... print("Word Counts:", word_counter)...Word Counts: Counter({'an': 5, 'boy': 4, 'cat': 4, 'dog': 3, 'girl': 2, 'bag': 2})
该计数器类是在 collections 模块中可用的。要使用该类,咱们只需建立一个 generator:,x.lower() for x in words 每一个项目都将被计数。如咱们所见,Counter对象是相似dict的映射对象,每一个键对应于单词列表的惟一项,而值是这些项的计数。
此外,若是咱们有兴趣找出单词列表中最频繁出现的项目,咱们能够利用Counter对象的 most_common() 方法。如下代码展现了这种用法。咱们只须要指定一个整数(N),便可从列表中找出最频繁的N个项目。附带说明,该对象还将与其余序列数据一块儿使用,例如字符串和元组。
>>> # Find out the most common item... print("Most Frequent:", word_counter.most_common(1))Most Frequent: [('an', 5)]>>> # Find out the most common 2 items... print("Most Frequent:", word_counter.most_common(2))Most Frequent: [('an', 5), ('boy', 4)]
9.按不一样的订单要求排序
在许多项目中,对列表中的项目进行排序是一项广泛的任务。最基本的排序基于数字或字母顺序,咱们可使用内置 sorted() 函数。默认状况下,该 sorted() 函数将按升序对列表进行排序(实际上,它能够是可迭代的)。若是将 reverse 参数指定为 True ,则能够按降序得到项目。一些简单的用法以下所示。
>>> # A list of numbers and strings... numbers = [1, 3, 7, 2, 5, 4]... words = ['yay', 'bill', 'zen', 'del']... # Sort them... print(sorted(numbers))... print(sorted(words))...[1, 2, 3, 4, 5, 7]['bill', 'del', 'yay', 'zen']>>> # Sort them in descending order... print(sorted(numbers, reverse=True))... print(sorted(words, reverse=True))...[7, 5, 4, 3, 2, 1]['zen', 'yay', 'del', 'bill']
除了这些基本用法外,咱们还能够指定 key 参数,以即可以对复杂项进行排序,例如元组列表。考虑这种状况的如下示例。
>>> # Create a list of tuples... grades = [('John', 95), ('Aaron', 99), ('Zack', 97), ('Don', 92), ('Jennifer', 100), ('Abby', 94), ('Zoe', 99), ('Dee', 93)]>>> # Sort by the grades, descending... sorted(grades, key=lambda x: x[1], reverse=True)[('Jennifer', 100), ('Aaron', 99), ('Zoe', 99), ('Zack', 97), ('John', 95), ('Abby', 94), ('Dee', 93), ('Don', 92)]>>> # Sort by the name's initial letter, ascending... sorted(grades, key=lambda x: x[0][0])[('Aaron', 99), ('Abby', 94), ('Don', 92), ('Dee', 93), ('John', 95), ('Jennifer', 100), ('Zack', 97), ('Zoe', 99)]
上面的代码经过利用传递给 key 参数的lambda函数,向咱们展现了两个高级排序的示例。第一个使用降序对项目进行排序,第二个使用默认的升序对项目进行排序。咱们要结合这两个要求,若是考虑使用该 reverse 参数,则可能会获得一个错误的排序树,由于若是尝试按多个条件进行排序,则反向参数将适用于全部参数。请参见下面的代码段。
>>> # Requirement: sort by name initial ascending, and by grades, descending... # Both won't work... sorted(grades, key=lambda x: (x[0][0], x[1]), reverse=True)[('Zoe', 99), ('Zack', 97), ('Jennifer', 100), ('John', 95), ('Dee', 93), ('Don', 92), ('Aaron', 99), ('Abby', 94)]>>> sorted(grades, key=lambda x: (x[0][0], x[1]), reverse=False)[('Abby', 94), ('Aaron', 99), ('Don', 92), ('Dee', 93), ('John', 95), ('Jennifer', 100), ('Zack', 97), ('Zoe', 99)]>>> # This will do the trick... sorted(grades, key=lambda x: (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 (most recent call last): File "<input>", line 2, in <module>KeyError: 'gender'
假设咱们正在处理单词,而且想要将与列表相同的字符分组,而且这些列表与做为键的字符相关联。这是使用内置dict类型的幼稚实现。值得注意的是,检查dict对象是否具备 letter 键是相当重要的,由于若是键不存在,则调用该 append() 方法会引起 KeyError 异常。
>>> letters = ["a", "a", "c", "d", "d", "c", "a", "b"]... final_dict = {}... for letter in letters:... if letter not in final_dict:... final_dict[letter] = []... final_dict[letter].append(letter)...... print("Final Dict:", final_dict)...Final Dict: {'a': ['a', 'a', 'a'], 'c': ['c', 'c'], 'd': ['d', 'd'], 'b': ['b']}
让咱们看看如何使用defaultdict编写更简洁的代码。尽管该示例很简单,可是它只是为咱们提供了有关defaultdict类的一些想法,这使咱们没必要处理字典对象中不存在的键。
>>> from collections import defaultdict...... final_defaultdict = defaultdict(list)... for letter in letters:... final_defaultdict[letter].append(letter)...... print("Final Default Dict:", final_defaultdict)...Final Default Dict: defaultdict(<class 'list'>, {'a': ['a', 'a', 'a'], 'c': ['c', 'c'], 'd': ['d', 'd'], 'b': ['b']})
结论
在阅读本文以前,咱们可能已经了解了一些技巧,可是但愿仍然对这些技巧有所了解。在项目中实践这些惯用用法将使您的Python代码更具可读性和性能。
交流群
欢迎加入公众号读者群一块儿和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(之后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,不然不予经过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,不然会请出群,谢谢理解~
本文分享自微信公众号 - 小白学视觉(NoobCV)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。