V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
bytest
V2EX  ›  Python

关于 python 里的值传递

  •  
  •   bytest · 2016-12-27 20:12:47 +08:00 · 2024 次点击
    这是一个创建于 2670 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 就只告诉你简单,可是后面的坑得自己一个一个去踩,真心不爽。

    WKPlus
        1
    WKPlus  
       2016-12-27 20:34:27 +08:00
    python 里面都是引用,所以 a=x 只是把 a 指向另外一个对象并不会修改 a 原来指向的对象的值。然后, python 里面有 mutable 和 immutable 对象的概念,可以了解一下
    tonghuashuai
        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 绑定的内存中的值。
    Kilerd
        3
    Kilerd  
       2016-12-27 20:45:43 +08:00
    in python ,everything is a object.
    Kilerd
        4
    Kilerd  
       2016-12-27 20:46:02 +08:00
    anything
    zmj1316
        5
    zmj1316  
       2016-12-27 20:53:51 +08:00
    C++ 里面的 foreach (Range-based for loop) 也是类似的吧
    crab
        6
    crab  
       2016-12-27 20:54:35 +08:00
    第一次 1 个列表
    第二次是 list(range(i)) ,每一次里面又有一次列表,列表从 0 到 i 。
    bytest
        7
    bytest  
    OP
       2016-12-27 20:56:22 +08:00
    @crab 我知道……
    bytest
        8
    bytest  
    OP
       2016-12-27 21:01:11 +08:00
    @WKPlus
    @tonghuashuai
    谢谢二位。我知道可变和不可变对象的概念。

    自己想明白了,一直以为是 for 处理不同类型有区别,其实这里的关键在于“=”, append 方法修改了引用的对象,即列表中的列表,而“=”是重新绑定引用对象了。我试了第二个循环里如果写成 i = ['end'],原二维列表也不变了。
    bytest
        9
    bytest  
    OP
       2016-12-27 21:06:23 +08:00
    @zmj1316 你是说 STL algorithm 库里的 for_each ,还是 c++11 的 range for loop ?这是两个概念。这里问题的关键在于 python 里的“=”赋值的问题。
    zmj1316
        10
    zmj1316  
       2016-12-27 21:21:20 +08:00
    @bytest 我括号里说明了,并且我没看出来你这个两个样例中 python 的 = 有什么区别,因为你第二个里面没用到 = ,

    如果说你第二个里面写的是
    In [8]: for i in list2:
    ...: i = ['end']

    倒是可以有比较
    LPeJuN6lLsS9
        11
    LPeJuN6lLsS9  
       2016-12-27 22:54:06 +08:00
    维基有详细解释: https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing
    这翻来覆去讨论的问题,知乎是有,不知道本站有没有

    效果和 C++的引用传递类似,不过不能用来把函数调用者作用域内的变量改掉( out parameter 可以返回元祖代替),所以不叫引用传递
    CRVV
        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;
    }
    ```
    mnzlichunyu
        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 的内容是不会发生改变的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1514 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 17:08 · PVG 01:08 · LAX 10:08 · JFK 13:08
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.