python函数编程技巧_用这10个小技巧加速Python编程

编码很有趣,而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])

...

FirstNumber: 1

FirstFour Numbers: [1, 2, 3, 4]

Odd Numbers: [1, 3, 5, 7]

但是,Python通过支持负索引而进一步走了一步。具体来说,我们可以使用-1来引用序列中的最后一个元素,并向后计数。例如,最后一个元素的索引为-2,依此类推。重要的是,负索引也可以与切片对象中的正索引一起使用。

>>> # 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 whenthe listisnotempty

else:

# do something elsewhenthe listisempty

但是,这不是最好的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()方法来创建字符串列表,如下面的代码片段所示。

>>> # Listofstrings

... # 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 withspaces: ['name','age','gender','address','account_type']

* Split withcommas: ['name','age','gender','address','account type']

如上所示,split()默认情况下,该方法使用空格作为分隔符,并根据字符串创建字符串列表。值得注意的是,当您创建包含某些包含空格的元素的字符串列表时,可以选择使用其他类型的分隔符(例如,逗号)。

这种用法受到一些内置功能的启发。例如,当你创建一个元组类,我们可以这样做:Student = namedtuple(“Student”, [“name”, “gender”, “age”])。字符串列表指定了元组的“属性”。但是,也可以通过以下方式定义该类来本地支持它:Student = namedtuple(“Student”, “name gender age”)。对于另一个实例,创建一个Enum类支持相同的替代解决方案。

4.三元表达

在许多用例中,我们需要根据条件定义具有特定值的变量,并且我们可以简单地使用if ... else语句来检查条件。但是,它需要几行代码。如果仅处理一个变量的赋值,则可能需要使用三元表达式,该表达式检查条件并仅用一行代码即可完成赋值。此外,它的格式更短,从而使代码更加简洁。考虑以下示例。

# The typical way

if score > 90:

reward = "1000 dollars"

else:

reward = "500 dollars"

# Do this instead

reward = "1000 dollars"if score > 90else"500 dollars"

有时,我们可以从已定义的函数中获取一些数据,并且可以利用这一点并编写三元表达式的简单操作,如下所示。

# Another possible scenario

# You got a reward amount fromsomewhereelse, but don't know if None/0ornot

reward = reward_known or"500 dollars"

# The above line ofcodeisequivalenttobelow

reward = reward_known if reward_known else"500 dollars"

5.带文件对象的语句

我们经常需要从文件读取数据并将数据写入文件。最常见的方法是使用内置open()函数简单地打开文件,该函数会创建一个我们可以操作的文件对象。

>>> #Createa text file that has the text: Hello World!

...

... # Openthe fileandappendsomenew data

... text_file0 = open("hello_world.txt","a")

... text_file0.write("Hello Python!")

...

... # Openthe file againforsomethingelse

... text_file1 = open("hello_world.txt")

... print(text_file1.read())

...

Hello World!

在前面的代码片段中,我们从一个文本文件开始,该文件的文本为“ Hello World!”。然后,我们将一些新数据附加到文件中。但是,过了一会儿,我们想再次处理该文件。当我们读取文本文件时,它仍然具有旧数据。换句话说,附加的文本不包括在文本文件中。

这是因为我们首先没有关闭文件对象。如果不关闭文件,则无法保存更改。确实,我们可以close()在文件对象上显式调用该方法。但是,我们可以使用“ with”语句执行此操作,该语句将自动为我们关闭文件对象,如下所示。完成对文件的操作后,我们可以通过访问文件对象的closed属性来验证文件已关闭。

>>>withopen("hello_world.txt","a")asfile:

...     file.write("Hello Python!")

...

... withopen("hello_world.txt")asfile:

...     print(file.read())

...

... print("Is file close?", file.closed)

...

Hello World!Hello Python!Hello Python!

Isfileclose?True

用更笼统的术语来说,with语句是在Python中使用上下文管理器的语法。上一个示例涉及文件操作,因为这些文件是共享资源,我们负责释放这些资源。上下文管理器可以帮助我们完成工作。如前所示,文件操作结束后,将使用with语句自动关闭文件。

6.评估多个条件

通常,我们需要评估多个条件。有几种可能的方案。对于数值,我们可以对同一变量进行多次比较。在这种情况下,我们可以链接这些比较。

# Multiple Comparisons

# The typical way

if a  1:

# do something here# Do this instead

if 1 

# do somerthing here

在其他一些情况下,我们可以进行多个相等比较,并且可以使用以下in关键字进行成员测试。

# The typical way

if b == "Mon"orb =="Wed"orb =="Fri"orb =="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 ways

if a  5andc == 4:

# do somethingif a  5orc == 4:

# do something# Do these instead

if all([a  5, c == 4]):

# do somethingif any([a  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"""

# createthe plot basedonthe data

...

if image_name:

# save the image

...# Inmany cases, we don't needtosave the image

generate_plot(data, None)# The one withadefaultvalue

def generate_plot(data, image_name=None):

pass# Now, we can omit the secondparameter

generate_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()forxinset(words)}

... forwordinunique_words:

...     print(f"* Count of {word}: {words.count(word)}")

...

* Countofcat: 3

* Countofbag: 1

* Countofboy: 3

* Countofdog: 2

* Countofan: 5

* Countofgirl: 1

如上所示,我们首先必须创建一个仅包含唯一单词的集合。然后,我们迭代单词集,并使用该count()方法找出每个单词的出现情况。但是,有一种更好的方法可以使用Counter类来完成此计数任务。

>>>fromcollections import Counter

...

... word_counter = Counter(x.lower()forxinwords)

... 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个项目。附带说明,该对象还将与其他序列数据一起使用,例如字符串和元组。

>>> # Findoutthe most common item

... print("Most Frequent:", word_counter.most_common(1))

Most Frequent: [('an', 5)]

>>> # Find outthe most common 2 items

... print("Most Frequent:", word_counter.most_common(2))

Most Frequent: [('an', 5), ('boy', 4)]

9.按不同的订单要求排序

在许多项目中,对列表中的项目进行排序是一项普遍的任务。最基本的排序基于数字或字母顺序,我们可以使用内置sorted()函数。默认情况下,该sorted()函数将按升序对列表进行排序(实际上,它可以是可迭代的)。如果将reverse参数指定为True,则可以按降序获得项目。一些简单的用法如下所示。

>>> # A listofnumbersandstrings

... 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 indescendingorder

... print(sorted(numbers, reverse=True))

... print(sorted(words, reverse=True))

...

[7, 5, 4, 3, 2, 1]

['zen','yay','del','bill']

除了这些基本用法外,我们还可以指定key参数,以便可以对复杂项进行排序,例如元组列表。考虑这种情况的以下示例。

>>> #Createa listoftuples

... grades = [('John', 95), ('Aaron', 99), ('Zack', 97), ('Don', 92), ('Jennifer', 100), ('Abby', 94), ('Zoe', 99), ('Dee', 93)]

>>> # Sort bythe 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 bythename'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: sortbynameinitial ascending,andbygrades, 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 "", line 2,in

KeyError: 'gender'

假设我们正在处理单词,并且想要将与列表相同的字符分组,并且这些列表与作为键的字符相关联。这是使用内置dict类型的幼稚实现。值得注意的是,检查dict对象是否具有letter键是至关重要的,因为如果键不存在,则调用该append()方法会引发KeyError异常。

>>> letters = ["a","a","c","d","d","c","a","b"]

... final_dict = {}

... forletterinletters:

...     if letter notinfinal_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类的一些想法,这使我们不必处理字典对象中不存在的键。

>>>fromcollections import defaultdict

...

... final_defaultdict = defaultdict(list)

... forletterinletters:

...     final_defaultdict[letter].append(letter)

...

... print("Final Default Dict:", final_defaultdict)

...

Final DefaultDict: defaultdict(, {'a': ['a','a','a'],'c': ['c','c'],'d': ['d','d'],'b': ['b']})

结论

在阅读本文之前,我们可能已经了解了一些技巧,但是希望仍然对这些技巧有所了解。在项目中实践这些惯用用法将使您的Python代码更具可读性和性能。

【编辑推荐】

【责任编辑:华轩 TEL:(010)68476606】

点赞 0

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值