学习一些技巧来编写更好的Python代码

  |   0 评论   |   0 浏览   |   给我丶鼓励

Python 是一种很棒的编程语言,它为我们提供了惊人的工具,使我们的代码更具可读性,简洁性和酷劲。今天,我想谈谈编写更多 Pythonic 代码的方法,我们将介绍一些可以提高您的代码质量的绝妙技巧。开始吧…


使用拆箱

Python 允许变量的元组(或列表)出现在操作的赋值侧。这使我们可以简化代码,使其更具可读性。

让我们从拆开元组的示例开始:

>>> a, b, c = (1, 2, 3)
>>> a
1
>>> b
2
>>> c
3

足够简单,在我们的赋值操作的左侧可以有多个变量,而在右侧的值将被逐一分配给每个变量。请注意,左侧的项目数应等于右侧的项目数,否则您可能会得到 ValueError 如下所示:

>>> a, b = 1, 2, 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)
>>> a, b, c = 1, 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 2)

但是,由于 Python 很棒,因此有多种方法可以防止这种情况的发生,您可以执行以下操作:

>>> a, *b, c = 1, 2, 3, 4, 5, 6
>>> a
1
>>> b
[2, 3, 4, 5]
>>> c
6

刚刚发生了什么?在这种情况下使用*运算符时,它将扩展拆包功能,以允许我们在单个变量中收集或打包多个值。表达式完全不使用一次。太棒了,只要记住您在分配中只能有一个*运算符来避免 SyntaxError

>>> a, *b, c, *d = 1, 2, 3, 4, 5, 6
  File "<stdin>", line 1
SyntaxError: two starred expressions in assignment

但您也可以解压缩列表:

>>> a, b, c = ['a', 'b', 'c']
>>> a
'a'

或字符串...。

>>> a, b, c = 'def'
>>> a
'd'

实际上,您可以通过这种方式使用任何迭代。但是,让我向您展示一件更酷的事情,然后再跳到下一个主题:

>>> a = 1
>>> b = 2
>>> a, b = b, a
>>> a
2
>>> b
1

那不是有史以来最漂亮的变量交换吗?


不检查

Python 中的 None 关键字用于定义一个空值,或根本不定义任何值。与其他语言不同,Python 中的 None 是其自己的数据类型(NoneType),只有 None 可以是 None。让我们在示例中查看它如何工作:

x = None

>>> type(x)
<class 'NoneType'>

>>> x == 0
False

>>> x == False
False

如果要检查变量实际上是否为 None,可以执行以下操作:

>>> x == None
True

它是有效的,但是还有一种更 Python 化的方式可以做到这一点:

>>> x is None
True

>>> x is not None
False

它执行相同的工作,但是看起来更人性化。


反复进行

python 擅长的另一个很好的例子,Python 中的迭代可以非常优雅,甚至可以说是非常 Unpythonic(如果该单词甚至存在的话)。

如果您来自 JS 或 C,您将如何尝试在 Python 中循环?刚发生在我身上

>>> x = [1, 2, 3, 5, 8, 11]
>>> for i in range(len(x)):
...     print(x[i])
... 
1
2
3
5
8
11

然后我学会了其他一些选择:

>>> x = [1, 2, 3, 5, 8, 11]
>>> for i in x:
...     print(i)
... 
1
2
3
5
8
11

但是也许您想要以相反的顺序进行迭代,那么您可以执行以下操作:

>>> x = [1, 2, 3, 5, 8, 11]
>>> for item in x[::-1]:
...     print(item)
... 
11
8
5
3
2
1

很好,但是看起来仍然很奇怪,看起来不像人类,也许还有另一种方式:

>>> for item in reversed(x):
...     print(item)
... 
11
8
5
3
2
1

现在看起来很漂亮!但是如果我们需要索引和项目值怎么办?我们在第一次尝试中就拥有了所有这些,现在似乎已经失去了它。不用担心,还有 Python 方式可以做到这一点:

>>> x = [1, 2, 3, 5, 8, 11]
>>> for i, item in enumerate(x):
...     print(i, item)
... 
0 1
1 2
2 3
3 5
4 8
5 11

但是有时候我们要遍历 1 个以上的数组,我们该怎么做呢?我们可以使用我们的打包/拆箱朋友:

>>> names = ['Juan', 'Gera', 'Martin']
>>> ages = [33, 30, 36]
>>> for person in zip(names, ages):
...     print(person)
... 
('Juan', 33)
('Gera', 30)
('Martin', 36)

如果我们要单独访问每个值:

>>> names = ['Juan', 'Gera', 'Martin']
>>> ages = [33, 30, 36]
>>> for person in zip(names, ages):
...     name, age = person
...     print(name, age)
... 
Juan 33
Gera 30
Martin 36

甚至更好:

>>> names = ['Juan', 'Gera', 'Martin']
>>> ages = [33, 30, 36]
>>> for name, age in zip(names, ages):
...     print(name, age)
... 
Juan 33
Gera 30
Martin 36

有时我们需要遍历对象,Python 使我们可以轻松地做到这一点:

>>> obj = {'name': 'Juan', 'age': 33}
>>> for k in obj:
...     print(k, obj[k])
... 
name Juan
age 33

但是我们也可以使用 .items() 对象的方法获取键和值:

>>> obj = {'name': 'Juan', 'age': 33}
>>> for k, v in obj.items():
...     print(k, v)
... 
name Juan
age 33

对象还提供类似 .keys() 和方法 .values(),具体取决于您的用例,它们可能会非常有帮助。

请注意,Python 为我们提供了许多遍历事物的方法,有时 range() 我们需要循环遍历,这是完全可以的,但是对于开发人员来说,其他一些更清晰的替代方案可能更清晰易读,因此我们应该尽可能使用它们。


避免可变的可选参数

就像其他许多语言一样,Python 为我们提供了具有可选参数的可能性,并且由于它们非常方便,它们还可能带来一些意外的行为。让我们看下面的例子:

>>> def add_value(value, seq=[]):
...     seq.append(value)
...     return seq
...

到目前为止,我们有一个名为的函数 add_value,它将在每次调用该函数时在列表中添加一个值,并在最后返回列表。该列表是一个可选参数。现在让我们调用函数并查看其行为:

>>> add_value(5)
[5]

完美,我们的可选参数正在运行,因此,我们获得了具有单个值的列表。让我们尝试添加更多:

>>> add_value(5)
[5]
>>> add_value(8)
[5, 8]
>>> add_value(13)
[5, 8, 13]

等等...什么?这看起来并不正确,但是实际结果是正确的,一旦我们对其进行了解释,这将是有意义的……。也许不是……让我们看看。

当我们定义函数时,Python 会生成默认值的实例,然后在每次未提供可选值时都使用该实例。对于我们的用例来说,这是一个大问题,因为即使这不是我们在特定情况下所需要的,列表也会不断增长。那么我们如何解决它?我们可以做以下事情:

>>> def add_value(value, seq=None):
...     if not seq:
...             seq = []
...     seq.append(value)
...     return seq
...

这是在 Python 中看起来很奇怪的东西,我不太喜欢它,尽管我们现在检测到的这个功能在很多情况下是非常有用的,我们只需要意识到这一点并明智地使用它即可。


属性与 Getter 和 Setters

警告:本主题可能引起 Java 开发人员 😛的争议。在所有笑话中,如果您是 Java 开发人员或来自 C ++ 来尝试执行以下操作,这将非常诱人:

>>> class Person:
...     def get_name(self):
...             return self.__name
...     def set_name(self, name):
...             self.__name = name
... 
>>> person = Person()
>>> person.set_name('Juan')
>>> person.get_name()
'Juan'

现在,即使这没什么问题,也不是 Python 方式。在向您介绍 Python 方式之前,请让我拿起手套 🥊

>>> class Person:
...     @property
...     def name(self):
...             return self.__name
...     @name.setter
...     def name(self, value):
...             self.__name = value
... 
>>> person = Person()
>>> person.name = 'Juan'
>>> person.name
'Juan'

我现在准备战斗了。。😛


受保护和私有属性,但不是真正的……。

Python 中不存在只能从对象内部访问的“受保护”或“私有”实例变量,但是所有 Python 开发人员都使用约定来指定这些属性。

>>> class Test:
...     def __init__(self, *args):
...             self.x, self._y, self.__z = args
... 
>>> test = Test(1, 2, 4)

如果现在尝试从类块外部访问 x 属性,则将获得 x 的实际值。这是正确的做法

>>> test.x
1

我们可以尝试使用 _y

>>> test._y
2

而且我们得到了结果,但是这是不**正确的做法,**因为该属性以_开头,并且该类的开发人员希望该属性无法从外部访问。

但是现在发生了什么 __z

>>> test.__z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__z'

在这种情况下,我们会得到一个错误,很好,但是仍然可以访问该属性,我们只需要在代码中添加一些魔术即可:

>>> test._Test__z
4

如果我们在属性名称前加上_classname,我们仍然可以访问该值,但是这样做是很错误的。

根据 Python 文档:

带有下划线的名称(例如_spam)应被视为 API 的非公开部分(无论是函数,方法还是数据成员)。它应被视为实现细节,如有更改,恕不另行通知。
形式为__spam 的任何标识符(至少两个前导下划线,至多一个下划线)在文本上被_classname__spam 替换,其中 classname 是当前类名,前导下划线被去除


使用上下文管理器处理资源

在处理资源(例如文件,数据库连接等)时,我们需要处理代码以成功关闭或释放资源,并且看到类似以下内容的情况很常见:

>>> my_file = open('filename.txt', 'w')
>>> # read my_file or do something with it
>>> my_file.close()

这段代码是正确的,除非在两者之间发生任何事情,否则如果发生错误怎么办,我们如何确保文件始终关闭?这是上下文管理器起作用的地方:

>>> with open('filename.txt', 'w') as my_file:
...     # do something with `my_file`

这是一种更安全的方法!


摘要

Python 是一种非常简单而优雅的语言。它的简单性使其在学习代码的学生或人们中非常受欢迎,但是,编写正确的 Python 代码非常重要。我希望阅读本文后,您对使用 Python 编写代码的方式有一些想法,并且可以进行更多研究。


标题:学习一些技巧来编写更好的Python代码
作者:给我丶鼓励
地址:https://blog.doiduoyi.com/articles/1591800419204.html

评论

发表评论