What is the difference between a frame and object, and when should I modify one over the other?

后端 未结 1 1616
梦如初夏
梦如初夏 2021-02-04 20:55

I started reading about python\'s += syntax and stumbled onto the following post/answer: Interactive code about +=

So what I noticed was that there seems to be a differe

相关标签:
1条回答
  • 2021-02-04 21:36

    frame and object don't mean what you think they mean.

    In programming you have something called a stack. In Python, when you call a function you create something called a stack frame. This frame is (as you see in your example) basically just a table of all of the variables that are local to your function.

    Note that defining a function doesn't create a new stack frame, it's the calling a function. For instance something like this:

    def say_hello():
        name = input('What is your name?')
        print('Hello, {}'.format(name))
    

    Your global frame is just going to hold one reference: say_hello. You can see that by checking out what's in the local namespace (in Python you pretty much have a 1:1 relationship between namespace, scope, and stack frames):

    print(locals())
    

    You'll see something that looks like this:

    {'__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>}
    

    Note the dunder (short for double underscore double underscore) names - those are automagically provided, and for the purposes of our discussion you can ignore them. That leaves us with:

    {'say_hello': <function say_hello at 0x101962d90>}
    

    That 0x bit is the memory address where the function itself lives. So here, our global stack/frame contains just that one value. If you call your function and then check locals() again, you'll see that name isn't there. That's because when you call the function you create a new stack frame and the variable is assigned there. You can prove this by adding print(locals()) at the end of your function. Then you'll see something like this:

    {'name': 'Arthur, King of the Brits'}
    

    No dunder names here. You'll also note that this doesn't show a memory address. If you want to know where this value lives, there's a function for that.

    def say_hello():
        name = input('What is your name?')
        print('hello {}'.format(name))
        print(locals())
        print(id(name))
        return name
    
    print(id(say_hello()))
    

    That's what the example means when it's talking about a frame.

    But what about objects? Well, in Python, everything is an object. Just try it:

    >>> 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 objects. But they may be different objects. And how can you tell? With 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
    

    Note that you also can compare whether or not two objects are the same object by using 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...? Wait a minute, what happened there? Well, as it turns out, python(that is, CPython) caches small integers. So the object 512 is different from the object that is the result of the object 500 added to the object 12.

    One important thing to note is that the assignment operator = always assigns a new name to the same object. For example:

    >>> x = 592
    >>> y = 592
    >>> x is y
    False
    >>> x == y
    True
    >>> x = y
    >>> x is y
    True
    >>> x == y
    True
    

    And it doesn't matter how many other names you give an object, or even if you pass the object around to different frames, you still have the same object.

    But as you're starting to gather, it's important to understand the difference between operations that change an object and operations that produce a new object. Generally speaking you have a few immutable types in Python, and operations on them will produce a new object.

    As for your question, when do you want to change objects and when do you want to keep them the same is actually looking at it the wrong way. You want to use a mutable type when you want to change things, and you want to use an immutable type if you don't want things to change.

    For instance, say you've got a group, and you want to add members to the group. You might use a mutable type like a list to keep track of the group, and an immutable type like strings to represent the members. Like this:

    >>> 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']
    

    What happens when a member of the group is eaten?

    >>> del group[-2]  # And there was much rejoicing
    >>> id(group)
    4325836488
    >>> group
    ['Sir Lancelot', 'Sir Gallahad', 'Sir Robin', 'King Arthur']
    

    You'll notice that you still have the same group, just the members have changed.

    0 讨论(0)
提交回复
热议问题