改进Python代码的30个小技巧

1. 切片

a = "hello world"
print(a[::-1])
# dlrow olleh

2. 原地交换 / 同时赋值

a = 1
b = 2
print(f"First: {a, b}")
# First: (1, 2)

a, b = b, a + 2
print(f"Second: {a, b}")
# Second: (2, 3)

a, b* = 1, 2, 3
print(a, b)
# 1 [2, 3] 

3. 列表和元组

import sys

a = [1, 2, 3, 4, 5]
b = (1, 2, 3, 4, 5)

print(f"List size: {sys.getsizeof(a)} bytes")
print(f"Tuple size: {sys.getsizeof(b)} bytes")

'''
List size: 104 bytes
Tuple size: 80 bytes
'''

列表是可变的,元组不可变。在列表中,额外的内存会被分配以防我们扩展,被称为动态内存分配。而在不希望更改数据的场景中,出于内存方面原因,元组数据结构应该优先于列表,元组也比列表快。

4. 生成器

a = [x * 2 for x in range(10)]
b = (x * 2 for x in range(10))

print(a)
print(b)

"""
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
<generator object <genexpr> at 0x7f61f8808b50>
"""

列表解析是从另一个可迭代对象创建列表的Python方式–它比使用for循环快得多。但是如果不小心将方括号从[]改为(),将得到一个生成器对象。每个元素只在请求时使用,也就是惰性计算,使用生成器的主要好处是,它使用的内存较少,因为整个序列不是一次构建的。

5. 混叠

a = [1, 2, 3, 4 ,5]
b = a

b[4] = 7

print(id(a))
print(id(b))
print(a) 

"""
2278459070720
2278459070720
[1, 2, 3, 4, 7]
"""

Python是一种面向对象的编程语言–一切都是对象。因此,将对象分配给标识符就是创建对该对象的引用。
当我们将一个标识符赋给另一个标识符时,我们最终得到引用同一对象的两个标识符。这是一个称为混叠的概念。更改一个别名将影响另一个别名。有时候,这种行为是我们想要的,但通常,它让我们措手不及。
一种解决方法是在使用可变对象时避免别名。另一个解决方案是创建原始对象的克隆,而不是引用。
创建克隆最直接的方法是利用切片:

b = a[:] 

6. ‘not’ 操作符

a = []
print(not a)

"""
True
"""

下一个Python技巧是使用not操作符检查数据结构是否为空的最简单方法。Python内置的not是一个逻辑运算符,如果表达式不为真,它返回True,否则返回False
另一种使用方式是在if语句中:

if not a:
    # do something... 

7. F-字符串

first_name = "John"
age = 19

print(f"Hi, I'm {first_name} and I'm {age} years old!")

"""
Hi, I'm John and I'm 19 years old!
"""

也可以使用:

print("Hi, I'm {} and I'm {} years old!".format(first_name, age))

8. print函数中’end’参数

a = ["english", "french", "spanish", "german", "twi"]
for language in a:
    print(language, end=" ")

"""
english french spanish german twi
"""

使用print语句而不定义任何可选参数是很常见的。
我们可以更改的一个可选参数是end。end参数指定在调用print语句结束时应显示的内容。
end的默认值是“\n”,它告诉Python开始一个新行。在上面的代码中,我们将其更改为空格。因此,返回的输出中列表的所有元素都打印在同一行上。

9. 追加数据到元组

a = (1, 2, [1, 2, 3])
a[2].append(4)
print(a)

"""
(1, 2, [1, 2, 3, 4])
"""

我们已经知道元组是不可变的。尝试更改元组的状态将引发TypeError。但是,如果您将元组对象看作是绑定到不能更改的对象的名称序列,元组的前两个元素是整数–它们是不可变的。元组的最后一个元素是一个列表,在Python中是一个可变对象。
如果我们认为列表只是序列中的另一个名称,它绑定到一个不能更改的对象,那么我们会意识到列表仍然可以从元组中修改。

10. 合并字典

a = {"a": 1, "b": 2}
b = {"c": 3, "d": 4}

a_and_b = a | b
print(a_and_b)

"""
{"a": 1, "b": 2, "c": 3, "d": 4}
"""

在Python 3.9及更高版本中,可以使用 |。关于这个特殊的Python技巧,除了它是一个可读性更强的解决方案之外,没有什么可说的!

11. 三元运算符 / 条件表达式

condition = True
name = "John" if condition else "Doe"

print(name)

"""
John
"""

12. 从列表中去掉重复项

a = [1, 1, 2, 3, 4, 5, 5, 5, 6, 7, 2, 2]
print(list(set(a)))

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

从列表中删除重复元素的最简单方法是将列表转换为集合(如果愿意,然后再转换回列表)。
集合和列表之间的关键区别是集合不能包含重复项。

13. 单独下划线

>>> print(_)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>

>>> 1 + 2
3

>>> print(_)
3

下划线(_)是Python中的合法的标识符,因此,可以使用它来引用对象。但强调还有另一个责任:以存储最后的结果。文档指出,“交互式解释器使最后一次求值的结果在变量_中可用。“
因为我们在第一行调用对象之前没有给它赋值下划线,所以我们得到了一个错误。但是,当我们计算1 + 2的输出时,交互式解释器为我们将结果存储在_中。

14. 用下划线表示忽略的值

for _ in range(100):
    print("The index doesn't matter")

"""
The index doesn't matter
The index doesn't matter
...
"""

我们也可以用它来表示我们不关心的对象,或者在程序的后面不会使用的对象。

15. 尾部下划线

list_ = [0, 1, 2, 3, 4]
global_ = "Hi there" 

继续前两个技巧,Python的下划线()用法,它的另一个目的是避免与Python关键字冲突。
PEP8提到尾部下划线(
)应该按照约定使用,以避免与Python关键字冲突。它还指出,“通常最好附加一个尾随下划线,而不是使用缩写。”因此list_优于lst。

16. 前置下划线

class Example:
    def __init__(self):
        self._internal = 2
        self.external = 20

你经常会发现有经验的Python程序员倾向于在标识符或方法名前面加上下划线–这是有原因的。
标识符或方法前面的下划线具有隐藏的含义:此变量或方法仅用于内部使用。本质上,它是对其他程序员的免责声明,这些程序员在PEP 8中已经定义,但Python没有强制执行。因此,前置下划线是弱指示符。

17. 下划线用于数字显示

number = 1_500_000
print(number)

"""
15000000
"""

我们可以使用下划线的另一种方式是作为整数、浮点数和复数常量中数字分组的可视分隔符–这是在Python 3.6中引入的。 这样做的目的是为了提高长文本的可读性。

18. __name__ == “__main__”

if __name__ == "__main__":
    print("Read on to understand what is going on when you do this.")

"""
print("Read on to understand what is going on when you do this.")
"""

你很有可能在几个Python程序中见过这种语法;Python使用一个特殊的名称__main__,如果运行的Python文件是主程序,则将其设置为一个名为__name__的标识符。
如果我们决定将当前显示的模块导入到另一个模块(Python文件)中并运行该文件,则代码中表达式的真值将为false。这是因为当我们从另一个模块导入时,__name__标识符被设置为模块(Python文件)的名称。

19. setdefault 方法

import pprint

text = "It's the first of April. It's still cold in the UK. But I'm going to the museum so it should be a wonderful day"

counts = {}
for word in text.split():
    counts.setdefault(word, 0)
    counts[word] += 1

pprint.pprint(counts)

"""
{'April.': 1,
"It's": 2,
'UK.': 1,
...
'still': 1,
'the': 3,
'to': 1,
'wonderful': 1}
"

可能会碰到这种需求,检查元素是否存在一个字典中,存在当前值加1,不存在添加当前值并设置其为1,看起来如下:

counts = {}
for word in text.split():
    if word in counts:
        counts[word] += 1
    else:
      counts[word] = 1

更简洁的方法是在dictionary对象上使用setdefault()方法。
传递给方法的第一个参数是我们要检查的键。传递的第二个参数是当键在字典中不存在时设置键的值-如果键存在,则方法将返回键值。因此,不会对其进行更改。

20. 正则匹配

import re

number = re.compile(r"(0?)(\+44)?\d(10)")
num_1 = number.search("My number is +447999999999")
num_2 = number.search("My number is 07999999999")

print(num_1.group())
print(num_2.group())

"""
'+447999999999'
'07999999999'
"""

21. 正则表达式管道‘|’使用

import re

heros = re.compile(r"Super(man|woman|human)")

h1 = heros.search("This will find Superman")
h2 =  heros.search("This will find Superwoman")
h3 = heros.search("This will find Superhuman")

print(h1.group())
print(h2.group())
print(h3.group())

"""
Superman
Superwoman
Superhuman
"""

正则表达式有一个特殊字符,称为管道(|),它允许您匹配许多表达式中的一个,并且它们可以在任何地方使用。

22. print函数中‘sep’参数

day = "04"
month = "10"
year = "2023"

print(day, month, year)
print(day, month, year, sep = "")
print(day, month, year, sep = ".")

"""
04 10 2023
04/10/2023
04.10.2023
"""

sep参数是print()函数中的一个可选参数,它允许我们指定在包含多个对象时应该如何分隔对象。
默认情况下是用空格分隔它们。

23. Lambda 函数

def square(num:int) -> int:
    return num ** 2

print(f"Function call: {square(4)}")
"""
Function call: 16
"""

square_lambda = lambda x: x**2
print(f"Lambda function: {square_lambda(4)}")
"""
Lambda functional: 16
"""

本质上,lambda关键字允许我们在一行中创建小的、受限的、匿名的函数。它们的行为与使用def关键字声明的常规函数相同,只是这些函数没有名称。

24. swapcase方法

string = "SoMe RaNDoM sTriNg"
print(string.swapcase())

"""
sOmE rAndOm StRInG
"""

swapcase()方法应用于字符串对象,允许我们在一行代码中将大写字母改为小写字母,反之亦然。swapcase()方法的用例并不多,但了解一下还是很不错的。

25. isalnum方法

password = "ABCabc123"
print(password.isalnum())

"""
True
"""

假设我们正在创建一个程序,要求用户输入密码,但密码必须是数字和字母的组合。我们可以通过调用string实例上的isalnum()在一行代码中完成此操作。
该方法检查所有字符是否都是字母表(A-Za-z)和数字(0 - 9)的一部分。空格或符号(!# %$&?等等)将返回False。

26. 异常处理

def get_ration(x:int, y:int) -> int:
    try:
        ratio = x/y
    except: ZeroDivisionError:
        y = y + 1
        ratio = x/y
    return ratio

print(get_ratio(x=400, y=0))

"""
400.0
"""

Python程序在遇到错误时终止。
有时候,我们不希望出现这种行为,比如当最终用户与我们的代码交互时。如果我们的代码在这种情况下过早终止,会有多糟糕?
关于如何处理这种例外情况,有几种思路。大多数Python程序员通常都认为请求原谅比获得许可更容易。这意味着它们更愿意通过提供能够处理异常的周围上下文来捕获引发的错误。这种想法背后的思想是,浪费时间试图防范所有各种例外情况是没有意义的。
但这只有在问题发生后有一种处理机制的情况下才成立。

27. 比较两个的列表差异

list_1 = [1, 3, 5, 7, 8]
list_2 = [1, 2, 3, 4, 5, 6, 7, 8, 9]

solution_1 = list(set(list_2) - set(list_1))
solution_2 = list(set(list_1) ^ set(list_2))
solution_3 = list(set(list_1).symmetric_difference(set(list_2)))

print(f"Solution 1: {solution_1}")
print(f"Solution 2: {solution_2}")
print(f"Solution 3: {solution_3}")

"""
Solution 1: [9, 2, 4, 6]
Solution 2: [2, 4, 6, 9]
Solution 3: [2, 4, 6, 9]
"""

这里有三种不同的方法来比较Python中两个列表之间的差异。
注意:除非你知道list_1是list_2的子集,否则solution 1和其他两个solution是不一样的。

28. Args & Kwargs

def some_function(*args, **kwargs):
    print(f"Args: {args}")
    print(f"Kwargs: {kwargs}")

some_function(1, 2, 3,  a=4, b=5, c=6)

"""
Args: (1, 2, 3)
Kwargs: {'a': 4, 'b': 5, 'c': 6}
"""

当我们不知道函数应该包含多少变量时,我们使用 *args和 **kwargs作为函数的参数。
参数 *args允许我们在函数没有关键字时(即:我们传递的参数不需要相关联的名称)。另一方面,**kwargs参数使我们能够向函数传递任意数量的关键字参数。
事实上,args和 **kwargs这两个词并没有那么神奇:真正的魔力在于星号()。这意味着我们可以在星号后面使用任何单词,但是使用args和kwargs是常见的做法。

29. 省略号

print(...)

"""
Ellipsis
"""

def some_function():
    ...

# Alternative solution
def another_function():
    pass

省略号是一个Python对象,可以通过提供三个点(…)的序列来调用它。或者调用对象本身(省略号)。
它最值得注意的用法是访问NumPy中的多维数组并对其进行切片,例如:

import numpy as np

arr = np.array([[2,3], [1,2], [9,8]])

print(arr[...,0])
"""
[2 1 9]
"""
print(arr[...])

"""
[[2 3]
[1 2]
[9 8]]
"""

30. 列表推导式

even_numbers = [x for x in range(10) if x % 2 == 0 and x != 0]
print(even_numbers)

"""
[2, 4, 6, 8]
"""

Python的最后一个技巧是列表解析,这是一种从另一个序列创建列表的优雅方法。它们允许您执行复杂的逻辑和过滤,就像我们在上面的代码中所做的那样。
还有其他方法可以达到同样的目的;例如,我们可以使用lambda函数,如下所示:

even_numbers = list(filter(lambda x: x % 2 ==0 and x != 0, range(10)))
print(even_numbers)
"""
[0, 2, 4, 6, 8]
"""
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值