我开始阅读python的+ =语法并偶然发现以下帖子/答案: Interactive code about +=
所以我注意到框架和物体之间似乎存在差异。
在全局框架中,即使它们是不同的变量,它们也指向同一个对象;如果该行
l2 += [item]
是
l2 = l2 + [item]
然后'l2'在该行运行时成为一个单独的对象。我最大的问题是你何时想要一个变量指向一个单独的对象?另外,为什么以及何时要让它们指向同一个对象?
任何解释或用例都将不胜感激!如果您能提及与数据科学相关的任何内容,请特别感谢:)
答案 0 :(得分:11)
frame
和object
并不代表您认为的含义。
在编程中,你有一个叫做堆栈的东西。在Python中,当你调用一个函数时,你会创建一个叫做堆栈框架的东西。这个框架(如你的例子中所示)基本上只是一个包含你函数本地变量的表格。
请注意,定义函数不会创建新的堆栈帧,它是调用函数。比如像这样:
def say_hello():
name = input('What is your name?')
print('Hello, {}'.format(name))
您的全局框架只会包含一个引用:say_hello
。你可以通过查看本地命名空间中的内容来看到(在Python中你几乎在命名空间,范围和堆栈帧之间有1:1的关系):
print(locals())
你会看到这样的东西:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x1019bb320>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/private/tmp/fun.py', '__cached__': None, 'say_hello': <function say_hello at 0x101962d90>}
请注意dunder(双下划线双下划线的缩写)名称 - 这些是自动提供的,出于讨论的目的,您可以忽略它们。这让我们:
{'say_hello': <function say_hello at 0x101962d90>}
0x
位是函数本身所在的内存地址。所以在这里,我们的全局堆栈/帧只包含那个值。如果您调用自己的功能,然后再次检查locals()
,则会发现name
不在那里。这是因为当你调用函数时,你创建了一个新的堆栈框架,并在那里分配了变量。您可以通过在函数末尾添加print(locals())
来证明这一点。然后你会看到类似的东西:
{'name': 'Arthur, King of the Brits'}
这里没有dunder的名字。您还会注意到,这并未显示内存地址。如果你想知道这个价值存在的地方,那就有了它的功能。
def say_hello():
name = input('What is your name?')
print('hello {}'.format(name))
print(locals())
print(id(name))
return name
print(id(say_hello()))
该示例在谈论框架时的含义。
但物体怎么样?好吧,在Python中,所有都是一个对象。试试吧:
>>> isinstance(3, object)
True
>>> isinstance(None, object)
True
>>> isinstance('hello', object)
True
>>> isinstance(13.2, object)
True
>>> isinstance(3j, object)
True
>>> def fun():
... print('hello')
...
>>> isinstance(fun, object)
True
>>> class Cool: pass
...
>>> isinstance(Cool, object)
True
>>> isinstance(Cool(), object)
True
>>> isinstance(object, object)
True
>>> isinstance(isinstance, object)
True
>>> isinstance(True, object)
True
他们所有对象。但它们可能是不同的对象。你怎么知道?使用id
:
>>> id(3)
4297619904
>>> id(None)
4297303920
>>> id('hello')
4325843048
>>> id('hello')
4325843048
>>> id(13.2)
4322300216
>>> id(3j)
4325518960
>>> id(13.2)
4322300216
>>> id(fun)
4322635152
>>> id(isinstance)
4298988640
>>> id(True)
4297228640
>>> id(False)
4297228608
>>> id(None)
4297303920
>>> id(Cool)
4302561896
请注意,您还可以使用is
来比较两个对象是否是相同的对象。
>>> True is False
False
>>> True is True
True
>>> 'hello world' is 'hello world'
True
>>> 'hello world' is ('hello ' + 'world')
False
>>> 512 is (500+12)
False
>>> 23 is (20+3)
True
Ehhhhh ...?等一下,那里发生了什么?好吧,事实证明,python
(即CPython)caches small integers。因此,对象512
与作为对象500
添加到对象12
的结果的对象不同。
需要注意的一件重要事情是,赋值运算符=
始终为同一对象分配新名称。例如:
>>> x = 592
>>> y = 592
>>> x is y
False
>>> x == y
True
>>> x = y
>>> x is y
True
>>> x == y
True
你给对象提供了多少其他名称并不重要,或者即使你通过object around to different frames,你仍然拥有相同的对象。
但是,当您开始收集时,了解更改对象的操作与生成 new 的操作之间的区别非常重要宾语。一般来说,你在Python中有few immutable types,对它们的操作会产生一个新对象。
至于你的问题,你什么时候想要改变对象?你想什么时候保持它们一样,实际上是以错误的方式看待它。如果你想改变一些东西,你想使用一个可变类型,如果你不想改变它,你想要使用一个不可变类型。
例如,假设您有一个群组,并且您想要向该群组添加成员。您可以使用类似列表的可变类型来跟踪组,并使用不可变类型(如字符串)来表示成员。像这样:
>>> group = []
>>> id(group)
4325836488
>>> group.append('Sir Lancelot')
>>> group.append('Sir Gallahad')
>>> group.append('Sir Robin')
>>> group.append("Robin's Minstrels")
>>> group.append('King Arthur')
>>> group
['Sir Lancelot', 'Sir Gallahad', 'Sir Robin', "Robin's Minstrels", 'King Arthur']
当小组成员被吃掉时会发生什么?
>>> del group[-2] # And there was much rejoicing
>>> id(group)
4325836488
>>> group
['Sir Lancelot', 'Sir Gallahad', 'Sir Robin', 'King Arthur']
您会注意到您仍然拥有相同的群组,只是会员已经更改。