python如何执行变量赋值?

时间:2015-07-27 03:45:39

标签: python memory memory-management

这不是一个“操作方法”问题,而是关于python如何存储变量的问题。

据我所知(如果我错了请纠正我),当在(例如)C中创建变量时,该值存储在内存中,该内存的地址存储在变量名下。在python中,它在内存中创建值,然后使用变量名称“标记”该内存地址。所以,如果你这样做:

>>> x = 3
>>> y = x
>>> id(x)
42
>>> id(y)
42

x和y的内存地址相同

然后如果我这样做:

>>> x = 4
>>> print y
3

应该如此。如果我查询地址:

>>> id(x)
46
>>> id(y)
42

似乎python为新的x值创建了新的内存,y保持不变。但是当我这样做时:

>>> y = 4
>>> id(y)
46

似乎python已经在内存中搜索了一个与输入变量匹配的值,然后标记了名为“y”的内存地址 - 如果没有找到这样的值,那么它会在内存中创建一个新值然后标记

我在这里思考是对的吗?

我发现这个问题对我的小“x”和“y”示例来说都很好,但是对于一个包含数百万个变量的程序呢?这是否意味着无论何时分配变量值,它都必须在分配新内存之前搜索整个内存,寻找匹配?

直观地说,虽然这种方法肯定会节省大量空间,但它可能不会非常节省时间,但是我想很多人,比我更聪明,投入了大量的精力和工作来制作python ,显然情况并非如此。所以我的问题是我的理解中缺少什么使得这种方法成为内存管理的好方法?

仅仅是通过列表搜索最多时间复杂度为O(n)的问题,对于快速处理器来说,这不是一个真正的问题吗?还是有更深层次的东西?

4 个答案:

答案 0 :(得分:0)

首先,你要问的是字面意思定义为" implementation defined"。

我的猜测是,Python实际上是在编译时进行检查,根据它的知识优化内存布局。

我几乎可以保证你不会搜索每个变量。首先,它必须知道什么是可变的,什么不是(提示:"什么'不是"是一个非常短的列表)。如果它是可变的,它就不能重复使用旧的。

答案 1 :(得分:0)

这里有CPython实现的详细说明:

http://www.laurentluce.com/posts/python-integer-objects-implementation/

你看到的行为是因为python特殊情况下处理小整数,因为它们很常见。例如,尝试这样可以看到不同的结果。

>>> x = 88888888888888888888888
>>> y = 88888888888888888888888
>>> id(x)
4321062304
>>> id(y)
4321062344

答案 2 :(得分:0)

  

变量在(例如)C中创建,该值存储在内存中,该内存的地址存储在变量名称下

没有。在C中,创建变量,并在不迟于声明它的语句的执行中获取内存中的位置(就声明是可执行的而言)。对该变量的赋值会改变其内存位置。

  

在python中,它在内存中创建值,然后用变量名“标记”该内存地址。

右。或者,等效地,变量将指针(无论何种类型)保存到对象。

  

id

id不返回内存地址。它返回一个整数,该整数仅在该对象的生命周期内为该特定对象返回。

  

似乎python已经在内存中搜索了一个与输入变量匹配的值

此行为是实现定义的,并且在CPython中仅针对整数0-127发生。在任何其他值的情况下都不会发生这种情况。您也可以“击败”这种实习行为。

此外,没有扫描。实习可能使用数组来存储实习值,因为它们是整数。

答案 3 :(得分:0)

我很谨慎地说id代表一个“内存地址” - 它恰好是在当前版本的CPython中以内存地址的形式实现的,但这几乎不能保证(并且它在例如,PyPy)。

但除了那个狡辩之外,你的想法大致正确,尽管这种机制比你想象的更简单,更快捷。

CPython(但不一定是其他实现)的作用是预先分配和缓存一定数量的“小”整数(当前从-5到包括256的所有内容)。然后,当请求其中一个时,它会在此缓存中找到它,而不是分配新的Python对象。但是,不是你想象的O(n)线性搜索,它实际上是作为O(1)C数组查找实现的。你可以在CPython的intobject.c中看到它是如何工作的:

#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS           257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS           5
#endif
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
/* References to small integers are saved in this array so that they
   can be shared.
   The integers that are saved are those in the range
   -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
#endif
#ifdef COUNT_ALLOCS
Py_ssize_t quick_int_allocs;
Py_ssize_t quick_neg_int_allocs;
#endif

PyObject *
PyInt_FromLong(long ival)
{
    register PyIntObject *v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
    if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
        v = small_ints[ival + NSMALLNEGINTS];
        Py_INCREF(v);
#ifdef COUNT_ALLOCS
        if (ival >= 0)
            quick_int_allocs++;
        else
            quick_neg_int_allocs++;
#endif
        return (PyObject *) v;
    }
#endif
    if (free_list == NULL) {
        if ((free_list = fill_free_list()) == NULL)
            return NULL;
    }
    /* Inline PyObject_New */
    v = free_list;
    free_list = (PyIntObject *)Py_TYPE(v);
    PyObject_INIT(v, &PyInt_Type);
    v->ob_ival = ival;
    return (PyObject *) v;
}