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