frame
and object
并不意味着你认为他们的意思。
在编程中你有一个叫做stack。在Python中,当你调用一个函数时,你会创建一个叫做栈帧。该框架(如您在示例中所见)基本上只是一个包含函数本地所有变量的表。
注意defining函数不会创建新的堆栈帧,它是calling一个函数。例如这样的事情:
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>}
That 0x
bit 是函数本身所在的内存地址。所以在这里,我们的全局堆栈/帧仅包含该一个值。如果你调用你的函数然后检查locals()
再次,你会看到name
不在那里。这是因为当您调用该函数时,您会创建一个新的堆栈帧,并在其中分配变量。您可以通过添加来证明这一点print(locals())
在你的函数结束时。然后你会看到这样的东西:
{'name': 'Arthur, King of the Brits'}
这里没有重复的名字。您还会注意到,这不显示内存地址。如果你想知道这个值在哪里,可以使用一个函数。
def say_hello():
name = input('What is your name?')
print('hello {}'.format(name))
print(locals())
print(id(name))
return name
print(id(say_hello()))
这就是这个例子在谈论一个问题时的意思。frame.
但是物体呢?嗯,在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
They're all对象。但它们可能是不同的对象。你怎么知道呢?和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
请注意,您还可以比较两个对象是否是same对象通过使用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
呃呃……?等一下,那里发生了什么?好吧,事实证明,python
(即CPython)缓存小整数 https://stackoverflow.com/questions/306313/is-operator-behaves-unexpectedly-with-integers。所以对象512
与对象不同的是对象的结果500
添加到对象12
.
需要注意的一件重要事情是赋值运算符=
always分配一个新名称同一个物体。例如:
>>> x = 592
>>> y = 592
>>> x is y
False
>>> x == y
True
>>> x = y
>>> x is y
True
>>> x == y
True
无论你给一个对象赋予多少个其他名称,或者即使你通过了物体围绕不同的框架 https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument,你仍然有相同的对象。
但当您开始收集时,了解以下操作之间的区别非常重要:change一个对象和产生一个的操作new目的。一般来说,你有一个一些不可变类型 https://docs.python.org/3/reference/datamodel.html在Python中,对它们的操作将产生一个新对象。
至于你的问题,你什么时候想改变对象,什么时候你想保持它们不变,实际上是错误的看待方式。当您想要更改内容时,您希望使用可变类型;如果您不想更改内容,则希望使用不可变类型。
例如,假设您有一个群组,并且您想要向该群组添加成员。您可以使用可变类型(例如列表)来跟踪组,并使用不可变类型(例如字符串)来表示成员。像这样:
>>> 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']
您会注意到,您仍然拥有相同的组,只是成员发生了变化。