理解 Python 交换:为什么 a, b = b, a 并不总是等价于 b, a = a, b?

如何解决理解 Python 交换:为什么 a, b = b, a 并不总是等价于 b, a = a, b?

众所周知,交换 ab 两项值的 Pythonic 方法是

a,b = b,a

它应该等价于

b,a = a,b

然而,今天我在做一些代码的时候,无意中发现以下两个交换给出了不同的结果:

nums = [1,2,4,3]
i = 2
nums[i],nums[nums[i]-1] = nums[nums[i]-1],nums[i]
print(nums)
# [1,3]

nums = [1,3]
i = 2
nums[nums[i]-1],nums[i] = nums[i],nums[nums[i]-1]
print(nums)
# [1,3,4]

这对我来说令人难以置信。有人可以向我解释这里发生了什么吗?我认为在 Python 交换中,两个分配同时且独立地发生。

解决方法

来自python.org

将对象分配给目标列表(可选地括在圆括号或方括号中)的递归定义如下。

...

  • Else:对象必须是与目标列表中的目标数量相同的可迭代对象,并且项目从左到右分配给相应的目标。

所以我认为这意味着你的任务

df2.TestCaseName = pd.Categorical(df2.TestCaseName,categories=df1.TestCaseName.values,ordered=True)

df2 = df2.sort_values('TestCaseName')

大致相当于

nums[i],nums[nums[i]-1] = nums[nums[i]-1],nums[i]

(当然还有更好的错误检查)

而另一个

tmp = nums[nums[i]-1],nums[i]
nums[i] = tmp[0]
nums[nums[i] - 1] = tmp[1]

就像

nums[nums[i]-1],nums[i] = nums[i],nums[nums[i]-1]

所以在两种情况下都会首先评估右侧。但是然后左边的两块按顺序求值,求值后立即赋值。至关重要的是,这意味着左侧的第二项仅在第一项分配已经完成后进行评估。因此,如果您先更新 tmp = nums[i],nums[nums[i]-1] nums[nums[i] - 1] = tmp[0] nums[i] = tmp[1] ,那么 nums[i] 所指的索引与您第二次更新 nums[nums[i] - 1] 时所指的索引不同。

,

这是因为评估——特别是在 =左侧——从左到右发生:

nums[i],nums[nums[i]-1] =

首先分配 nums[i],然后 那个 值用于确定分配给 nums[nums[i]-1]

的索引

当做这样的作业时:

nums[nums[i]-1],nums[i] =

... nums[nums[i]-1] 的索引依赖于 nums[i] 的旧值,因为 nums[i] 的赋值还在后面...

,

这是按照规则发生的:

  • 首先评估右侧
  • 然后,左侧的每个值都会从左到右获得新值。

因此,对于 JFileChooser f = new JFileChooser("D:\\"); f.showOpenDialog(null); FileNameExtensionFilter fnef = new FileNameExtensionFilter("IMAGES","png","jpg","jpeg"); f.setFileFilter(fnef); File selected = f.getSelectedFile(); String selectedPath = selected.getAbsolutePath(); Image getAbsolutePath = null; ImageIcon icon = new ImageIcon(selectedPath); Image image = icon.getImage(); selectedPath = selectedPath.replace("\\","\\\\"); // INSERT IN MYSQL String sql = "INSERT INTO stage1 (ATTESTATION) VALUES (" + selectedPath + ")"; pst = (PreparedStatement) cnx.prepareStatement(sql); pst.executeUpdate(sql); JOptionPane.showMessageDialog(null,"succes!!"); } catch (SQLException ex) { ex.printStackTrace(); } ,您的代码在第一种情况下

nums = [1,2,4,3]

相当于:

nums[2],nums[nums[2]-1] = nums[nums[2]-1],nums[2]

现在评估右侧,分配等价于:

nums[2],nums[2]

nums[2],nums[nums[2]-1] = nums[3],nums[nums[2]-1] = 3,4

给出:

nums[2] = 3
nums[nums[2]-1] = 4

nums[2] = 3
nums[3-1] = 4

nums[2] = 3
nums[2] = 4

在第二种情况下,我们得到:

print(nums)
# [1,3]
,

为了理解求值顺序,我创建了一个“变量”类,该类在设置和出现“值”时进行打印。

class Variable:
    def __init__(self,name,value):
        self._name = name
        self._value = value

    @property
    def value(self):
        print(self._name,'get',self._value)
        return self._value

    @value.setter
    def value(self):
        print(self._name,'set',self._value)
        self._value = value

a = Variable('a',1)
b = Variable('b',2)

a.value,b.value = b.value,a.value

当运行结果:

b get 2
a get 1
a set 2
b set 1

这表明首先评估右侧(从左到右),然后评估左侧(再次从左到右)。

关于 OP 的示例: 在这两种情况下,右侧将评估为相同的值。左侧第一项已设置,这会影响第二项的评估。它从来没有同时进行过独立评估,只是大多数时候你看到它被使用,这些术语不相互依赖。在列表中设置一个值,然后从该列表中获取一个值以用作同一列表中的索引通常不是一件事,如果这很难理解,你就明白了。就像在 for 循环中更改列表的长度很糟糕一样,这也有同样的味道。 (虽然很刺激,你可能已经猜到我跑到便笺簿了)

,

在表达式的左侧,您同时在读取和写入 nums[i],我不知道 python 是否保证按从左到右的顺序处理解包操作,但假设确实如此,您的第一个示例相当于.

t = nums[nums[i]-1],nums[i]  # t = (3,4)
nums[i] = t[0] # nums = [1,3,3]
n = nums[i]-1 # n = 2
nums[n] = t[1] # nums = [1,3]

虽然你的第二个例子等同于

t = nums[i],nums[nums[i]-1]  # t = (4,3)
n = nums[i]-1 # n = 3
nums[n] = t[0] # nums = [1,4]
nums[i] = t[0] # nums = [1,4]

这与你得到的一致。

,

在 CPython 中分析代码片段的一种方法是为其模拟堆栈机器反汇编其字节码。

>>> import dis
>>> dis.dis("nums[i],nums[i]")
  1           0 LOAD_NAME                0 (nums)
              2 LOAD_NAME                0 (nums)
              4 LOAD_NAME                1 (i)

              6 BINARY_SUBSCR
              8 LOAD_CONST               0 (1)
             10 BINARY_SUBTRACT
             12 BINARY_SUBSCR
             14 LOAD_NAME                0 (nums)
             16 LOAD_NAME                1 (i)
             18 BINARY_SUBSCR

             20 ROT_TWO

             22 LOAD_NAME                0 (nums)
             24 LOAD_NAME                1 (i)
             26 STORE_SUBSCR

             28 LOAD_NAME                0 (nums)
             30 LOAD_NAME                0 (nums)
             32 LOAD_NAME                1 (i)
             34 BINARY_SUBSCR
             36 LOAD_CONST               0 (1)
             38 BINARY_SUBTRACT
             40 STORE_SUBSCR

             42 LOAD_CONST               1 (None)
             44 RETURN_VALUE

我添加了空行以使阅读更容易。这两个提取表达式在字节 0-13 和 14-19 中计算。 BINARY_SUBSCR 用从对象获取的值替换堆栈上的前两个值,一个对象和下标。交换两个获取的值,以便第一个计算的值是第一个边界。这两个存储操作在字节 22-27 和 28-41 中完成。 STORE_SUBSCR 使用并删除堆栈中的前三个值、要存储的值、对象和下标。 (显然总是在最后添加 return None 部分。)问题的重要部分是 store 的计算是在单独和独立的批次中按顺序完成的。

Python 中最接近 CPython 计算的描述需要引入一个栈变量

stack = []
stack.append(nums[nums[i]-1])
stack.append(nums[i])
stack.reverse()
nums[i] = stack.pop()
nums[nums[i]-1] = stack.pop()

这是反向语句的反汇编

>>> dis.dis("nums[nums[i]-1],nums[nums[i]-1]")
  1           0 LOAD_NAME                0 (nums)
              2 LOAD_NAME                1 (i)
              4 BINARY_SUBSCR

              6 LOAD_NAME                0 (nums)
              8 LOAD_NAME                0 (nums)
             10 LOAD_NAME                1 (i)
             12 BINARY_SUBSCR
             14 LOAD_CONST               0 (1)
             16 BINARY_SUBTRACT
             18 BINARY_SUBSCR

             20 ROT_TWO

             22 LOAD_NAME                0 (nums)
             24 LOAD_NAME                0 (nums)
             26 LOAD_NAME                1 (i)
             28 BINARY_SUBSCR
             30 LOAD_CONST               0 (1)
             32 BINARY_SUBTRACT
             34 STORE_SUBSCR

             36 LOAD_NAME                0 (nums)
             38 LOAD_NAME                1 (i)
             40 STORE_SUBSCR

             42 LOAD_CONST               1 (None)
             44 RETURN_VALUE
,

在我看来,只有当列表的内容在列表的列表索引范围内时才会发生这种情况。如果例如:

nums = [10,20,40,30]

代码将失败:

>>> nums[i],nums[i]
Traceback (most recent call last):
  File "<stdin>",line 1,in <module>
IndexError: list index out of range

所以肯定,一个问题。永远不要使用列表的内容作为该列表的索引。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-