python中for语句中对当前遍历对象赋值的问题?
PHP中文网
PHP中文网 2017-04-17 17:23:44
0
4
882

我有一个二维列表,列表中的元素是以字符串为元素的列表。
由于这些字符串表示的是数字,我想把这些字符串都转换成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中文网

认证高级PHP讲师

全部回覆 (4)
迷茫

变量只是指向一个对象的指针

理解一下,然后接着看。

第一种: 在遍历时,创建了一个名叫i的指针,并指向data中的某个元素。执行i=float(i)时,只是创建了一个新对象float(i),并让i指向它,仅此而已。

第二种同理。

    左手右手慢动作

    准确地说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']
      PHPzhong

      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

        伊谢尔伦

        Python 中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

        关于标识符和命名空间,可以看一下这篇文章:Python 的命名空间

          最新下載
          更多>
          網站特效
          網站源碼
          網站素材
          前端模板
          關於我們 免責聲明 Sitemap
          PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!