Python,明明只append了一次,为啥所有子列表都变了啊.jpg

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python,明明只append了一次,为啥所有子列表都变了啊.jpg相关的知识,希望对你有一定的参考价值。

参考技术A a 和 b 有何不同?有的同学可能会觉得这两种方法的结果是一样的,都是:

然而真实的结果是:

为什么会产生不同的结果呢?明明只append了一次,为什么 a 中的所有子列表都变了呢?这要从 Python 的数据模型说起。

Python 中每个对象都有各自的编号、类型和值。一个对象被创建后,它的编号就绝不会改变,你可以将其理解为该对象在内存中的地址。 Python 还提供了 is 运算符和 id() 函数用于比较和查看对象的编号。

is 运算符可以比较两个对象的编号是否相同; id() 函数能返回一个代表其编号的整型数。

在 Python 中有些对象的值可以改变,有些不可以。值可以改变的对象被称为可变对象;值不可以改变的对象就被称为不可变对象。(一个不可变容器对象如果包含对可变对象的引用,当后者的值改变时,前者的值也会改变;但是该容器仍属于不可变对象,因为它所包含的对象集是不会改变的。因此,不可变并不严格等同于值不能改变,实际含义要更微妙。) 一个对象的可变性是由其类型决定的;例如,数字、字符串和元组是不可变的,而字典和列表是可变的。 [1]

在一开始的问题中,列表 a 初始化的时候,内层的子列表 [] 实际上是引用了同一个可变对象。因此对内层的任意一个子列表的 append() 操作,最终会因为引用了同一个对象的原因,体现在每一个子列表上。

而 b 初始化的时候是创建了3个不同 [] 对象。每个子列表引用了不同的对象。

在 Python 的官方文档中也说明了这种情况, lst * n 这种形式相当于 lst 与自身进行 n 次拼接。 lst 中的项不会被拷贝,而是会进行多次引用。 [2]

我们再深入的看一下刚才括号里面很拗口的那句话:

“一个不可变容器对象如果包含对可变对象的引用,当后者的值改变时,前者的值也会改变;但是该容器仍属于不可变对象,因为它所包含的对象集是不会改变的。因此,不可变并不严格等同于值不能改变,实际含义要更微妙。”

简单做个实验:

我们可以看到 a 、 b 是可变对象,而 c 是不可变容器对象。我们改变了 a 的值,可以看到 a 的编号没有发生改变,这个很正常,因为 a 是可变对象。
同时因为 c 中的元素包括 a ,所以 c 的“值”也发生了变化,但是 c 的编号没有变化,也就是说还是那个不可变对象。也就是说 c 的不变性是基于它的对象集没有变化。

为啥我的不和谐机器人只执行一次我的命令,而且只执行一次?

【中文标题】为啥我的不和谐机器人只执行一次我的命令,而且只执行一次?【英文标题】:Why is my discord bot only executing my command one time and one time only?为什么我的不和谐机器人只执行一次我的命令,而且只执行一次? 【发布时间】:2017-09-10 11:15:22 【问题描述】:

我正在完成一个简单的声音剪辑 Discord 机器人,我通过在 python 中回收一个基本的音乐机器人示例来制作。我想要机器人做的就是进入调用命令(!womble)的用户的语音通道,从声音剪辑文件夹中播放随机声音剪辑,然后离开语音通道。

“很简单,对吧?”当然不是,显然不是这个 API。

经过一堆试验和错误,寻找至少 3 个 API 修订版,我让机器人实际执行命令.....一次。该命令的任何进一步提示都会遇到蟋蟀。我可以执行 !summon 并将机器人带入频道,但 !womble 命令不再起作用。

def bot_leave(self, ctx):
    state = self.get_voice_state(ctx.message.server)
    coro = state.voice.disconnect()
    fut = asyncio.run_coroutine_threadsafe(coro, state.voice.loop)
    try:
        fut.result()
    except:
        # an error happened sending the message
        pass

@commands.command(pass_context=True, no_pm=True)
async def womble(self, ctx):
    state = self.get_voice_state(ctx.message.server)
    opts = 
        'default_search': 'auto',
        'quiet': True,
    

    if state.voice is None:
        success = await ctx.invoke(self.summon)
        if not success:
            return

    try:
        random_clip = clip_dir + "\\" + random.choice(os.listdir(clip_dir))
        player = state.voice.create_ffmpeg_player(random_clip, after=lambda: self.bot_leave(ctx))
        player.start()
    except Exception as e:
        fmt = 'An error occurred while processing this request: ```py\n: \n```'
        await self.bot.send_message(ctx.message.channel, fmt.format(type(e).__name__, e))

我尝试进入 Python 聊天是 Discord API 服务器,但很像我的机器人,我遇到了蟋蟀。 (猜猜这就是我试图从已经进行 4 次对话的聊天中寻求支持的原因。)

【问题讨论】:

【参考方案1】:

我猜您可能不再需要帮助,但以防万一您应该尝试删除 coroutine.result() 并直接运行它。即改变:

def bot_leave(self, ctx):
state = self.get_voice_state(ctx.message.server)
coro = state.voice.disconnect()
fut = asyncio.run_coroutine_threadsafe(coro, state.voice.loop)
try:
    fut.result()
except:
    # an error happened sending the message
    pass

到:

def bot_leave(self, ctx):
state = self.get_voice_state(ctx.message.server)
coro = state.voice.disconnect()
try:
    asyncio.run_coroutine_threadsafe(coro, state.voice.loop)
except:
    # an error happened sending the message
    pass

这是我看到你的代码 sn-p 唯一能想到的,但问题可能出在代码的其他地方。

【讨论】:

Welp,它设法在一个方面改进了机器人:机器人现在将重新加入服务器。第一次完美无缺,第二次,机器人出现了......然后就坐在那里。它似乎不喜欢多次创建播放器......或者至少这是我的假设。

以上是关于Python,明明只append了一次,为啥所有子列表都变了啊.jpg的主要内容,如果未能解决你的问题,请参考以下文章

动态创建输入字段时,<select> 只填充了一次 $.each,为啥?

pandas库明明安装成功了,为啥总是导入错误?

python中为啥我的for循环里嵌套的if只能循环一次?

Python多进程(multiprocessing)

Python3 多进程和多线程

python为啥for循环只查到一次数据