USF MSDS501 计算数据科学中文讲义 2.5 数据别名

来源:『USF计算数据科学中文讲义』翻译项目
原文:Data
译者:飞龙
协议:CC BY-NC-SA 4.0
编程最棘手的事情之一是确切地确定变量所指的数据 。请记住,我们使用data和这样的名称来表示保存数据值的内存单元 。名称比物理内存地址更容易记住,但我们可能被愚弄 。例如,显然两个变量x和y都可以具有相同的整数值 7:
x = y = 7print(x,y)# 7 7
但是,你知道他们都指的是同一个 7 对象吗? 换句话说,中的变量始终是引用或指向数据的指针,因此变量在技术上并不是持有值 。指针就像电话号码“指向”手机,但指针本身不是手机本身 。
我们可以使用内置的id(x)函数来发现这个间接的秘密层次,该函数返回由x指向的物理内存地址 。为了证明这一点,让我们问一下x和y指向的是什么:
x = y = 7print(id(x))print(id(y))'''44683074884468307488'''
哇! 他们是一样的 。该数字表示存储共享对象 7 的内存位置 。
当然,作为程序员,我们并不认为这些原子元素指的是同一个对象;请记住他们这样做 。我们更有可能将它们视为相同数字的副本,因为在视觉上显示:
from lolviz import *callviz(varnames=['x','y'])

USF MSDS501 计算数据科学中文讲义 2.5 数据别名

文章插图
让我们验证字符串是否发生了同样的事情:
name = 'parrt'userid = name # userid now points at the same memory as nameprint(id(name))print(id(userid))'''45061787604506178760'''
好的,很好,所以我们实际上共享相同的内存地址来保存字符串'parrt',并且两个变量名都指向同一个共享空间 。我们在语言实现中称之为别名 。
当我们开始更改共享数据时,事情才会变得怪异 。整数和字符串不会发生这种情况,因为它们是不可变的(无法更改) 。让我们看一个列表的两个相同副本:
you = [1,3,5]me= [1,3,5]print(id(you))print(id(me))callviz(varnames=['you','me'])'''45089625044508962440'''
这些列表具有相同的值,但存在不同的内存地址 。他们不是别名;它们不是共享的 。因此,更改一个不会改变另一个:
you = [1,3,5]me= [1,3,5]print(you, me)you[0] = 99print(you, me)'''[1, 3, 5] [1, 3, 5][99, 3, 5] [1, 3, 5]'''
另一方面,让我们看看如果我们让you和me共享相同的列表副本(指向相同的内存位置)会发生什么:
you = [1,3,5]me= youprint(id(you))print(id(me))print(you, me)callviz(varnames=['you','me'])'''45091394644509139464[1, 3, 5] [1, 3, 5]'''
【USF MSDS501 计算数据科学中文讲义 2.5 数据别名】现在,更改一个似乎改变另一个,但实际上两者都刚好引用内存中的相同位置:
you[0] = 99print(you, me)callviz(varnames=['you','me'])# [99, 3, 5] [99, 3, 5]
不要混淆“更改列表元素”和“更改指向列表的指针”:
you = [1,3,5]me= youcallviz(varnames=['you','me'])
me = [9,7,5] # doesn't affect `you` at allprint(you)print(me)callviz(varnames=['you','me'])'''[1, 3, 5][9, 7, 5]'''
当我们将列表或其他数据结构传递给函数时,这种数据别名大量存在 。将列表传递给其参数名为data的函数,意味着这两个是别名 。我们将在使用函数组织代码的“符号可见性”部分中,更详细地查看这个现象 。
浅复制
X = [[1,2],[3,4]]Y = X.copy() # shallow copycallviz(varnames=['X','Y'])
X[0][1] = 99callviz(varnames=['X','Y'])print(Y)# [[1, 99], [3, 4]]