Max
Max

Reputation: 271

C# vs Python argument passing

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

Answers (5)

Jörg W Mittag
Jörg W Mittag

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

pepr
pepr

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:

Illustrated example of passing the argument

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

Jonathan Eunice
Jonathan Eunice

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

user1655481
user1655481

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

Wormbo
Wormbo

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

Related Questions