「译」在 python 中,如果 x 是 list,为什么 x += "ha" 可以运行,而 x = x + "ha" 却抛出异常呢?...

本文解析了Python中列表拼接的两种方式:+= 和 + 的区别。+= 调用 extend() 方法,允许拼接可迭代对象;而 + 则需要两边都是列表,且会产生新的列表对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

问题

众所周知,在 python 中,+ 运算符可以使用在列表上,+ 运算符只需要第二个操作数是可迭代的(原文:iterable。@justjavac),那么 + 显然可以运算在 "ha" 上。

代码如下:

>>> x = []
>>> x += "ha"
>>> x
['h', 'a']

>>> x = x + "ha"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "str") to list

解答

当我们在列表 list 上使用 += 的时候,其实相当于调用函数 extend(),而不是使用的 +。

  • 你可以在一个可迭代(iterable)对象上调用 extend()。
    • 但是,当您使用 + 时,另一个操作数必须是列表(list)。

为什么 python 会如此诡异,也许是出于性能方面的考虑。 调用 + 时,将会创建一个新的对象,并复制里面的所有内容。但是当调用 extend() 函数时,将可以使用现有的空间。

这样就会产生另一个副作用:如果你写 X += Y,在其他对列表的引用(reference)中,会看到变化;但如果你使用 X = X + Y,就不会。

下面的代码说明了这一点:

>>> x = ['a','b']
>>> y = ['c', d']
>>> z = x
>>> x += y
>>> z
['a', 'b', 'c', 'd']    // z 也发生了变化

>>> x = ['a','b']
>>> y = ['c', d']
>>> z = x
>>> x = x + y
>>> z
['a', 'b']  // z 函数原始值

参考文献

Python source code for list.

python:+= 的源代码:

static PyObject *
list_inplace_concat(PyListObject *self, PyObject *other)
{
    PyObject *result;

    result = listextend(self, other);
    if (result == NULL)
        return result;
    Py_DECREF(result);
    Py_INCREF(self);
    return (PyObject *)self;
}

python:+ 的源代码:

static PyObject *
list_concat(PyListObject *a, PyObject *bb)
{
    Py_ssize_t size;
    Py_ssize_t i;
    PyObject **src, **dest;
    PyListObject *np;
    if (!PyList_Check(bb)) {
        PyErr_Format(PyExc_TypeError,
                  "can only concatenate list (not \"%.200s\") to list",
                  bb->ob_type->tp_name);
        return NULL;
    }

    // etc ...

原文:python - If x is list, why does x += "ha" work, while x = x + "ha" throw an exception?

译文:在 python 中,如果 x 是 list,为什么 x += "ha" 可以运行,而 x = x + "ha" 却抛出异常呢?

译者:justjavac

import numpy as np import matplotlib.pyplot as plt import pandas as pd def calc_distance(city1, city2): return np.linalg.norm(np.array(city1) - np.array(city2)) def calc_distances(cities): n_cities = len(cities) distances = np.zeros((n_cities, n_cities)) for i in range(n_cities): for j in range(i + 1, n_cities): distances[i][j] = distances[j][i] = calc_distance(cities[i], cities[j]) return distances class AntColony: def __init__(self, distances, n_ants, n_iterations, decay, alpha=1, beta=5, rho=0.1, Q=1): self.distances = distances self.pheromone = np.ones_like(distances) / len(distances) self.n_ants = n_ants self.n_iterations = n_iterations self.decay = decay self.alpha = alpha self.beta = beta self.rho = rho self.Q = Q self.shortest_path_lengths = [] def run(self): shortest_path = None shortest_path_length = np.inf for _ in range(self.n_iterations): all_paths = self.gen_all_paths() self.spread_pheromone(all_paths) shortest_path, shortest_path_length = self.get_shortest(all_paths, shortest_path, shortest_path_length) self.pheromone *= (1 - self.rho) self.shortest_path_lengths.append(shortest_path_length) return shortest_path, shortest_path_length def spread_pheromone(self, all_paths): for path in all_paths: for move in path: self.pheromone[move] += self.Q / self.distances[move] def gen_path_dist(self, path): total = 0 for move in path: total += self.distances[move] return total def gen_all_paths(self): all_paths = [] for _ in range(self.n_ants): path = self.gen_path() all_paths.append(path) return all_paths def gen_path(self): n_cities = len(self.distances) path = [] visited = set() start = np.random.randint(n_cities) visited.add(start) prev = start for _ in range(n_cities - 1): next_city = self.pick_city(prev, visited) path.append((prev, next_city)) prev = next_city visited.add(next_city) path.append((prev, start)) return path def pick_city(self, prev, visited): pheromone = np.copy(self.pheromone[prev]) pheromone[list(visited)] = 0 row = pheromone ** self.alpha * (1.0 / (self.distances[prev] + 1e-6) ** self.beta) norm_row = row / row.sum() next_city = np.random.choice(range(len(self.distances)), 1, p=norm_row)[0] return next_city def get_shortest(self, all_paths, shortest_path, shortest_path_length): for path in all_paths: path_dist = self.gen_path_dist(path) if path_dist < shortest_path_length: shortest_path = [(int(a), int(b)) for (a, b) in path] shortest_path_length = path_dist return shortest_path, shortest_path_length # 数据加载模块 try: df = pd.read_excel("TSP.xlsx", header=None) cities = df.iloc[:, [0, 1]].values assert cities.shape[1] == 2, "坐标数据必须包含X,Y两列" except FileNotFoundError: print("误:Excel文件未找到,请检查以下事项:") print("1. 文件是否位于当前工作目录") print("2. 文件名是否为'100个城市的坐标.xlsx'") print("3. 文件扩展名是否正确(.xlsx)") exit() except Exception as e: print(f"数据加载失败:{str(e)}") exit() # 计算距离矩阵 distances = calc_distances(cities) # 参数设置 n_ants = 20 n_iterations = 1000 decay = 0.1 alpha = 1 beta = 2 rho = 0.1 Q = 1 # 创建并运行蚁群算法 ant_colony = AntColony(distances, n_ants, n_iterations, decay, alpha, beta, rho, Q) shortest_path, shortest_path_length = ant_colony.run() # 路径格式化函数 def format_path(path): path_order = [int(path[0][0])] + [int(move[1]) for move in path] if path_order[-1] == path_order[0]: path_order = path_order[:-1] return ','.join(map(str, path_order)) # 输出结果 print("最短路径序列:", format_path(shortest_path)) print("最短路径长度:", shortest_path_length) # 可视化设置 plt.rcParams['font.sans-serif'] = 'SimHei' plt.rcParams['axes.unicode_minus'] = False # 绘制迭代曲线 plt.figure(figsize=(10, 5)) plt.plot(range(1, n_iterations + 1), ant_colony.shortest_path_lengths) plt.xlabel('迭代次数') plt.ylabel('路径长度') plt.title('算法优化过程') plt.grid(True) plt.show() # 路径可视化函数 def plot_path(cities, path): path_order = [path[0][0]] + [move[1] for move in path] x, y = cities[path_order, 0], cities[path_order, 1] plt.figure(figsize=(12, 8)) # 绘制城市点并添加编号(从1开始) for idx, (xi, yi) in enumerate(cities): plt.scatter(xi, yi, color='red', s=6,zorder=2) plt.text(xi, yi, str(idx + 1), fontsize=8,ha='center',va='bottom', color='black',zorder=3) # 绘制路径连线 plt.rcParams['font.sans-serif'] = 'SimHei' # 设置中文显示 plt.rcParams['axes.unicode_minus'] = False plt.plot(x, y, linestyle='-', marker='o', color='blue', linewidth=1, markersize=4, zorder=1) plt.xlabel('X坐标', fontsize=12) plt.ylabel('Y坐标', fontsize=12) plt.title('TSP最优路线 城市编号(1-1000)', fontsize=14) plt.grid(True, linestyle='--', alpha=0.7) plt.tight_layout() plt.show() plot_path(cities, shortest_path)输出误D:\python\space\.venv\Scripts\python.exe D:\python\space\8\6.py D:\python\space\8\6.py:43: RuntimeWarning: divide by zero encountered in scalar divide self.pheromone[move] += self.Q / self.distances[move] D:\python\space\8\6.py:77: RuntimeWarning: invalid value encountered in divide norm_row = row / row.sum() Traceback (most recent call last): File "D:\python\space\8\6.py", line 118, in <module> shortest_path, shortest_path_length = ant_colony.run() ^^^^^^^^^^^^^^^^ File "D:\python\space\8\6.py", line 33, in run all_paths = self.gen_all_paths() ^^^^^^^^^^^^^^^^^^^^ File "D:\python\space\8\6.py", line 54, in gen_all_paths path = self.gen_path() ^^^^^^^^^^^^^^^ File "D:\python\space\8\6.py", line 66, in gen_path next_city = self.pick_city(prev, visited) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\python\space\8\6.py", line 78, in pick_city next_city = np.random.choice(range(len(self.distances)), 1, p=norm_row)[0] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "numpy\\random\\mtrand.pyx", line 990, in numpy.random.mtrand.RandomState.choice ValueError: probabilities contain NaN 修改代码正确,提供修改之后的完整代码可直接运行
最新发布
05-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值