[PYTHON] Passer par valeur, passer par référence, passer par référence,

2017/3/1 postscript: J'ai écrit un article supplémentaire car l'explication de Python était un peu inexacte → [Un peu plus sur la référence ~ Utilisation de Python et Java comme exemple ~](http://qiita.com/ur_kinsk/items / 7055ec8b50622289eeb2)

C'est un sujet courant, mais j'ai décidé de résumer le comportement lors du passage de variables à des fonctions, comme le passage par valeur ou par référence.

Tout d'abord, à propos des types primitifs

Dans la plupart des langages, les ** types primitifs ** et d'autres se comportent différemment. Bien qu'il diffère légèrement selon la langue, le type primitif ressemble à ceci.

  1. Entier
  2. Nombre à virgule flottante
  3. Caractère
  4. Bourian
  5. Référence ou pointeur

D'autres tableaux, classes, structures, etc. sont ici appelés ** objets **.

Passer par valeur

Une nouvelle variable est créée qui copie la valeur lorsqu'elle est passée à la fonction, et qui devient un argument formel. La modification de l'argument formel dans la fonction ne change pas la variable appelante. Exemples de C ++, Java et Python.

//C++
#include <iostream>

void foo(int x){
	x = 42;
}

int main(){
	int x = 334;
	std::cout << x << "\n";
	foo(x);
	std::cout << x << "\n";
	return 0;
}
//Java
public class Val{
	public static void foo(int x){
		x = 42;
	}
	
	public static void main(String[] args){	
		int x = 334;
		System.out.println(x);
		foo(x);
		System.out.println(x);		
	}
}
#Python
def foo(x):
	x = 42
	
x = 334
print(x)
foo(x)
print(x)

Les résultats sont les suivants.

334
334

Les types primitifs sont passés par valeur dans la plupart des langages comme celui-ci. De plus, dans les langages où il existe une distinction entre les structures et les classes, les structures sont passées par valeur, comme C # et Swift, et les classes sont définies comme passant par référence ou par référence.

Passer par référence

Les arguments réels et formels passés à la fonction pointeront désormais vers le même emplacement en mémoire. Pour ainsi dire, c'est comme créer un «alias» pour la même chose. Si vous modifiez l'argument formel dans la fonction, il sera reflété dans l'appelant. Exemples C ++, C #, Swift.

//C++
#include <iostream>

class Box{
public:
	int value;
	
	Box(int value) : value(value){}
};

//Lors de la définition d'une fonction&Si vous ajoutez, il sera passé par référence
void foo(Box& box){
	box.value = 42;
}

void bar(Box& box){
	box = Box(42);	
}

int main(){
	Box box1(334);
	std::cout << "foo: \n";
	std::cout << box1.value << "\n";
	foo(box1);
	std::cout << box1.value << "\n";
	
	Box box2(334);
	std::cout << "bar: \n";
	std::cout << box2.value << "\n";
	bar(box2);
	std::cout << box2.value << "\n";
	return 0;	
}
//C#
using System;

class Ref{
	public class Box{
		public int value;
		
		public Box(int value){
			this.value = value;
		}	
	}
	
	//Si ref est ajouté lors de la définition et de l'appel d'une fonction, il sera passé par référence.
	public static void Foo(ref Box box){
		box.value = 42;	
	}

	public static void Bar(ref Box box){
		box = new Box(42);
	}
	
	public static void Main(string[] args){
		Box box1 = new Box(334);
		Console.WriteLine("foo: ");
		Console.WriteLine(box1.value);
		Foo(ref box1);	
		Console.WriteLine(box1.value);

		Box box2 = new Box(334);
		Console.WriteLine("bar: ");
		Console.WriteLine(box2.value);
		Bar(ref box2);
		Console.WriteLine(box2.value);
	}
}
//Swift
class Box{
	public var value:Int
	
	init(_ value:Int){
		self.value = value
	}
}

func foo(_ box:Box){
	box.value = 42
}

//Lors d'une nouvelle affectation à un argument formel inout au moment de la définition de la fonction, au moment de l'appel&Mettez
func bar(_ box:inout Box){
	box = Box(42)
}

var box1 = Box(334)
print("foo: ")
print(box1.value)
foo(box1)
print(box1.value)

var box2 = Box(334)
print("bar: ")
print(box2.value)
bar(&box2)
print(box2.value)

Le résultat ressemble à ceci.

foo: 
334
42
bar: 
334
42

Passer par référence

Livraison également partagée. La plupart des langages prennent cette forme lors du passage d'un objet. Ceci est parfois appelé passage par référence, mais c'est en fait un peu différent, comme indiqué ci-dessous. Exemples Java, C #, Python.

//Java
class Ptr{
	public static class Box{
		public int value;
		
		public Box(int value){
			this.value = value;
		}
	}
	
	public static void foo(Box box){
		box.value = 42;
	}
	
	public static void bar(Box box){
		box = new Box(42);
	}
	
	public static void main(String[] args){
		Box box1 = new Box(334);
		System.out.println("foo: ");
		System.out.println(box1.value);
		foo(box1);
		System.out.println(box1.value);
		
		Box box2 = new Box(334);
		System.out.println("bar: ");
		System.out.println(box2.value);
		bar(box2);
		System.out.println(box2.value);
	}
}
//C#
using System;

class Ptr{
	public class Box{
		public int value;
		
		public Box(int value){
			this.value = value;
		}	
	}
	
	//Sans ref, la classe sera passée par référence
	public static void Foo(Box box){
		box.value = 42;	
	}

	public static void Bar(Box box){
		box = new Box(42);
	}
	
	public static void Main(string[] args){
		Box box1 = new Box(334);
		Console.WriteLine("foo: ");
		Console.WriteLine(box1.value);
		Foo(box1);	
		Console.WriteLine(box1.value);

		Box box2 = new Box(334);
		Console.WriteLine("bar: ");
		Console.WriteLine(box2.value);
		Bar(box2);
		Console.WriteLine(box2.value);
	}
}
#Python
class Box:
	def __init__(self, value):
		self.value = value

def foo(box):
	box.value = 42
	
def bar(box):
	box = Box(42)
	
box1 = Box(334)
print("foo: ")
print(box1.value)
foo(box1)
print(box1.value)

box2 = Box(334)
print("bar: ")
print(box2.value)
bar(box2)
print(box2.value)

Le résultat est le suivant.

foo: 
334
42
bar: 
334
334

De cette manière, le comportement lorsqu'un nouvel objet est affecté à un argument formel est différent du passage par référence. En C ++, si vous passez un pointeur comme indiqué ci-dessous, le comportement sera le même que ci-dessus.

//C++
#include <iostream>

class Box{
public:
	int value;
	
	Box(int value) : value(value){}
};

void foo(Box *box){
	box->value = 42;
}

void bar(Box *box){
	box = new Box(42);
	delete box;
}

int main(){
	Box *box1 = new Box(334);
	std::cout << "foo: \n";
	std::cout << box1->value << "\n";
	foo(box1);
	std::cout << box1->value << "\n";
	
	Box *box2 = new Box(334);
	std::cout << "bar: \n";
	std::cout << box2->value << "\n";
	bar(box2);
	std::cout << box2->value << "\n";
	delete box1;
	delete box2;
	return 0;
}

~~ Pratiquement, vous devriez utiliser std :: shared_ptr au lieu du pointeur brut, mais cette fois, j'ose l'utiliser pour la comparaison. ~~ Il peut être déroutant de dire «passer par référence», mais en bref, «** référence » signifie ici « pointeur **». Autrement dit, en passant le pointeur par valeur. Dans cet esprit, le traitement effectué par bar est

・ En passant par référence

  1. Créez un argument formel box qui pointe vers le même objet que l'argument réel passé à bar
  2. Remplacez l'objet à l'emplacement indiqué par «box» par «Box (42)»
  3. box et l'appelantbox2 pointent vers le même emplacement, ce dernier change donc

・ En passant par référence

  1. Créez un argument formel box qui pointe vers le même objet que l'argument réel passé à bar
  2. Changez l'emplacement pointé par box par l'objet nouvellement créé Box (42)
  3. L'objet pointé par l'appelant «box2» reste le même

Il se trouve que. Il ressemble à ceci lorsqu'il est dessiné sur la figure.

ref_ptr.png

Si vous souhaitez reproduire le comportement du passage par référence dans un code utilisant des pointeurs,

void bar(Box *box){
	box = new Box(42);
	delete box;
}

La partie est

void bar(Box *box){
	*box = Box(42);
}

devenir. D'ailleurs, dans le code Objective-C, il devient plus clair que cet "objet est manipulé par un pointeur".

//Objective-C
#import <Foundation/Foundation.h>

@interface Box : NSObject
@property(nonatomic) int value;

- (id)initWithValue:(int)value;

@end

@implementation Box
- (id)initWithValue:(int)value{
	if(self = [super init]){
		_value = value;
	}
	return self;
}
@end

void foo(Box *box){
	box.value = 42;
}

void bar(Box *box){
	box = [[Box alloc] initWithValue:42];
}

int main(){
	Box *box1 = [[Box alloc] initWithValue:334];
	printf("foo:\n");
	printf("%d\n", box1.value);
	foo(box1);
	printf("%d\n", box1.value);

	Box *box2 = [[Box alloc] initWithValue:334];
	printf("bar:\n");
	printf("%d\n", box2.value);
	bar(box2);
	printf("%d\n", box2.value);
	return 0;
}

Résumé

・ Par valeur Une nouvelle variable est créée avec la valeur de l'argument réel copiée. C'est à ce moment que vous passez le type primitif. La modification de l'argument formel n'affecte pas l'appelant. Selon la langue, les structures peuvent également être passées par valeur.

・ Passer par référence Les arguments réels et formels pointeront désormais vers le même emplacement en mémoire. Si vous modifiez l'argument formel, il sera reflété dans l'appelant. De nombreuses langues ne prennent pas en charge ce formulaire.

-Passage par référence Passer un pointeur en C ou C ++. En donnant un emplacement en mémoire à un argument formel, vous pouvez utiliser le même objet que l'argument réel via cet emplacement. Similaire au passage par référence, mais faites attention au comportement lorsque la valeur de l'argument formel elle-même est réécrite. De nombreux langages prennent cette forme lors du passage d'objets.

Recommended Posts

Passer par valeur, passer par référence, passer par référence,
Trier par valeur de valeur de type dict
Tous les arguments Python sont passés par référence
Pandas: groupby () pour compléter la valeur par groupe