首頁  >  問答  >  主體

python中for语句中对当前遍历对象赋值的问题?

我有一个二维列表,列表中的元素是以字符串为元素的列表。
由于这些字符串表示的是数字,我想把这些字符串都转换成float型。

def loadCsv(filename):
    # 取出数据
    lines = csv.reader(open(filename, 'rb'))
    # 存入dataset
    dataset = list(lines)
    for data in dataset:
        for i in data:
            i = float(i) #为什么执行了该语句后i不变?
    return dataset

上面这段程序执行后dataset不变,为什么i = float(i)不能改变i的值呢?
虽然我知道这样写不是好习惯,但是想问python里for语句中所遍历的对象并不是在原对象上修改?

def loadCsv(filename):
    # 取出数据
    lines = csv.reader(open(filename, 'rb'))
    # 存入dataset
    dataset = list(lines)
    for i in range(len(dataset)):
        dataset[i] = [float(x) for x in dataset[i]]
    return dataset

这段程序能够成功,又是为什么呢?

PHP中文网PHP中文网2742 天前913

全部回覆(4)我來回復

  • 迷茫

    迷茫2017-04-17 17:25:44

    變數只是指向一個物件的指標

    理解一下,然後接著看。

    第一種: 在遍歷時,建立了一個名叫i的指針,並指向data中的某個元素。執行i=float(i)時,只是創建了一個新物件float(i),並讓i指向它,僅此而已。

    第二種同理。

    回覆
    0
  • ringa_lee

    ringa_lee2017-04-17 17:25:44

    準確地說i = float(i)的确改变了i的值,但i只是data元素的一份copy,并没有改变data的值,就好像:

    >>> a = ["ab", "cd"]
    >>> i = a[0]
    >>> i = 123
    >>> a
    ['ab', 'cd']

    第二種是因為你直接對原來的清單進行操作:

    >>> a[0] = 123
    >>> a
    [123, 'cd']

    回覆
    0
  • PHPz

    PHPz2017-04-17 17:25:44

    python 中,通過for遍歷物件的時候不能修改遍歷物件本身,這是一條通用的規則。
    一般來說都是透過 next() 方法來取得下一個元素。
    若是允許修改遍歷對象,就影響了元素的順序,導致每次next產生的結果不可控。
    可考慮用 enumerate 這種方式在來修改。

    for i,v in enumerate(data):

    data[i] = float(v)

    此時迭代物件為enumerate(data), 而不是data,所以可以修改。

    額外的資訊可參考pytohon官方手冊對for語句的描述:
    https://docs.python.org/2/reference/compound_stmts.html#the-for-statement

    回覆
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-17 17:25:44

    Python 中 for 遍歷通常不會建議(不是不能)直接修改遍歷物件本身,因為這樣就會出現類似如下的問題:for 遍历通常不建议(不是不能)直接修改遍历对象本身,因为这样就会出现类似如下的问题:

    n = [1, 2, 3]
    for i in n:
        n.append(i)
        print(i)

    上段程序会进入无限死循环,因为 n 在每次迭代时,长度都会增加,因此for永远不可能穷尽,所以我们通常会使用迭代对象的副本进行遍历:

    n = [1, 2, 3]
    for i in n[:]:
        n.append(i)
        print(i)

    这样就可以修改n的值,也可以顺利完成遍历。

    现在回到你的例子的第一段程序中:

    def loadCsv(filename):
        # 取出数据
        lines = csv.reader(open(filename, 'rb'))
        # 存入dataset
        dataset = list(lines)
        for data in dataset:
            for i in data:
                i = float(i) #这里前后两个`i`其实指代的是不同的对象
        return dataset

    事实上,你的i = float(i) 前后两个指代的根本不是同一个对象,后一个 idata的元素,前一个 i 则是loadCsv作用域内的局部变量,这里涉及到 Python 语言设计中的一个不合理的地方,来看一段程序:

    for i in range(3):
        pass
    
    print(i)
    # 2

    也就是说参与迭代的标识符 i 在退出 for 循环之后,仍然没有被回收,并且保留着与迭代最后一个值之间的关联,这对同名的全局变量会造成影响,时常会出现这样的错误:

    i = 7
    
    for i in range(3):
        pass
    
    print(i)
    # 2

    一个 for 循环之后,全局变量 i 的值尽然莫名其妙的变了,原因在于 i 其实并非对象本身,而是对象的标识符,Python 的标识符并非对象的属性,而是可以复用的命名空间的一部分。

    因此当 for 循环内有同名的 i 标识符被赋值时,情况就又不一样了:

    for i in range(5):
        i = 3
        
    print(i)
    # 3

    这里 i 的值完全等同于 for 内给它的赋值,原因在于 Python 中的赋值操作,就是将值对象与标识符关联的操作,最后一次迭代时,数值 3 会被关联到标识符 i,因此 i 就被绑定到新的对象上了,回到你的第一段程序,情况也就是如此:i = float(i) 是将值对象 float(i) 绑定到标识符 i,因此赋值后的 i 压根不是 data的元素对象,因此不会更改 dataset
    而你的第二段程序:

    def loadCsv(filename):
        # 取出数据
        lines = csv.reader(open(filename, 'rb'))
        # 存入dataset
        dataset = list(lines)
        for i in range(len(dataset)):
            dataset[i] = [float(x) for x in dataset[i]]
        return dataset

    for 内的 i 只是个索引,你修改的不是i,而是 dataset[i]标识符关联的对象 ,而dataset[i]则是dataset的组成元素,因此可以更改data rrreee

    上段程式會進入無限死循環,因為n 在每次迭代時,長度都會增加,因此for永遠不可能窮盡,所以我們通常會使用迭代物件的副本進行遍歷:

    rrreee

    這樣就可以修改n的值,也可以順利完成遍歷。 #🎜🎜# #🎜🎜#現在回到你的例子的第一段程式:#🎜🎜# rrreee #🎜🎜#事實上,你的i = float(i) 前後兩個指涉的根本不是同一個對象,後一個idata< /code>的元素,前一個i 則是loadCsv作用域內的局部變量,這裡涉及到Python 語言設計中的一個不合理的地方,來看一段程序:#🎜🎜# rrreee #🎜🎜#也就是說參與迭代的標識符i 在退出for 循環之後,仍然沒有被回收,並且保留著與迭代最後一個值之間的關聯,這對同名的全域變數會造成影響,時常會出現這樣的錯誤:#🎜🎜# rrreee #🎜🎜#一個for 迴圈之後,全域變數i 的值盡然莫名其妙的變了,原因在於i 其實並非物件本身,而是物件的標識符,Python 的標識符並非物件的屬性,而是可以重複使用的命名空間的一部分。 #🎜🎜# #🎜🎜#因此當 for 循環內有同名的 i 標識符被賦值時,情況就又不一樣了:#🎜🎜# rrreee #🎜🎜#這裡i 的值完全等同於for 內給它的賦值,原因在於Python 中的賦值操作,就是將值物件與識別碼關聯的操作,最後一次迭代時,數值3 會被關聯到識別符i,因此i 就被綁定到新的物件上了,回到你的第一段程序,情況就是如此:i = float(i) 是將值物件float(i) 綁定到標識符 i,因此賦值後的i 壓根不是data的元素對象,因此不會改變dataset
    而你的第二段程式:#🎜🎜# rrreee #🎜🎜#for 內的i 只是個索引,你修改的不是i,而是dataset[i]標識符關聯的物件,而dataset[i]則是dataset的組成元素,因此可以更改data。 #🎜🎜# #🎜🎜#關於標識符和命名空間,可以看一下這篇文章:Python 的命名空間#🎜🎜#

    回覆
    0
  • 取消回覆