【问题标题】:float() object id creation orderfloat() 对象 id 创建顺序
【发布时间】:2018-12-12 18:42:58
【问题描述】:
float(1.0) is float(1.0) #True
float(1) is float(1) #False

我已将此处的 float() 怪异与对象创建顺序隔离开来,因为

x1 = float(1)
x2 = float(1)
x1 is x2 #False
id(x1) == id(x2) #False
y1 = float(1.0)
y2 = float(1.0)
y1 is y2 #True
id(y1) == id(y2) #True

注意:我已经检查了浮点数的精度,但这不是发生这种情况的原因。

我想了解 Python 为什么以及如何决定创建浮点对象。 为什么 float(1.0) 指向同一个对象,而 float(1) 指向 2 个不同的对象,其中一个对象被创建了两次?

另外,供进一步参考:

float(1) is float(1) #False
id(float(1)) == id(float(1)) #True
float(1.0) is float(1.0) #True
id(float(1.0)) == id(float(1.0)) #True

【问题讨论】:

  • is== 不同。
  • IornPython ... 4 个陈述都是错误的
  • @Austin 我很清楚is== 不同。请阅读问题。我显然是在询问对象创建顺序。
  • @ycx,我没有回答你的问题,这就是为什么它是一个评论。
  • @wim 我。好奇心。是的。

标签: python python-3.x floating-point


【解决方案1】:
>>> float(1.0) is float(1.0)
True

这是因为 float 返回对象本身,因为它已经是 float(对于字符串 BTW Should I avoid converting to a string if a value is already a string? 也是如此)。

检查source code 以确认(添加了 cmets):

static PyObject *
float_float(PyObject *v)
{
    if (PyFloat_CheckExact(v))   // if v is already a float, just increase reference and return the same object
        Py_INCREF(v);
    else
        // else create a new float object using the input value
        v = PyFloat_FromDouble(((PyFloatObject *)v)->ob_fval);
    return v;
}

并且对文字1.0 的引用可能在编译时共享(这是定义的实现,这是我能想到的唯一解释,Dunes answer 解释得更好),所以它与1.0 is 1.0 相同。

>>> float(1) is float(1)
False

Python 必须为每一边创建浮点对象,所以它是不同的。没有像整数那样的浮点实习。

最后一个有趣的部分:

>>> id(float(1)) == id(float(2))
True

因为float对象在id被调用后被垃圾回收,所以id被重用,即使字面值是不同如上例显示(在Unnamed Python objects have the same idWhy is the id of a Python class not unique when called quickly? 中公开)。

【讨论】:

  • @ycx 直观地说,float(1.0) 不会创建新对象,而是引用包含 1.0 值的原始对象。因此,每次调用 float(with_float_value) 时,它都指的是前一个对象,而不是创建新对象。但是float(1) 每次都必须创建一个新对象,所以它的 id 每次都会不同。
  • 关于你的最后一点:确切地说,不是对象被重用,而是第二个对象是在与第一个对象相同的地址创建的。
  • @glglgl 没有别的说法,我的回答说 id 被重用,而不是 object
  • @ycx 检查了源代码并编辑了我的答案。我必须猜测的最后一点是浮动文字的共享引用,我没有引用(哈哈)。
  • 值得注意的是,将id 用于两个连续对象是 CPython 的一个怪癖。它实际上保证会在 CPython 中发生,但实际上保证不会在我所知道的所有其他 Python 实现中发生。
【解决方案2】:

1.0 是浮点对象的文字语法,因此解释器必须创建一个可以传递给float 的浮点对象。由于浮点数是不可变的,float 函数可以只返回未更改的对象。另一方面,1 是整数的文字语法。因此,float 函数必须创建一个新的浮点对象。在同一代码块中,解释器有时(并非总是)能够识别不可变对象的两个字面量相同,然后它能够​​缓存该对象并重用于其他引用。这是内部内存优化,不应依赖。

因此:

def f():
    x = 1.0
    y = float(1.0)
    z = float(x)
    assert x is y # x and y are the same object
    assert x is z # z is also the same as both x and y
f()

但是:

def f():
    return 1.0

def g():
    return 1.0

assert f() is not g() # The interpreter was not able to detect it could reuse the same object

长话短说,有时python中相等的数字可能也是完全相同的对象,但这不能保证。

【讨论】:

  • 欣赏关于不变性和文字的详细解释。您还介意在float(1) is float(1) #False; id(float(1)) == id(float(1)) #True 上解释一下吗?是Jean-François Fabre 在他关于垃圾收集的回答中所说的还是你有更好的解释?
  • @ycx 我在我的回答中添加了 2 个链接。如果你问我,这两个答案都很完美。关于python仅在同一个表达式中重用同一个对象的部分很酷。
猜你喜欢
  • 2014-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-02
  • 2016-06-02
  • 2017-03-12
相关资源
最近更新 更多