Python编程实力大揭秘:你真的了解Python吗?

发表时间: 2024-07-28 14:26

1:列表可变性

x = [1, 2, 3]y = xy.append(4)print(x)
Output: [1, 2, 3, 4]

乍一看,可能认为x仍为[1, 2, 3] 。毕竟,只修改了y ,对吗?错误的!这个谜题揭示了 Python 的一个基本特征:列表可变性和引用共享。

在 Python 中,当您列表分配给变量时,并不是在创建新列表。相反,正在创建对现有列表的新引用。因此,当我们执行y = x时, xy都指向内存中的同一个列表对象。

可视化表示:

x ----→ [1, 2, 3]y ----↗

当将 4 附加到y时,修改xy引用的列表:

x ----→ [1, 2, 3, 4]y ----↗

这种行为对于理解有效的 Python 编程至关重要。如果不小心,它可能会导致意外结果,但它也可以实现高效的内存使用。

解决方法:如果需要列表的单独副本,请使用copy()方法或切片符号[:]

y = x.copy()  # or y = x[:]

2:默认可变参数

def func(a, b=[]):    b.append(a)    return bprint(func(1))print(func(2))print(func(3))
Output:[1][1, 2][1, 2, 3]

这个谜题暴露了 Python 中的一个常见陷阱:可变的默认参数。这里的陷阱是默认参数b=[]仅在定义函数时计算一次,而不是每次调用函数时计算。

这意味着在所有未显式提供b func调用中,相同的列表对象将用作b的默认值。每次调用func都会修改同一个列表,从而导致在输出中看到的累积效果。

这是一步一步发生的事情:

  1. 首先调用func(1)
  • a = 1 , b是默认的空列表[]
  • 1附加到b
  • 返回[1]

2. 第二次调用func(2)

  • a = 2b与上次调用的列表相同,现在包含[1]
  • 2附加到b
  • 返回[1, 2]

3. 第三次调用func(3)

  • a = 3 , b是相同的列表,现在包含[1, 2]
  • 3附加到b
  • 返回[1, 2, 3]

解决方法:要避免这种行为,请使用None作为默认值并在函数内创建一个新列表:

def func(a, b=None):    if b is None:        b = []    b.append(a)    return b

该版本在未提供b时为每次调用创建一个新列表,避免了累积效应。

3:多重赋值

a = 1b = 2a, b = b, aprint(a, b)
Output: 2 1

这个谜语展示了 Python 最优雅的功能之一:多重赋值。它允许交换变量而无需临时变量,就像在其他语言中所做的那样。

它的工作原理如下:

  1. 首先计算赋值b, a的右侧,创建一个元组(2, 1)
  2. 然后将该元组解压缩并分别分配给ab

值得注意的是,这是同时发生的,因此不存在一个变量在分配另一个变量之前被覆盖的风险。

该技术不仅限于两个变量。您可以将它与任意数量的变量一起使用:

a, b, c = c, a, b  # rotates the values of a, b, and c

有趣的应用:这种多重赋值功能可以轻松地为斐波那契数列等算法编写简洁的代码:

a, b = 0, 1for _ in range(10):    print(a, end=' ')    a, b = b, a + b# Output: 0 1 1 2 3 5 8 13 21 34

4:布尔算术

print(True + True + True)
Output: 3

这个谜语乍一看似乎违反直觉,但它展示了 Python 的一个有趣的方面:​布尔值是整数的子类。

在Python中:

  • True的数值为 1
  • False的数值为 0

在算术运算中使用时, True被视为 1, False被视为 0。因此,该表达式相当于1 + 1 + 1 ,等于 3。

这种行为既有用又危险。在某些情况下,它允许简洁的代码,但如果不小心,它也可能导致微妙的错误。

例如,可以执行以下操作:

count = sum([True, False, True, True])  # count will be 3

但要小心这样的表达:

if True == 1:    print("This will actually print!")

虽然这可行,但依赖布尔值通常被认为是不好的做法。使用正确的布尔运算更清晰、更安全。

5:字符串索引和切片

print("hello"[-1] + "world"[1])
Output: oo

这个谜语展示了 Python 字符串索引和切片的强大功能和灵活性。

分解一下:

  1. "hello"[-1] :在Python中,负索引从序列末尾开始计数。 -1 指最后一个字符,所以这给了我们“o”。
  2. "world"[1] :常规(正)索引从 0 开始,因此索引 1 为我们提供了第二个字符“o”。

连接这些结果得到“oo”。

Python 中的字符串索引非常通用:

  • 正索引从头开始计数(从 0 开始)
  • 负索引从末尾开始计数(-1 表示最后一个字符)
  • 您可以使用带有 start:end:step 语法的切片,例如, "hello"[1:4]给出“ell”
  • 省略切片索引使用默认值:start=0、end=len(string)、step=1

这里还有一些例子:

s = "Python"print(s[0])    # "P"print(s[-2])   # "o"print(s[1:4])  # "yth"print(s[::-1]) # "nohtyP" (reverses the string)

应用:虽然巧妙地使用字符串切片可以使代码简洁,但始终优先考虑可读性。如果切片操作很复杂,请考虑将其分解或添加注释来解释其作用。

6:F-string和格式

x = 10y = 50print(f"{x}% of {y} is {x/100*y}")
Output: 10% of 50 is 5.0

这个展示了 Python 3 最受欢迎的功能之一:f 字符串(格式化字符串文字)。

F 字符串提供了一种简洁且可读的方式来将表达式嵌入到字符串文字中。它们以“f”为前缀,并使用大括号 {} 括起表达式。

  • {x}替换为 x (10) 的值
  • {y}替换为 y 的值 (50)
  • {x/100*y}计算为表达式 (10/100 * 50 = 5.0)

F 字符串不仅仅用于简单的变量替换。可以将任何有效的 Python 表达式放在大括号内,包括函数调用、算术运算,甚至嵌套的 f 字符串!

更多示例:

name = "Alice"age = 30print(f"{name.upper()} is {age * 12} months old.")# Output: ALICE is 360 months old.import mathradius = 5print(f"A circle with radius {radius} has an area of {math.pi * radius**2:.2f}")# Output: A circle with radius 5 has an area of 78.54

注意事项: F 字符串非常具有可读性,但要小心不要在其中放入过于复杂的表达式。如果表达式很复杂,则单独计算它然后将结果包含在 f 字符串中通常会更清楚。 7:运算符优先级

print(2 * 3 ** 3 * 2)
Output: 108

这个谜题测试你对 Python 中运算符优先级的理解。如果不小心,可能会认为计算结果为 432(如果从左到右计算)。

以下是正确的操作顺序:

  1. 求幂 ( ** ) 具有最高优先级,因此首先计算3 ** 3 ,得到 27。
  2. 然后从左到右执行乘法: 2 * 27 * 2

所以该表达式等价于:

print(2 * (3 ** 3) * 2)  # 2 * 27 * 2 = 54

Python 遵循 PEMDAS 规则(括号、指数、乘法/除法、加法/减法),还有一些附加规则:

  • 求幂 ( ** ) 是右关联的,这意味着a**b**c被解释为a**(b**c) ,而不是(a**b)**c
  • 乘法和除法具有相同的优先级并且是左结合的。
  • 加法和减法同样如此。

这是一个更复杂的示例:

print(2 + 3 * 4 ** 2 - 6 / 2)  # What's the result?

其计算过程为:

  1. 4 ** 2 = 16
  2. 3 * 16 = 48
  3. 6 / 2 = 3
  4. 2 + 48 - 3 = 47

注意事项:如有疑问,请使用括号来明确您的意图。最好是明确的,而不是依赖于并非所有读者都记得的运算符优先级规则。

8:函数返回和字符串连接

def greet(name):    return f"Hello, {name}!"print(greet("Alice") + " " + greet("Bob"))
Output: Hello, Alice! Hello, Bob!

这个将函数调用与字符串连接结合起来。来分解一下:

  1. greet("Alice")返回字符串"Hello, Alice!"
  2. greet("Bob")返回字符串"Hello, Bob!"
  3. 这两个字符串之间用空格" "连接起来

这演示了一些重要的 Python 概念:

  • 函数可以返回值,这些值可以立即在表达式中使用。
  • 使用+运算符连接字符串。
  • F 字符串可在函数内部使用来创建格式化字符串。

可以通过不同的方式获得相同的结果:

# Using an f-stringprint(f"{greet('Alice')} {greet('Bob')}")# Using str.format()print("{} {}".format(greet("Alice"), greet("Bob")))# Using the join methodprint(" ".join([greet("Alice"), greet("Bob")]))

注意事项:连接多个字符串时,请考虑使用str.join()或 f 字符串以获得更好的性能和可读性,特别是在迭代构建字符串时。

9:范围函数

print(list(range(5, 0, -1)))
Output: [5, 4, 3, 2, 1]

这个展示了 Python 的range()函数的多功能性以及如何使用它来生成相反顺序的序列。

range()函数最多可以接受三个参数:

  • start : 序列中的第一个数字(包含)
  • stop :序列中的最后一个数字(不包括)
  • step : 序列中每个数字之间的差异

在这种情况下:

  • start是 5
  • stop为 0(不包括,因此序列将以 1 结束)
  • step是-1,意味着我们正在倒计时

然后使用list()函数将范围对象转换为列表,使其更易于打印和可视化。

以下是range()的更多示例:

print(list(range(5)))          # [0, 1, 2, 3, 4]print(list(range(2, 8)))       # [2, 3, 4, 5, 6, 7]print(list(range(0, 10, 2)))   # [0, 2, 4, 6, 8]print(list(range(10, 0, -2)))  # [10, 8, 6, 4, 2]

注意事项: range()可以节省内存,因为它不会一次将所有数字存储在内存中。如果只需要迭代数字,请直接在 for 循环中使用range()而无需转换为列表:

for i in range(5, 0, -1):    print(i, end=' ')# Output: 5 4 3 2 1

10:使用.format()格式化字符串

print("可视化, 列表, {0}".format('a', 'b', 'c'))
Output: c, b, a

这展示了使用str.format()方法进行 Python 字符串格式化的灵活性。

在这个方法中:

  • 字符串中的大括号{}是参数的占位符。
  • 大括号内的数字指的是传递给format()的参数的索引。
  • 参数以 0 为索引,因此{0}指第一个参数, 列表指第二个参数,依此类推。

这允许我们根据需要对输出字符串中的参数进行重新排序。在本例中,我们颠倒了参数的顺序。

str.format()方法的用途非常广泛。这里还有一些例子:

# Reusing argumentsprint("{0} 列表 {0}".format("hello", "world"))  # Output: hello world hello# Named argumentsprint("{name} is {age} years old".format(name="Alice", age=30))  # Output: Alice is 30 years old# Accessing object attributesclass Point:    def __init__(self, x, y):        self.x, self.y = x, yp = Point(4, 5)print("The point is at ({0.x}, {0.y})".format(p))  # Output: The point is at (4, 5)# Specifying formatprint("Pi is approximately {:.2f}".format(3.14159))  # Output: Pi is approximately 3.14

虽然str.format()功能强大且仍然广泛使用,但 Python 3.6+ 引入了 f 字符串,它以更简洁的语法提供类似的功能:

name = "Alice"age = 30print(f"{name} is {age} years old")  # Output: Alice is 30 years old

注意事项:对于简单的格式化,f 字符串由于其可读性而通常是首选。对于更复杂的格式或需要重用格式字符串时, str.format()仍然非常有用。始终选择使代码最具可读性和可维护性的方法。