python 入门很快,可是细枝末节的东西还是很多的,弄不明白。
In [1]: list1 = list(range(5)) In [2]: for i in list1: ...: i = 100 ...: In [3]: list1 Out[3]: [0, 1, 2, 3, 4]
list1 没变化。
In [4]: list2 = [] In [5]: for i in range(5): ...: list2.append(list(range(i))) ...: In [6]: list2 Out[7]: [[], [0], [0, 1], [0, 1, 2], [0, 1, 2, 3]] In [8]: for i in list2: ...: i.append('end') ...: In [9]: list2 Out[9]: [['end'], [0, 'end'], [0, 1, 'end'], [0, 1, 2, 'end'], [0, 1, 2, 3, 'end']]
list2 中的每个列表都变化了,换成 dict 也是变了的。
前面是值传递?后面是引用传递? 抱歉,我知道 c++里这么叫,请教 python 里这是什么特点?
python 的书现在越来越多,入门的都讲的太简单,就跟给小学生看的一样,看完入门书用 python 写代码到处都是问题。 请问大家有没有推荐好些的中高级进阶的书?最好中文,看得快,不要讲 api 或者应用的,要谈语法或者语言特性的?
被 c++虐惯了,换到 python 就只告诉你简单,可是后面的坑得自己一个一个去踩,真心不爽。
1
WKPlus 2016-12-27 20:34:27 +08:00
python 里面都是引用,所以 a=x 只是把 a 指向另外一个对象并不会修改 a 原来指向的对象的值。然后, python 里面有 mutable 和 immutable 对象的概念,可以了解一下
|
2
tonghuashuai 2016-12-27 20:38:47 +08:00 1
python 中不存在所谓的传值调用,一切传递的都是对象的引用,也可以认为是传址。 python 中,对象分为可变(mutable)和不可变(immutable)两种类型,元组( tuple)、数值型( number)、字符串(string)均为不可变对象,而字典型(dictionary)和列表型(list)的对象是可变对象。
a = 1 #将名字 a 与内存中值为 1 的内存绑定在一起 a = 2 #将名字 a 与内存中值为 2 的内存绑定在一起,而不是修改原来 a 绑定的内存中的值。 |
3
Kilerd 2016-12-27 20:45:43 +08:00
in python ,everything is a object.
|
4
Kilerd 2016-12-27 20:46:02 +08:00
anything
|
5
zmj1316 2016-12-27 20:53:51 +08:00
C++ 里面的 foreach (Range-based for loop) 也是类似的吧
|
6
crab 2016-12-27 20:54:35 +08:00
第一次 1 个列表
第二次是 list(range(i)) ,每一次里面又有一次列表,列表从 0 到 i 。 |
8
bytest OP @WKPlus
@tonghuashuai 谢谢二位。我知道可变和不可变对象的概念。 自己想明白了,一直以为是 for 处理不同类型有区别,其实这里的关键在于“=”, append 方法修改了引用的对象,即列表中的列表,而“=”是重新绑定引用对象了。我试了第二个循环里如果写成 i = ['end'],原二维列表也不变了。 |
9
bytest OP @zmj1316 你是说 STL algorithm 库里的 for_each ,还是 c++11 的 range for loop ?这是两个概念。这里问题的关键在于 python 里的“=”赋值的问题。
|
10
zmj1316 2016-12-27 21:21:20 +08:00
@bytest 我括号里说明了,并且我没看出来你这个两个样例中 python 的 = 有什么区别,因为你第二个里面没用到 = ,
如果说你第二个里面写的是 In [8]: for i in list2: ...: i = ['end'] 倒是可以有比较 |
11
LPeJuN6lLsS9 2016-12-27 22:54:06 +08:00
维基有详细解释: https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing
这翻来覆去讨论的问题,知乎是有,不知道本站有没有 效果和 C++的引用传递类似,不过不能用来把函数调用者作用域内的变量改掉( out parameter 可以返回元祖代替),所以不叫引用传递 |
12
CRVV 2016-12-27 23:16:01 +08:00
从行为来说, immutable 的类型和值类型一样, mutable 的类型和指针一样
正经的解释当然是名字绑定什么的那些 等价的 C++ 代码大概是: ``` #include <iostream> #include <vector> template<typename T> void print(T& arg) { std::cout << arg << ", "; } template<typename T> void print(std::vector<T>* list) { for (auto& i : *list) { print(i); } std::cout << std::endl; } std::vector<int>* range(int length) { auto result = new std::vector<int>(); for (int i = 0; i < length; ++i) { result->push_back(i); } return result; } int main() { auto list1 = range(5); print(list1); for (auto i : *list1) { i = 100; } print(list1); auto list2 = new std::vector<std::vector<int>*>(); for (int i = 0; i < 5; ++i) { list2->push_back(range(i)); } print(list2); for (auto i : *list2) { i->push_back(-1); } print(list2); for (auto i : *list2) { i = range(0); } print(list2); return 0; } ``` |
13
mnzlichunyu 2016-12-28 12:13:04 +08:00 1
一般这种奇怪的问题,大多数都能从 bytecode 上找到原因。楼主给的第一种情况的字节码是这样的:
>>> dis.dis(list_change) 2 0 LOAD_GLOBAL 0 (list) 3 LOAD_GLOBAL 1 (range) 6 LOAD_CONST 1 (5) 9 CALL_FUNCTION 1 12 CALL_FUNCTION 1 15 STORE_FAST 0 (list1) 3 18 SETUP_LOOP 20 (to 41) 21 LOAD_FAST 0 (list1) 24 GET_ITER >> 25 FOR_ITER 12 (to 40) 28 STORE_FAST 1 (each) 4 31 LOAD_CONST 2 (1) 34 STORE_FAST 1 (each) 37 JUMP_ABSOLUTE 25 >> 40 POP_BLOCK >> 41 LOAD_CONST 0 (None) 44 RETURN_VALUE 您可以注意 index 为 28,34 的 STORE_FAST ,根据字节码来看,你给的第一种情况和下面的代码是等效的: list1 = list(range(5)) for index in range(len(list1)): i = list1[index] i = 100 所以, list1 的内容是不会发生改变的。 |