python的参数传递规则与c的参数传递规则的主要区别是什么(如果有的话)?
我非常熟悉python,只开始学习c。我想知道我是否可以考虑规则集,比如当一个对象被引用传递时,或者按照C的值传递时,就像在python中一样,或者我需要记住一些关键的区别。
- 在python中通过引用传递,从何时开始?????
- python按值传递对对象的引用:link
- 我知道你一定在谈论一些附加的列表,我还没有看到链接,但我想它一定是……
- 马克斯:对。但是传递引用值只意味着通过引用传递。简单地说,传递4个字节,字节是通过引用传递的对象的地址。
- @不,这不是通过参考。"pass-by-reference"中的"reference"指的是一个不同的概念,即允许您操作存储位置(变量、对象属性、集合中的槽等)并且不存在于python中的内容。旁路参考允许def f(x): x = whatever起作用。
- @Delnan:传递值意味着复制值。这不是Python的情况。不复制对象的值。传递值还意味着您不能从函数内部修改外部对象。从技术上讲,通过引用传递意味着复制目标对象的地址。这就是python的情况。这个问题隐藏在疑问中:在python中什么是变量,在c和数学中说是变量。是的,传递的参数允许您在Python中操作外部对象。问题是赋值运算符所做的操作超出了您的预期。
- stackoverflow.com/a/12438316/1346705增加了直观说明
C按值传递参数,除非您指定要以不同的方式传递参数。如果参数类型是结构,则复制其值,否则复制对对象的引用。返回值也是如此。
可以使用ref或out修饰符修改此行为,修饰符必须在方法声明和方法调用中都指定。两者都将该参数的行为更改为按引用传递。这意味着您不能再传递更复杂的表达式。ref和out的区别在于,当将变量传递给ref参数时,它必须已经初始化,而传递给out参数的变量不必初始化。在该方法中,out参数被视为未初始化的变量,必须在返回之前为其赋值。
- 关于REF,OP可能会发现这个问题很有趣。
- 之所以选择这个答案,是因为它是唯一一个告诉我C中传递参数是如何工作的。
型
将python称为"传递值"或"传递引用"语言,并与C、C等进行比较的问题是,python对数据的引用方式有不同的概念。python不容易适应传统的按值或按引用二分法,导致混淆和"它是按值调用的!"不,这是参考电话,我可以证明!"不,你这栗色的,很明显这是按价值计算的!"无穷无尽的循环。
事实上,两者都不是。python使用按共享调用(也称为按对象调用)。有时,这似乎是一种按值策略(例如,当处理诸如int、float和str等标量值时),有时也像一种参照策略(例如,当处理诸如list、dict、set和object等结构化值时)。DavidGoodger的代码就像一个pythonista,完美地总结了这一点,"其他语言有变量;Python有名字。"作为一个额外的好处,他提供了清晰的图形来说明不同之处。
在封面下,共享调用的实现更像是引用调用(正如Noctis Skytower提到的一个突变的float示例所演示的那样),但是如果你把它看作引用调用,你会很快偏离轨道,因为引用是实现,它们不是公开的语义。
相比之下,C使用按值调用或参照调用——尽管有人可能会认为,out选项代表了一种超出C、pascal等中所示的纯参照调用的微调。
所以,python和c确实是非常不同的——在体系结构级别,无论如何。在实践中,按价值和参照的结合将允许您创建与共享调用非常相似的程序——尽管在细节和角落的案例中有一个棘手的小魔鬼。
如果您有兴趣了解不同语言在比较上下文中的参数传递策略,维基百科关于表达评估策略的页面值得一读。虽然这并不详尽(有很多方法可以剥下这只特别的猫的皮!)它巧妙地涵盖了一系列最重要的内容,以及一些有趣的、不常见的变化。
- 共享调用仅仅是由Barbara Liskov给出的特定类型的按值传递(其中传递的值总是隐式的指针),它在CLU中实现,后来在SimulalTalk、Objul-C、Java、C、Python、Ruby、Soad、NeXTalk、IO、IOKE、Seph等语言中实现。按共享调用是按值传递的。这也是Java和默认C语言使用的,至少对于引用类型也是如此。
- my+1@j&246;rgwmittag:问题在于,您需要考虑变量,并在传递地址后将其命名。但地址不是参数值。此外,当传递占用大量内存的参数时,传递值意味着复制大量内存。历史上,传递引用是为了避免复制,或者通过修改传递的变量来允许输出。
- 当然,python是按值调用的。以完全相同的方式,所有按引用调用都是按值调用的。在CPU寄存器级别,一切都是一个值。甚至参考文献。"哈!按值调用!按值调用!QED!"接下来,让我们来讨论一下,物体只不过是图灵机上的一层糖衣而已。再想一想——不,我们严肃点。当我们为不同的意图和效果组织事物时,我们给它们不同的名称来表示不同的语义和结果。call by-引用、共享、复制回等不能有效地简化为按值调用。
- python和c(不使用ref或out)表现出完全相同的语义。那么,为什么C传递值而python不传递?
- @乔纳森尼斯:谢谢你添加了关于Python如何工作的解释。认识到传递引用实际上是传递值的一种形式是一种很好的洞察力。正如您所指出的,按值调用是"没有用处的可还原性"。
- @Pepr:很好地指出传递值会在内存中创建数据副本,而传递引用只会创建指向内存中已有内容的指针!
- @j&246;rgwmittag:当您将一个由20字节组成的结构传递给c中的一个函数(不带ref和out时),复制了多少字节?
- @Pepr:当您在C中将值类型作为参数传递给子例程时,整个值将被复制到参数中,就像在Python中一样。在C中将引用类型作为参数传递给子例程时,只有引用被复制到参数中,就像在Python中一样。
- @j&246;rgwmittag:当您将值类型作为参数传递时,该值不会在python中复制。只传递参数对象的引用值。您可以在调用外部和函数内部检查参数的id()--您将获得相同的标识。
- 表达这种想法的另一种方法是说,如果python中的所有对象都是可变的,并且如果赋值操作符总是改变对象而不是重新绑定变量名,那么python(明确地)就是引用语言调用。这说明了by-reference/by-value的区别对于python来说是多么的不足:它应该是评估样式之间的区别,但是赋值(语句)的语义才是真正的区别所在。
python总是使用pass-by引用值。也不例外。任何变量分配都意味着分配参考值。也不例外。任何变量都是绑定到引用值的名称。总是。
您可以将reference视为使用时自动取消引用的目标对象的地址。这样,您似乎可以直接处理目标对象。但是在两者之间总是有一个参照物,要跳到目标上还要多走一步。
更新——这里有一个需要的示例,证明通过引用传递:
如果参数是按值传递的,则不能修改外部lst。绿色是目标对象(黑色是存储在其中的值,红色是对象类型),黄色是内存,其中的引用值——绘制为箭头。蓝色实心箭头是传递给函数的参考值(通过蓝色虚线箭头路径)。难看的深黄色是内部词典。(实际上它也可以画成绿色的椭圆。颜色和形状只表示它是内部的。)
更新——与fgb关于通过引用示例swap(a, b)的评论和delnan关于不可能编写swap的评论有关。
在编译语言中,变量是能够捕获类型值的内存空间。在python中,变量是绑定到保存目标对象引用值的引用变量的名称(在内部捕获为字符串)。变量的名称是内部字典中的键,该字典项的值部分存储对目标的引用值。
其他语言中的swap的目的是交换传递变量的内容,即交换内存空间的内容。这也可以在python中实现,但只适用于可以修改的变量——这意味着可以修改其内存空间的内容。这仅适用于可修改的容器类型。从这个意义上讲,一个简单的变量总是常量,即使它的名称可以被重用用于其他目的。
如果函数应该创建一些新的对象,那么获取它的唯一方法是通过容器类型参数,或者通过python return命令。但是,python return在语法上看起来好像可以传递多个参数。实际上,传递到外部的多个值形成了一个元组,但可以在语法上将该元组分配给更多的外部python变量。
与其他语言中的变量模拟相关的更新。内存空间是由单个元素列表模拟的——也就是说,还有一个间接级别。那么,swap(a, b)就可以像用其他语言一样编写。唯一奇怪的是,我们必须使用列表中的元素作为对模拟变量值的引用。这样模拟其他语言变量的必要性是因为只有容器(容器的子集)是Python中唯一可以修改的对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| >>> def swap(a, b):
... x = a[0]
... a[0] = b[0]
... b[0] = x
...
>>> var1 = ['content1']
>>> var2 = ['content2']
>>> var1
['content1']
>>> var2
['content2']
>>> id(var1)
35956296L
>>> id(var2)
35957064L
>>> swap(var1, var2)
>>> var1
['content2']
>>> var2
['content1']
>>> id(var1)
35956296L
>>> id(var2)
35957064L |
注意,现在的var1和var2模拟了经典语言中"正常"变量的外观。swap更改了内容,但地址保持不变。
对于可修改对象(例如列表),您可以编写与其他语言完全相同的swap(a, b):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| >>> def swap(a, b):
... x = a[:]
... a[:] = b[:]
... b[:] = x[:]
...
>>> lst1 = ['a1', 'b1', 'c1']
>>> lst2 = ['a2', 'b2', 'c2']
>>> lst1
['a1', 'b1', 'c1']
>>> lst2
['a2', 'b2', 'c2']
>>> id(lst1)
35957320L
>>> id(lst2)
35873160L
>>> swap(lst1, lst2)
>>> lst1
['a2', 'b2', 'c2']
>>> lst2
['a1', 'b1', 'c1']
>>> id(lst1)
35957320L
>>> id(lst2)
35873160L |
注意,像a[:] = b[:]这样的多重赋值必须用于表示列表内容的复制。
- 投反对票的是恶意的或错误的,或者他/她不认识Python。通过例子可以证明我所说的是真的。但是,您不能证明python传递的是值。
- 我不是投反对票的人,但"分配参考值"与"传递参考"不同。看看这个问题:stackoverflow.com/questions/986006/…听起来python确实使用了pass-by值,其中(如c)变量的值从来不是实际的对象,只是一个引用。
- @乔恩斯基特:即使被引用的问题说的不是这样,它还是很和谐的。通过引用传递是否意味着传递引用值,这是一个关于单词的游戏。当然。但引用值只是对象的地址。通过引用传递是指传递对象的地址。这种方法也在Python中使用。将参数传递给函数实际上意味着从外部为函数的局部变量赋值。这与Python中的任何其他分配都是相同的分配。
- @佩珀:你说:"可以用例子证明我说的是真的。"好吧,那就证明吧!您还说:"但是,您不能证明python是按值传递的。"我在python中提供了一个pass-by值的示例。当然,一个例子并不能证明这一点,您必须参考Python语言规范。另一方面,无论您还是其他人都无法在python中提出pass-by-reference的例子,所以我认为这是一个有力的迹象,表明我是对的。
- 如果它是与其他任务相同的类型,那么它就是传递值。传递引用意味着传递的/局部变量是彼此的引用。对变量的任何更改(包括赋值)都具有相同的效果,无论使用哪个变量。
- 此示例不演示按引用传递。它表明列表是可变的。但这不是问题。没有人声称python是一种纯粹的函数式语言。这与传递参考值和传递值完全正交。这就是共享可变状态的工作原理。
- @我理解你的困惑…python中的任何赋值都意味着复制对目标对象的引用。这就是如何实现python变量。
- @Pepr:你写到:"在python中任何赋值都意味着复制对目标对象的引用。"好吧,这就是传递值的定义。
- @j&246;rgwmittag:在任何编程语言中传递值时,规则是不能修改外部对象。这是使用传递值的原因之一。传递值意味着复制值,而不是引用。如果在4个字节上有整数,只需复制包含内容的4个字节。如果数组中有1000个元素,那么按值传递意味着复制所有1000个元素的内容。
- @佩普,我不困惑。我知道变量和引用是如何工作的。你只是重复一些我们都知道与争论无关的事情。
- @fgb:在python中,除非您明确地这么说,否则永远不会复制1000个元素。永远不要复制元素,因为它是作为参数传递给函数的。
- @不,传递值意味着对参数的修改不会修改参数。这与说修改参数引用的对象不是通过参数看到的不同。在这方面,它听起来像是python就像c。请阅读pobox.com/~skeet/csharp/parameters.html了解C版本。传递引用确实不同。
- @佩普:在你给出的代码中,如果你写了:x = ['modified']而不是x.append('modified'),会发生什么?呼叫方是否会看到x的值发生变化?如果是这样,那就是一个很好的引用传递的例子。如果没有,则使用传递值-参数的值(lst是一个引用,然后通过值传递。
- @琼斯基特:说得对。问题是,当您执行x = ['aaaa']操作时,会重用名称x来绑定到另一个对象['aaaa']的引用。您没有使用作为参数传递的引用。换句话说,您忘记了局部变量x之间的绑定,而将x用于另一个目的。在python中,x只是一个绑定到引用变量的字符串。将字符串重新绑定到示例中的另一个引用变量。
- PPR:我不能用Python说话,但听起来很像Java和C语言中的价值工程。除此之外,你会说只有一个x变量,你给它一个不同的值。在任何情况下,x的值都不是一个对象,而是一个引用。
- @Pepr:阅读python文档,第5.3.4节谈到:"否则,参数的值被放在槽中,填充它",这听起来像是传递值。它是所用参数的值。同样,第7.6节中的"函数调用始终为参数列表中提到的所有参数赋值,无论是位置参数、关键字参数还是默认值"。到目前为止,我在python文档中没有读到任何建议传递引用语义的内容——而且您的示例也肯定没有演示它。
- @乔恩斯基特:嗯,槽实际上是一个参考变量。该值是参考值。当使用引用值时,您将按引用排序。还有一个时刻会引起混乱。任何引用变量的行为(在任何语言中)就像您直接处理目标对象一样(因为自动取消引用)。不管怎样,技术上的区别是有一个间接的层次。以我为例…如果外部对象不是通过引用传递的,如何修改它?
- @ PPER:因为引用将通过值传递——这正是C和Java工作的方式。老实说,我得出的结论是,"传递值"和"传递引用"的传统术语可能对Python没有帮助。很容易看出它们是如何工作的,因为变量的值是非常明确的定义,而在Python中有绑定和变量,听起来好像是不同的东西,引入混乱…
- @琼斯基特,我同意。对于如何传递变量,python应该有自己的名称。它的行为是非常熟悉的,但是底层的逻辑导致了很多混乱。
- @麦克斯:过去我也很困惑。实际上,当您专注于回答以下问题时,困惑的根源就消失了:Python中的变量意味着什么?在python中的赋值意味着什么?.经典语言中的变量表示足够大的存储类型值的内存空间。它的名称通常在编译过程中丢失(转换为地址等)。在python中创建一个变量意味着将其名称存储为一个字符串加上4个字节的副本——引用值。任务就是这样做的手段。不多不少。
- 这个答案是错误的,这个:the arguments are actually the copied reference values. But this is excactly what is named passing by reference in all other languages.也是错误的。您描述的是按值传递引用,这与按引用传递不同。引用传递的参数是传递变量的别名,函数中的赋值在调用方中可见。您的列表示例演示了可变性,传递约定不相关。
- @ Lee:好的。当通过引用传递时(在其他语言中),什么形式的变量使用别名?在某种意义上,目标对象的引用值、指针值和地址是相同的。通过引用传递时,必须始终传递地址(技术上)。您如何调用传递示例中使用的参数的类型?注意,python中的赋值操作符只在内部复制引用值。它不同于经典编译语言中的赋值运算符导致复制右侧对象的内存。
- @你的答案描述了通过值传递引用。lst是对列表的引用,它被复制(即通过值传递)到func中的局部变量x中。func可以通过引用改变传递的列表,但不能更改调用方中lst指向的列表。这也是引用类型在C中默认传递的方式,但是您也可以使用ref或out关键字通过引用传递它们,这就是为什么区分通过引用传递和通过值传递引用很重要的原因。
- 没有阅读所有的注释:传递值的公共意义不适合Python语义,并且数据模型与C和C++完全不同。不过,我必须反对。正如我之前解释过的,按引用传递意味着(a)存在存储位置,(b)能够"引用"此类存储位置。python的数据模型可以以一种涉及存储位置的方式描述,其中所有存储位置都包含对值/对象的"引用"。但根本没有等价于(b)。你不能写swap。
- 相比之下,让我们为python定义引用:除了常规的"名称"(这些名称是对对象的引用)之外,还可以有其他一些含糊不清的东西(semm)。让我们把它们表示为x&,而不是x。然后,ref& <- x将semm绑定到变量,ref& <- obj.attr将semm绑定到对象属性,ref& <- arre[key]将semm绑定到集合中的槽。ref& = ...现在更改名称、对象属性或集合槽引用的对象。def swap(x&, y&): x&, y& = x&, y&现在具有效力,称为swap(x, a[i]),而不是swap(1, ["foo"])。
- 这些都与程序是编译还是解释无关。赋值和参数传递不会复制对象,这只是对象是否具有引用语义或值语义的问题。在Python中,所有东西都有引用语义,这就是为什么事物是这样的。但是引用语义也存在于编译语言中(RPython是一种编译语言,具有引用语义专用,在C和C++中有指针)。
- @李:我明白你的意思了。但是,如果我们从活动的单线程的角度来考虑,就没有办法使名为lst的目标对象在外部和x的目标对象在内部有所不同。每当我修改列表时,我都会修改内部和外部已知的相同列表。当谈到引用传递时,我指的是经典编译语言中使用的方法。不复制对象,只传递引用,并且可以从内部修改对象。这种方法在Pascal,现代C,C++中是这样的。通过EDCOX1×11或通过C或C++中的指针完成结果。
- @ PPER——C++中的PASS引用与您在Python中如何分配给C++中的REF参数的描述不一样,在调用方中是可见的。如void modify(int &i) { i = 10; }。无法用Python编写这样的函数。C不支持按引用传递,只传递指针,指针本身按值传递。
- @德尔南:我知道了。但是,不能用与其他语言语法相同的python编写swap的原因仅仅意味着没有可用的指针类型变量(直接)。如果c中的out意味着我可以使用内部传递的未初始化变量(纠正我,我在c中不好)。这意味着您将分两个阶段创建初始化变量:1)外部内存分配,2)函数/过程中分配的内容。但是,在Python中,只能修改集合对象。这样,它总是与任务密切相关。
- 这样,python就必须为输出目的(语法上)使用多重赋值。创建的简单对象必须在单个阶段中创建。那么,a, b = b, a就是交换对象的方法。不管怎样,如果之前做过c = a,它将指向与交换后b相同的对象。要在Python中将变量模拟为充满内存的空间,您必须始终使用一个间接寻址,因为简单值始终是常量。在C和C++中,你也有引用(指针旁边)。
- (1)C的out确实支持填充未初始化的变量,但是在python中没有什么可以阻止这种特性的——已经存在未初始化的局部变量(在函数中尝试print(x); x = 1)。(2)我不知道你对最新评论的看法是什么。(3)您在20:55评论中引用的"通过引用"的定义是错误的。不复制对象和修改对象可以通过任何间接的方式实现,并且通过引用传递引用还允许您批量替换值/对象(而不只是更改其部分)。
- @lee和@delnan:当然,不能修改常量对象。但是,如果您接受可以有常量对象,那么可以为其他对象(即可以修改的对象)编写swap(a, b),即使是在python中。您也不能为其他语言中的常量对象编写swap(a, b)。(print(x); x = 1是错误的。这个错误只有在编译时才被发现。)对于间接寻址,我所说的是变量的模拟,正如在经典语言中所理解的那样(比如使用单个元素列表作为变量)。
- @Pepr-如何在python中编写(void)函数swap(a, b),以交换参数的值?
- @德尔南:我已经更新了我所说的间接性的意思。坦率地说,在李,我使用C++太长了。我以为参考资料后来被添加到了C中。我没有检查。无论如何,你也可以在C++中引用常量对象。那么通过引用进行的赋值甚至不会编译。在Python中,简单对象是常量,大多数容器不是常量。
- 交换示例不交换其参数,它只交换两个列表参数的第一个元素。在C语言中,你可以把它写成void Swap(ref T first, ref T second) { T temp = first; first = second; second = first; }。不能在python中编写这样的函数(可能使用一些低级的直接内存操作除外),因为不能通过引用传递参数。
- @李:当然,不能在python中交换字符串,因为不能修改字符串。不能交换常量对象。无论如何,您可以交换列表。
- @Pepr您不能交换列表。事实上,除了引用(Python意义,而不是C++感)之外,你不能交换任何东西。可以交换存储在序列/映射中特定索引/键、本地或全局变量以及对象属性中的引用,但不能交换对象本身。您的示例这样做是为了解决这样一个事实:您不能引用存储位置,只能引用对象(然后通过约定将该对象的项或属性视为存储位置)。但传递引用意味着您确实具有对存储位置的引用。
- 换句话说,您只演示了如何在Python中模拟按引用传递。没有人说过你不能模仿它。但您必须模拟它,因为Python不通过引用传递。
- @德尔南:请参阅更新后的答案。最后一个swap交换列表的内容。我们认为清单和清单的内容是什么?python中的列表实际上是元素引用的集合(技术上)。但从抽象的角度来看,列表包含值。总之,这些值是自动取消引用的结果。列表是一个动态引用数组。swap交换列表的内容。正确的?然后,第一个列表包含第二个列表包含的内容,反之亦然。
- @关于print(x); x = 1示例不是编译时错误:这完全是由于python是动态类型的,不需要任何编译时错误。变量未初始化;C实现执行静态分析以确保其具有值,而Python实现则动态检查该值。既不允许读取未初始化的变量,也不允许两个功能分别执行一个按引用传递变量的功能,该变量通过不允许被调用函数读取未初始化的变量来接受该变量。
- 但是python不知道真正包含字符串的容器。实际上,python文档没有提到引用。它与变量的技术实现有关。正如您可以讨论的那样,python不支持整型变量,从技术上讲,您是对的。
- @不,尽管每个列表现在都有另一个以前的项目,但它们仍然是不同的。id允许我们歧视他们。这不仅仅是理论上的或深奥的;通过ID识别对象的过程非常频繁。除此之外,传递引用还包括交换变量,我们都知道这是不可能发生的。只是交换对象内容不属于按引用传递,因为我们已经建立了任何间接寻址允许这样做。
- 让我们在聊天中继续讨论
- @德尔南:你说得对。我已经更新了第一句话。
python总是传递值:
1 2 3 4 5 6 7 8 9 10
| def is_python_pass_by_value(foo):
foo[0] = 'More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.'
foo = ['Python is not pass-by-reference.']
quux = ['Yes, of course, Python *is* pass-by-value!']
is_python_pass_by_value(quux)
print(quux[0])
# More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value. |
C默认为传递值,但如果在方法声明站点和调用站点都使用了ref关键字,则也支持传递引用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| struct MutableCell
{
public string value;
}
class Program
{
static void IsCSharpPassByValue (string[] foo, MutableCell bar, ref string baz, ref MutableCell qux )
{
foo [0] ="More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.";
foo = new string[] {"C# is not pass-by-reference." };
bar .value ="For value types, it is *not* call-by-sharing.";
bar = new MutableCell { value ="And also not pass-by-reference." };
baz ="It also supports pass-by-reference if explicitly requested.";
qux = new MutableCell { value ="Pass-by-reference is supported for value types as well." };
}
static void Main (string[] args )
{
var quux = new string[] {"Yes, of course, C# *is* pass-by-value!" };
var corge = new MutableCell { value ="For value types it is pure pass-by-value." };
var grault ="This string will vanish because of pass-by-reference.";
var garply = new MutableCell { value ="This string will vanish because of pass-by-reference." };
IsCSharpPassByValue (quux, corge, ref grault, ref garply );
Console .WriteLine(quux [0]);
// More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.
Console .WriteLine(corge .value);
// For value types it is pure pass-by-value.
Console .WriteLine(grault );
// It also supports pass-by-reference if explicitly requested.
Console .WriteLine(garply .value);
// Pass-by-reference is supported for value types as well.
}
} |
如您所见,如果没有使用ref关键字的显式注释,c的行为与python完全相同。值类型是传递值,其中传递的值是对象本身,引用类型是传递值,其中传递的值是指向对象的指针(也称为对象共享调用)。
python不支持可变的值类型(可能是件好事),因此不可能观察传递值逐值和传递指针逐值之间的区别,因此您可以将所有内容都视为传递指针逐值,并大大简化您的心理模型。
C还支持out参数。它们也是通过引用传递的,但可以保证被调用方永远不会从中读取,只会写入,因此调用方不需要预先初始化它们。当您在Python中使用元组时,它们用于模拟多个返回值。它们有点像单向引用。
- 但请记住,即使python使用pass-by值,如果输入参数是复杂的类型(如dict),也可以对其进行更改。在函数内部更改这些参数将更改原始值。
- @格雷戈:嗯,那只是可变状态。如果你改变一个易变的物体,它就会改变,这并不奇怪。python不是一种纯粹的功能性语言,C也不是。
- @j&246;rgwmittag:python从不使用pass-by值。恰恰相反。
- @Pepr:我添加了一个代码片段,演示了Python中的传递值。注意:我只在python 2.7中测试过它,但是在python 3中看到不同的结果会让我非常惊讶。
- @j&246;rgwmittag是c中的(类)对象,默认情况下不通过引用传递?
- @max:no,只有当被调用方法的作者和调用代码的作者通过在参数列表和参数列表中同时添加ref关键字明确请求时,c才通过引用。
- @麦克斯:不,根本没有传递对象。引用和值类型值是通过传递的——默认情况下,它们是通过值传递的,但也可以通过引用传递。参见pobox.com/~skeet/csharp/parameters.html
- @这不是证据。python中的字符串是不可变的。这样,在函数体中分配foo意味着foo名称绑定到另一个引用。不能更改通过引用传递的原始对象。另一个对象由内部分配创建。但是,您无法访问新值。尝试传递一个列表,然后在内部修改它。
- @max:引用类型(类的实例)和值类型(结构的实例)之间的区别不是如何传递,而是传递什么:对于引用类型,传递的值是指向对象的指针(副本),对于值类型,传递的值是对象本身(副本)。这对于传递值和传递引用是完全正交的:可以按值传递引用类型(默认值)或按引用传递值类型。
- @易变性或不变性在这里完全不相关。如果我分配给被调用者内部的变量,而这个变量改变了同一个变量(不是对象!)在调用者内部,这就是按引用传递,否则就不是。实际上,这几乎就是按引用传递的定义。
- 在Python中,值是通过引用传递的。python赋值不替换被引用的对象,它创建一个新的引用并存储后者。python值(对象)本身就存在于一个堆中。
- @抱歉,你错了。引用值的定义很简单——它是一个值,当使用时,它允许您使用自动取消引用来访问目标对象。引用值与指针值大致相同。只有在中才能指向有效的对象(这样它就可以自动取消引用而不会出错)。引用的实现称为4字节地址。反论点是你永远不能改变不变的物体。
- @佩珀:当然,你可以自由地用你喜欢的任何方式来定义"通过引用"这个词。然而,如果你选择用一个定义明确的术语,它的定义不同于(在某种意义上完全相反)在整个计算史上每个程序员、老师、教科书、每门课程、教授、每堂课、地球上每个计算机科学家都认同的定义,那么你必须做好准备。如果人们对你的陈述感到困惑。
- @我不喜欢使用以下参数。我喜欢真相。但我已经毕业于计算机科学,并获得了计算机科学博士学位。我在CS大学的课程中教学生(作为助理)编程8年。我做了12年的程序员。我能理解你的困惑,但在这种情况下必须澄清。然后让我看看你所说的"通过引用"是什么意思。
- @典型的例子是一个函数交换(a,b),它交换a和b的值。注意,没有通过可变对象进行间接寻址。
- python总是使用pass-by引用。
- @嗯,我明白你的意思。但python不是经典的编译语言。python变量的含义与编译语言略有不同。我会在这个意义上更新我的答案。
- @佩普:没有编译语言这样的东西。编译和解释是编译程序和解释器的特征,而不是语言。语言是一套抽象的数学规则。未编译或解释语言。语言就是这样。每种语言都可以由编译器和解释器实现。大多数都有。当前所有实际使用的Python实现(cpython、ironpython、jython、pypy)都有编译器。
- @J&246;rgwmittag:编译/解释与问题无关。不管怎样,我们有一种方法可以用经典编译语言和(比如)用Python来考虑变量。这就是它与众不同的原因。Python在更高的抽象级别上处理变量。我同意,从技术上讲,很难说出编辑的真正含义(更哲学的问题)。无论如何,它不会改变调用python函数时作为参数传递的事实。
- @J&246;rgwmittag:您的示例只证明您没有使用传递的引用修改传递的对象。原因是不能以任何方式修改通过引用传递的字符串对象。不能修改它。分配不会修改传递的对象。它将foo的名称用于另一个目的。在任务之前和之后,尝试使用orig = foo并调用id(foo)。然后在分配前后呼叫id(orig)。id()返回目标对象的标识(基本上是其地址)。
- @是的,这就是传递值和传递引用之间的定义差异。
没什么不同
1 2 3
| def func(a,b):
a[0]=5 #Python
b=30 |
1 2 3 4
| public int func( ref int a,out int b,int d)
{
a++;b--; //C#
} |
1 2 3 4
| x=[10]
y=20
func(20,30) #python
print x,y #Outputs x=[5],y=20 Note:I have used mutable objects.Not possible with int. |
1 2 3
| int x=10,y=20;
func(ref x,out y,18); //C#
Console.Writeline("x={0} y={1}",x,y);//Outputs x=11,y=19 |