pythonic_编写更多惯用和pythonic代码

pythonic

There are lots of ways one can implement same feature, algorithm or function. Some of them straightforward, clear — better, some of them confusing, inefficient — worse. The Python community often uses terms like Pythonic or when describing code that follows certain (natural, proper) style and conventions. That’s the kind of good, clear code we all try to write everyday and in this article we will go over a few tips, conventions and idioms that will help you write a bit more idiomatic and Pythonic code.

一个人可以通过多种方式实现相同的功能,算法或功能。 其中一些简单明了,清晰- 更好 ,其中一些令人困惑,效率低下- 更加糟糕 。 Python社区经常使用诸如Pythonic之类的术语,或在描述遵循某些(自然,适当的)样式和约定的代码时使用。 这就是我们所有人每天都尝试编写的优质清晰的代码,在本文中,我们将介绍一些技巧,约定和习惯用法,这些技巧,约定和惯用语将帮助您编写更多惯用Pythonic的代码。

同一性和平等比较 (Identity and Equality Comparisons)

Not just in Python, but really in any programming language, you can fall into the trap of mixing up identity and value equality. In Python you have choice of using either is or == for comparisons, where is checks identity and == checks value.

不仅在Python中,而且在任何编程语言中,您都可能陷入混淆身份和价值平等的陷阱。 在Python中,您可以选择使用is==进行比较,其中is检查身份,而==检查值。

Considering that most of the time we only care about value, not identity, we would usually choose ==. There are cases however, where you should always use is operator instead. One of those is comparison with all of Pythons singletons - None, True or False.

考虑到大多数时候我们只关心价值而不是身份,我们通常会选择== 。 但是,在某些情况下,应始终使用is运算符代替。 其中之一是与所有Python单身的比较- NoneTrueFalse

Using is None, is True or is False isn't just about convention or improved readability though. It also improves performance, especially if you would use x is None instead of x == None inside loop. Why is that? - you might ask. Well, it's because is operator cannot be overloaded like == (which really is just a.__eq__(b)), so Python can skip lookup of these dunder methods that are needed to evaluate comparison using ==.

但是,使用is Noneis Trueis False不仅与约定或提高可读性有关。 它还可以提高性能,尤其x is None在循环内使用x is None而不是x == None这是为什么? -您可能会问。 好吧,因为is运算符不能像==一样重载(实际上只是a.__eq__(b) ),因此Python可以跳过对这些dunder方法的查找,这些方法需要使用==进行比较评估。

So, bottom line here is, that you should try to use is when possible, as it is more readable, faster and idiomatic. But to find out whether you can actually use it, you should ask yourself whether you care about value or identity of variables being compared.

所以,这里的底线是,你应该尽量使用is在可能的情况,因为它是更具可读性,更快速和地道 。 但是要弄清楚您是否可以真正使用它,您应该问自己是否在乎所比较的变量的值或同一性。

上下文管理器,而不是try/finally (Context Managers Instead of try/finally)

In other languages it’s common practice to use try/finally to manage resources and to make sure you dispose of opened files or acquired locks if exception occurs. You could use try/finally in Python too, but we can do better using with statement:

在其他语言中,通常的做法是使用try/finally来管理资源,并确保在发生异常时确保处理打开的文件或获得的锁。 您也可以在Python中使用try/finally ,但是使用with语句可以做得更好:

Code above shows usage of so-called context protocol which consists of 2 methods — __enter__ and __exit__ which are called when entering and exiting body of with block, respectively. You probably already know about with statement and it's usage, but you might not know about contextlib used above. It's a module that provides tools for turning functions into context managers. As for the closing function above, it just forces call to .close() method of the object, in this case page.

上面的代码显示了所谓的上下文协议的用法,该上下文协议包含2种方法__enter____exit__ ,分别在进入退出 with块的正文时调用。 您可能已经知道with语句及其用法,但是您可能不知道上面使用的contextlib 。 它是一个模块,提供用于将功能转换为上下文管理器的工具。 至于上面的closing函数,它只是强制调用对象的.close()方法,在本例中为page

Usage of context protocol isn’t limited to management of resources, though. It can also be used for suppressing exceptions ( with suppress(...)) or redirecting output ( with redirect_stdout(...)):

但是, 上下文协议的使用不仅限于资源管理。 它也可以用于抑制异常( with suppress(...) )或重定向输出( with redirect_stdout(...) ):

检查是否提供参数 (Checking If Parameter Was Provided)

From time to time you might need to define function that takes optional arguments. This can be done in Python very easily and surely know how:

您可能不时需要定义带有可选参数的函数。 这可以很容易地在Python中完成,并且肯定知道如何做:

Most of the time, we use optional arguments to allow the user of the our function to omit the obvious default argument or rarely used option. In some cases though, we might want to change behaviour of our function based not just on the value of optional argument but also based on whether the argument was provided or not. One reasonable solution for this case could be to use None as default (when it's None do X, when it's not do Y). But what if None is acceptable value? You could choose another throwaway value, but there is nice idiomatic solution to this:

大多数情况下,我们使用可选参数来允许函数的用户忽略明显的默认参数或很少使用的选项。 但是在某些情况下,我们可能不仅要根据可选参数的值,还要根据是否提供参数来改变函数的行为。 对于这种情况,一种合理的解决方案是使用None作为默认值(当它为None时,不执行X,而当它不为Y时)。 但是,如果None一个可接受的值怎么办? 您可以选择另一个一次性的值,但是对此有一个很好的惯用解决方案:

We can solve this problem by creating constant — for example — called _no_value which we set as a default value for the optional argument. By doing this we avoid any possibly acceptable values, because we're actually not checking value at all - we are checking identity. In other words we are checking whether the y argument refers to same exact object as the one assigned to _no_value.

我们可以通过创建名为_no_value常量(例如,将其设置为可选参数的默认值)来解决此问题。 通过这样做,我们避免了任何可能的可接受值,因为我们实际上根本没有检查值-我们正在检查identity 。 换句话说,我们正在检查y参数是否与分配给_no_value对象_no_value

多重分配 (Multiple Assignment)

One of the nice features of Python, that most programming languages lack is multiple assignment. In its simplest form it looks like this:

大多数编程语言所缺少的Python的一项不错的功能就是多重赋值。 最简单的形式如下:

This is nice as it shortens and simplifies code, but I personally rarely get a chance to use it. Much more practical version of this can be used when unpacking iterables into multiple variables:

这很不错,因为它可以缩短和简化代码,但是我个人很少有机会使用它。 将可迭代项解压缩为多个变量时,可以使用更实用的版本:

This is definitely preferable option over assigning values to each variable using indices, as it creates less visual noise, is more concise and also less error prone.

与使用索引将值分配给每个变量相比,这绝对是更可取的选择,因为它产生的视觉噪声更少,更简洁并且也更容易出错。

可变包装 (Variable Unpacking)

Building on previous example and going little further — we can also use star expression to unpack elements of iterable of arbitrary length:

在前面的示例的基础上,进行进一步的介绍–我们还可以使用星号表达式来解包任意长度的可迭代元素:

Quite often, values in iterables will have some pattern or known component, which can be easily extracted using unpacking. This is always better solution than explicitly using indices into iterable, as that creates unreadable code with lots of unnamed and unknown variables.

迭代器中的值通常会具有某种模式或已知成分,可以使用拆包轻松提取。 这总是比显式地使用可迭代的索引更好的解决方案,因为它会创建带有许多未命名和未知变量的不可读代码。

There’s one thing to be aware of when using star expression, though. Unpacking with star expression always creates list even if the variable receives zero values from unpacking, which can be nice considering that you won’t need to do any extra type checking, but can be also a bit surprising to receive [] instead of None.

但是,使用星型表达式时要注意一件事。 即使变量从拆包中接收到零值,使用星号表达式进行拆包也始终会创建列表,考虑到您无需进行任何额外的类型检查,这可能会很好,但是如果接收到[]而不是None也会有些令人惊讶。

If we wanted to stretch the limits of this feature, then we could even unpack multiple levels of iterable into other iterables:

如果我们想扩展此功能的限制,那么我们甚至可以将多个级别的可迭代程序分解为其他可迭代程序:

I don’t necessarily recommend doing this, as this will not produce very readable or nice code, but it’s good to know limits of a tool we use even if we’re not going to use this particular option very often or at all.

我不一定建议这样做,因为这不会产生可读性强的代码,但是即使我们不打算频繁或根本不使用此特定选项,也要知道我们所使用工具的局限性。

交换价值 (Swapping Values)

In other languages you would need extra variable and 3 lines of code to swap 2 variables. In Python however, there is a better way similar to previously shown multiple assignment:

在其他语言中,您将需要额外的变量和3行代码来交换2个变量。 但是,在Python中,有一种更好的方法类似于先前显示的多重分配:

This is super simple and super useful and it’s one of those features which reminds you how great Python is. Apart from swapping variables this also applies to mutable iterables (e.g. lists) and their indices, which can be commonly seen in sorting:

这是非常简单和有用的,它是其中的一项功能,它使您想起Python多么出色。 除了交换变量之外,这还适用于可变的可迭代对象(例如列表)及其索引,这在排序中很常见:

This all might seem like some Python magic, but in reality Python is just clever enough to know when to create temporary variables, what to put into them, where to assign their values and when to throw them away.

这一切似乎有些Python 魔术 ,但实际上Python足够聪明,可以知道何时创建临时变量,将其放入什么,在何处分配它们的值以及何时将其丢弃。

并行处理列表 (Processing Lists in Parallel)

Oftentimes when working with — for example — databases or CSV tables, you will find yourself with multiple lists of related data. It might be a few columns from database table, a few related datasets, etc. Regardless of what the data really is, you will probably want to work with it and process it in parallel. The simplest way to do that in Python is to use zip:

通常,使用数据库或CSV表时,您会发现自己拥有多个相关数据列表。 它可能是数据库表中的几列,一些相关的数据集等。无论数据到底是什么,您可能都希望使用它并并行处理它。 在Python中最简单的方法是使用zip

zip function takes variable number of lists and produces lazy generator that yields tuples containing elements from each of the supplied lists. This is great for processing data and it's also very efficient because - as I mentioned - the generator is lazy, so it won't load whole lists into memory, only the current tuple of elements.

zip函数采用可变数量的列表,并产生惰性生成器,该生成器生成包含每个所提供列表中的元素的元组。 这对处理数据非常有用,而且效率很高,因为-正如我提到的-生成器是惰性的 ,因此它不会将整个列表加载到内存中,仅将当前的元组加载到内存中。

When using this function you might come to realize that it’s not so great when working with lists with different lengths, as it’s going yield values only until the shortest of the lists is exhausted which might not always be desirable. In case you’d rather consume values until the longest of the lists is exhausted, you can instead use itertools.zip_longest, which will fill missing values with None or fillvalue provided as argument.

使用此功能时,您可能会意识到,使用不同长度的列表时效果不是很好,因为只有在列表中的最短部分用尽之前,它才会屈服值,这可能并不总是理想的。 万一您要消耗值直到列表中的最长列表都用尽,可以改用itertools.zip_longest ,它将使用Nonefillvalue作为参数填充缺失的值。

避免贴图,过滤和缩小 (Avoid map, filter and reduce)

Python has many functional programming concepts and functions like lambda expressions, list comprehensions, functools module, etc. There are however, a few that are frowned upon by many people. These are map, reduce and filter. What is bad about these functions though? Well, there are multiple reasons, but the one I have to agree with is that it's usually cleaner and clearer to write list comprehension instead map or filter and in case of reduce the code becomes hard to read when used with non-trivial function argument. Another good reason to dislike these functions is that ideally there should be only one right way to do things, so why use map, filter, reduce or even lambda when we have list comprehensions?

Python具有许多函数式编程概念和函数,例如lambda表达式,列表functoolsfunctools模块等。但是,有许多人对此感到不满意。 这些是mapreducefilter 。 这些功能有什么不好呢? 嗯,有多种原因,但我必须同意的一个原因是,编写列表推导而不是mapfilter通常更清晰明了,并且在reduce使用非平凡函数参数的情况下,代码变得难以阅读。 不喜欢这些功能的另一个很好的理由是,理想情况下应该只有一种正确的处理方式,那么当我们有列表lambda时为什么要使用mapfilterreduce甚至lambda呢?

It’s understandable if you disagree with me, but before writing some angry comment, you might want to read short write-up by Guido va Rossum, which might change your mind.

如果您不同意我的理解,这是可以理解的,但是在写一些愤怒的评论之前,您可能需要阅读Guido va Rossum的简短文章 ,这可能会改变您的想法。

Bottom line — use above functions sparingly and ideally just replace them with list comprehensions wherever possible.

底线-谨慎使用上述功能,理想情况下,只要有可能,就用列表理解替换它们。

“The only purpose of ‘reduce’ is to write really obfuscated code that shows how cool you are. I’m just not that cool.” — Guido van Rossum

“减少”的唯一目的是编写真正令人困惑的代码,以显示您的能力。 我只是不那么酷。” —圭多·范·罗苏姆

包含电池 (Batteries Included)

Python has for very long time maintained the philosophy of “batteries included”, meaning that you will find lots of useful tools, modules and functions in the standard library, that you wouldn’t expect to be there. You should always check whether the problem you are trying to solve or function you are trying to implement isn’t somewhere in the standard library and if you can’t find it, chances are you aren’t looking hard enough.

Python长期以来一直保持“包括电池”的理念,这意味着您会在标准库中找到很多有用的工具,模块和功能,而这些东西是您所不希望的。 您应该始终检查要解决的问题或要实现的功能是否不在标准库中,如果找不到,则可能是您看起来不够努力。

There are many examples of these “batteries” all over standard library, first module that comes to mind my is itertools which provides iterator building blocks. Another great one is functools with collection of higher order functions and I also have to mention collections module with very useful datatypes like Counter, deque or namedtuple just to name a few.

在标准库中有许多“电池”的示例,想到的第一个模块是itertools ,它提供迭代器构件。 另一个很棒的功能是functools ,它具有高阶函数的集合,我还不得不提到具有非常有用的数据类型(例如Counterdequenamedtuple collections模块,仅举几例。

So, next time you need some fairly common functionality in your program, don’t reinvent a wheel, go see Python library docs, grab what’s already there and save yourself some time.

因此,下次您需要在程序中使用一些相当通用的功能时,不要重新发明轮子,而是去查看Python库文档 ,获取已有内容并节省一些时间。

“束缚”成语 (The “Bunch” Idiom)

When you define a Python class you will most likely declare couple of attributes in its __init__ method. You might declare just one or two attributes, but you can also end up with something like this:

定义Python class ,很可能会在其__init__方法中声明几个属性。 您可能只声明了一个或两个属性,但最终也可能得到如下所示:

With just a few attributes in class it's kind of okay to write them out and it won't clutter your code that much, but if there were 10 or so attributes - like in the code above - would you be still okay with writing them all out? Well, I wouldn't. So, to avoid it you can use the so-called "bunch" idiom:

class只有几个属性,可以将它们写出来,这样不会使您的代码杂乱无章,但是如果有大约10个属性(如上面的代码),您仍然可以全部编写它们出来吗? 好吧,我不会。 因此,为避免这种情况,您可以使用所谓的“ bunch”成语

The snippet above demonstrates usage of self.__dict__ which is a dictionary which stores all the attributes of class (unless __slots__ is declared). Here we pass any keyword arguments of the constructor to the update function which generates all the attributes. It's also possible to use vars(self) which looks little nicer in my opinion.

上面的代码段演示了self.__dict__用法,该字典存储了class所有属性(除非声明了__slots__ )。 在这里,我们将构造函数的所有关键字参数传递给生成所有属性的update函数。 在我看来,也可以使用vars(self)看起来更好一点。

You might consider this a dirty hack, but I think it’s okay to use it, especially if you have class (data structure) for storing bunch of attributes without any real functionality. Also Raymond Hettinger says it's okay to update the instance dictionary directly, so there's that.

您可能认为这是一个肮脏的技巧,但我认为可以使用它,特别是如果您具有用于存储一堆属性而又没有任何实际功能的class (数据结构)。 雷蒙德·海廷格(Raymond Hettinger)表示 ,可以直接更新实例字典,这样就可以了。

结论 (Conclusion)

I definitely recommend using all of the above idioms and tips in your Python code and I believe these will make your code more Pythonic and idiomatic. There’s however no single answer to “What is Pythonic?” or “What is idiomatic?”, and what works for me, might not work for you. So, use idioms to make your code more readable, concise and effective and not just because it’s idiomatic. In the same way, use language specific features of Python to improve your code, not just to make it more Pythonic.

我绝对建议在您的Python代码中使用上述所有惯用法和技巧,并且我相信这些会使您的代码更加Pythonic惯用 。 但是, “什么是Pythonic”没有唯一的答案“什么是惯用语?” ,而对我有用的东西,可能对您不起作用。 因此,使用成语,使你的代码更易读,简洁,有效,不只是因为它是地道的 。 以相同的方式,使用Python的特定于语言的功能来改进您的代码,而不仅仅是使其变得更加Pythonic

This article was originally posted at martinheinz.dev

本文最初发布于 martinheinz.dev

翻译自: https://towardsdatascience.com/writing-more-idiomatic-and-pythonic-code-c22e900eaf83

pythonic

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值