Reputation: 271
What are the main differences, if any, of Python's argument passing rules vs C#'s argument passing rules?
I'm very familiar with Python and only starting to learn C#. I was wondering if I could think of the rule set as to when an object is passed by reference or by value the same for C# as it is in Python, or if there are some key differences I need to keep in mind.
Upvotes: 4
Views: 5061
Reputation: 369556
Python is always pass-by-value:
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# is pass-by-value by default, but also supports pass-by-reference if both at the method declaration site and at the call site the ref
keyword is used:
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.
}
}
As you can see, without explicit annotation with the ref
keyword, C# behaves exactly like Python. Value types are pass-by-value where the value being passed is the object itself, Reference types are pass-by-value where the value being passed is a pointer to the object (also known as call-by-object-sharing).
Python does not support mutable value types (probably a good thing), so it is impossible to observe the distinction between pass-value-by-value and pass-pointer-by-value, so you can just treat everything as pass-pointer-by-value and greatly simplify your mental model.
C# also supports out
parameters. They are also pass-by-reference, but it is guaranteed that the callee will never read from them, only write, so the caller does not need to initialize them beforehand. They are used to simulate multiple return values, when you would use a tuple in Python. They are kind-of like one-way pass-by-reference.
Upvotes: 3
Reputation: 20792
Python always uses pass by reference values. There is no exception. Any variable assignment means assigning the reference value. No exception. Any variable is the name bound to the reference value. Always.
You can think about reference as about the address of the target object that is automatically dereferenced when used. This way it seems you work directly with the target object. But there always is a reference in between, one step more to jump to the target.
Updated -- here is a wanted example that proves passing by reference:
If the argument were passed by value, the outer lst
could not be modified. The green are the target objects (the black is the value stored inside, the red is the object type), the yellow is the memory with the reference value inside -- drawn as the arrow. The blue solid arrow is the reference value that was passed to the function (via the dashed blue arrow path). The uggly dark yellow is the internal dictionary. (It actually could be drawn also as a green elipse. The colour and the shape only says it is internal.)
Updated -- related to fgb's comment on passing by reference example swap(a, b)
and the delnan's comment on imposibility to write the swap
.
In compiled languages, variable is a memory space that is capable to capture the value of the type. In Python, variable is a name (captured internally as a string) bound to the reference variable that holds the reference value to the target object. The name of the variable is the key in the internal dictionary, the value part of that dictionary item stores the reference value to the target.
The purpose of the swap
in other languages is to swap the content of the passed variables, i.e. swap the content of the memory spaces. This can be done also for Python, but only for variables that can be modified--meaning the content of their memory space can be modified. This holds only for modifiable container types. A simple variable in that sense is always constant, even though its name can be reused for another purpose.
If the function should create some new object, the only way to get it outside is or via a container type argument, or via the Python return
command. However, the Python return
syntactically look as if it was able to pass outside more than one argument. Actually, the multiple values passed outside form a tuple, but the tuple can be syntactically assigned to more outer Python variables.
Update related to the simulation of variables as they are perceived in other languages. The memory space is simulated by a single-element lists -- i.e. one more level of indirection. Then the swap(a, b)
can be written as in other languages. The only strange thing is that we have to use the element of the list as the reference to the value of the simulated variable. The reason for neccessity to simulate the other-languate variables this way is that only containers (a subset of them) are the only objects in Python that can be modified:
>>> 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
Notice that the now the var1
and var2
simulate the look of "normal" variables in classic languages. The swap
changes their content, but the addresses remain the same.
For modifiable object--as the lists are for example--you can write exactly the same swap(a, b)
as in other languages:
>>> 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
Notice that the multiple assignment like a[:] = b[:]
must be used to express copying of the content of the lists.
Upvotes: 4
Reputation: 22473
The problem with calling Python a "pass-by-value" or "pass-by-reference" language and comparing to C, C#, etc. is that Python has a different conception of how data is referred to. Python does not fit easily into a conventional by-value or by-reference dichotomy, leading to confusion and the "It's call-by-value!" "No, it's call-by-reference, and I can prove it!" "NO, YOU MAROON, it's obviously call-by-value!" endless loop witnessed above.
The truth is, it's neither. Python uses call-by-sharing (aka call-by-object). Sometimes this appears to be a by-value strategy (e.g. when dealing with scalar values like int
, float
, and str
), and sometimes like a by-reference strategy (e.g. when dealing with structured values like list
, dict
, set
, and object
). David Goodger's Code like a Pythonista sums this up beautifully as "other languages have variables; Python has names." As a bonus, he provides clear graphics illustrating the difference.
Under the covers, call-by-sharing is implemented more like call-by-reference (as the mutate a float
example mentioned by Noctis Skytower demonstrates.) But if you think of it as call-by-reference, you will go off the tracks rapidly, because while references are the implementation, they're not the exposed semantics.
C#, in contrast, uses either call-by-value or call-by-reference--though one might argue that the out
option represents a tweak above-and-beyond pure call-by-reference as seen in C, Pascal, etc.
So Python and C# are very different indeed--at an architectural level, at any rate. In practice, the combination of by-value and by-reference would allow you to create programs that operate very similarly to call-by-sharing--albeit with a tricky little Devil living in the details and corner-cases.
If you're interested in understanding different languages' parameter passing strategies in a comparative context, Wikipedia's page on expression evaluation strategy is worth a read. While it's not exhaustive (there are many ways to skin this particular cat!), it ably covers a range of the most important ones, plus some interesting uncommon variations.
Upvotes: 3
Reputation: 376
Not so different
def func(a,b):
a[0]=5 #Python
b=30
public int func( ref int a,out int b,int d)
{
a++;b--; //C#
}
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.
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
Upvotes: 0
Reputation: 4992
C# passes parameters by value unless you specify that you want it differently. If the parameter type is a struct, its value is copied, otherwise the reference to the object is copied. The same goes for return values.
You can modify this behavior using the ref
or out
modifier, which must be specified both in the method declaration and in the method call. Both change the behavior for that parameter to pass-by-reference. That means you can no longer pass in more complex expressions. The difference between ref
and out
is that when passing a variable to a ref
parameter, it must have been initialized already, while a variable passed to an out
parameter doesn't have to be initialized. In the method, the out
parameter is treated as uninitialized variable and must be assigned a value before returning.
Upvotes: 7