Передача аргументов С# vs Python
Каковы основные различия, если таковые имеются, правил передачи аргументов Python против правил передачи аргументов С#?
Я очень хорошо знаком с Python и только начинаю изучать С#. Мне было интересно, могу ли я подумать о установленном правиле относительно того, когда объект передается по ссылке или по значению тот же для С#, что и в Python, или если есть некоторые ключевые различия, которые мне нужно иметь в виду.
Ответы
Ответ 1
С# передает параметры по значению, если вы не укажете, что вы хотите его по-разному. Если тип параметра является структурой, его значение копируется, в противном случае копируется ссылка на объект. То же самое касается возвращаемых значений.
Вы можете изменить это поведение с помощью модификатора ref
или out
, который должен быть указан как в объявлении метода, так и в вызове метода. Оба изменяют поведение для этого параметра для передачи по ссылке. Это означает, что вы больше не можете передавать более сложные выражения. Разница между ref
и out
заключается в том, что при передаче переменной в параметр ref
она должна быть инициализирована уже, а переменная, переданная в параметр out
, не должна быть инициализирована. В методе параметр out
обрабатывается как неинициализированная переменная и перед возвратом ему нужно присвоить значение.
Ответ 2
Python всегда использует pass по ссылочным значениям. Не исключение. Любое назначение переменной означает присвоение контрольного значения. Никаких исключений. Любая переменная - это имя, привязанное к эталонному значению. Всегда.
Вы можете думать о ссылке как о адресе целевого объекта, который автоматически разыменовывается при использовании. Таким образом, кажется, вы работаете напрямую с целевым объектом. Но всегда есть ссылка между ними, еще на один шаг, чтобы перейти к цели.
Обновлено - вот нужный пример, который доказывает прохождение по ссылке:
Если аргумент передан по значению, внешний lst
не может быть изменен. Зеленый - это целевые объекты (черный - это значение, хранящееся внутри, красный - тип объекта), желтый - это память с опорным значением внутри - рисуется как стрелка. Синяя сплошная стрелка - это контрольное значение, которое передается функции (через штрихованный синий путь стрелки). Желтый темно-желтый - это внутренний словарь. (На самом деле его можно было бы нарисовать также как зеленый elipse. Цвет и форма говорят только, что он является внутренним.)
Обновлен - связанный с комментарием fgb при прохождении по ссылочному примеру swap(a, b)
и delnan комментарий о невозможности написать swap
.
В скомпилированных языках переменная - это пространство памяти, способное фиксировать значение типа. В Python переменная - это имя (записанное внутри строки в виде строки), привязанное к ссылочной переменной, которая содержит ссылочное значение для целевого объекта. Имя переменной - это ключ во внутреннем словаре, часть значения этого словарного элемента сохраняет контрольное значение цели.
Цель swap
в других языках - обмен содержимым переданных переменных, т.е. замена содержимого пространств памяти. Это можно сделать и для Python, но только для переменных, которые могут быть изменены - это означает, что содержимое их пространства памяти может быть изменено. Это относится только к изменяемым типам контейнеров. Простая переменная в этом смысле всегда постоянна, хотя ее имя можно повторно использовать для другой цели.
Если функция должна создать какой-то новый объект, единственный способ получить ее снаружи - это либо через аргумент типа контейнера, либо через команду Python return
. Тем не менее, Python return
синтаксически выглядит так, как если бы он мог выходить за пределы нескольких аргументов. Фактически, несколько значений, прошедших вне формы, образуют кортеж, но кортеж может быть синтаксически назначен более внешним переменным Python.
Обновить, связанный с симуляцией переменных, поскольку они воспринимаются на других языках. Пространство памяти моделируется одноэлементными списками, т.е. Еще одним уровнем косвенности. Тогда swap(a, b)
может быть записано как на других языках. Странно, что мы должны использовать элемент списка как ссылку на значение моделируемой переменной. Причиной необходимости в симуляции переменных других переменных является то, что только контейнеры (их подмножество) являются единственными объектами в Python, которые могут быть изменены:
>>> 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)
, как на других языках:
>>> 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[:]
.
Ответ 3
Проблема с вызовом Python: "pass-by-value" или "pass-by-reference" язык и сравнение с C, С# и т.д. заключается в том, что Python имеет другую концепцию того, как передаются данные. Python не легко вписывается в обычную дихотомию по значению или по ссылке, приводящую к путанице и "Это вызов по значению!". "Нет, это вызов по ссылке, и я могу это доказать!" "НЕТ, ВАМ МАРКОН, это, очевидно, по цене!" бесконечный цикл, засвидетельствованный выше.
По правде говоря, это не так. Python использует call-by-sharing (aka call-by-object). Иногда это похоже на стратегию по значению (например, при работе со скалярными значениями, такими как int
, float
и str
), а иногда и как эталонная стратегия (например, при работе со структурированными значениями типа list
, dict
, set
и object
). Дэвид Гудгер Код, подобный Pythonista, прекрасно описывает это, поскольку "у других языков есть переменные, у Python есть имена". В качестве бонуса он обеспечивает четкую графику, иллюстрирующую разницу.
В рамках обложки совместное использование вызовов реализовано больше как call-by-reference (как мутировать пример float
упомянутый Noctis Skytower). Но если вы думаете об этом как о вызове, вы быстро удаляете треки, потому что, хотя ссылки - это реализация, они 'не является открытой семантикой.
С#, напротив, использует либо позывной, либо позывной - хотя можно утверждать, что out
представляет собой настройку выше и выше чистого вызова по ссылке, как показано на C, Pascal и т.д.
Итак, Python и С# очень разные - на архитектурном уровне, во всяком случае. На практике комбинация по значению и по ссылке позволит вам создавать программы, которые работают очень похоже на совместное использование звонков, хотя и с сложным маленьким дьяволом, живущим в деталях и в углах.
Если вы заинтересованы в понимании стратегий передачи параметров разных языков в сравнительном контексте,
Страница Википедии по стратегии оценки выражений стоит прочитать. Хотя он не является исчерпывающим (есть много способов убрать этот конкретный кот!), Он умело охватывает ряд наиболее важных, а также некоторые интересные необычные варианты.
Ответ 4
Python всегда имеет значение 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.
С# по умолчанию передается по значению, но также поддерживает передачу по ссылке, если на сайте объявления метода и на сайте вызова используется ключевое слово ref
:
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
, С# ведет себя точно так же, как Python. Типы значений являются передаваемыми по значению, где передаваемое значение является самим объектом, типы Reference являются передаваемыми по значению, где переданное значение является указателем на объект (также называемым совместным использованием "по одному" ).
Python не поддерживает изменяемые типы значений (вероятно, это хорошо), поэтому невозможно наблюдать различие между значениями pass-value-by-value и pass-pointer-by-value, поэтому вы можете просто рассматривать все как pass -оценка по значению и значительно упростить вашу ментальную модель.
С# также поддерживает параметры out
. Они также передаются по ссылке, но гарантируется, что вызываемый пользователь никогда не будет читать их, только писать, поэтому вызывающему абоненту не нужно их инициализировать заранее. Они используются для имитации нескольких возвращаемых значений, когда вы будете использовать кортеж в Python. Они вроде как односторонние передачи по ссылке.
Ответ 5
Не очень отличается
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