Ich bin ein erfahrener Programmierer, aber neu in Python, so dass ich nicht weiß, ob das, was ich hier sehe, Verhalten ist spezifisch für Python, dass ich nicht bewusst bin. Ich programmiere normalerweise in C++ und C#, also habe ich Codebeispiele in beiden Sprachen eingefügt, um die unterschiedlichen Ergebnisse zu demonstrieren.Rekursionsverhalten in Python vs C# - Seltsame Ergebnisse
Ich mache eine rekursive Suchfunktion, die eine Zustandsstruktur verwendet, um Ergebnisse aufzuzeichnen. In C# verhält es sich wie erwartet, aber in Python scheint es (soweit ich das beurteilen kann) nicht zu unterscheiden zwischen dem Zustand, der im aktuellen/letzten rekursiven Aufruf existiert. Mir ist bewusst, dass Python Objekte per Referenz verwaltet (wie in C#) - ich mache eine Tiefenaufnahme des Objekts, bevor ich es weiter in die Suchfunktion übergebe.
Zuerst auf die C# -Version und Ergebnisse:
class SimState
{
public List<string> stateDescriptions = new List<string>();
public SimState Clone()
{
return new SimState() {
stateDescriptions = new List<string>(this.stateDescriptions)
};
}
}
class Program
{
public void search_recursive(SimState state,int move,int recurseDepth)
{
if (recurseDepth > 2)
return;
state.stateDescriptions.Add(move.ToString());
Console.WriteLine(String.Format("{0}: {1}",
recurseDepth,
String.Join(",", state.stateDescriptions)));
for(int n = 0;n < 2;n++)
{
var newState = state.Clone();
this.search_recursive(newState, n + 1, recurseDepth + 1);
}
}
static void Main(string[] args)
{
var initialState = new SimState();
new Program().search_recursive(initialState, 0, 0);
Console.ReadKey();
}
}
Und die Ergebnisse: Das ist, was ich erwarten würde passieren. Es sucht alle Kombinationen bis zu einem Maximum. Tiefe 2:
0: 0
1: 0,1
2: 0,1,1
2: 0,1,2
1: 0,2
2: 0,2,1
2: 0,2,2
So weit so gut. Nun, was ich dachte, war ein äquivalentes Python-Programm:
import copy
class SimState:
stateDescriptions = []
def search_recursive(state: SimState, move, recurseDepth):
if(recurseDepth > 2):
return
state.stateDescriptions.append('{0}'.format(move))
print('{0}: '.format(recurseDepth) + ','.join(state.stateDescriptions))
for n in range(2):
newState = copy.deepcopy(state)
search_recursive(newState,n + 1,recurseDepth + 1)
initialState = SimState()
search_recursive(initialState,0,0)
Und die Ergebnisse:
0: 0
1: 0,1
2: 0,1,1
2: 0,1,1,2
1: 0,1,1,2,2
2: 0,1,1,2,2,1
2: 0,1,1,2,2,1,2
Es scheint, als ob Python nicht der Tatsache bewusst ist, dass ich in einem völlig neuen Zustand bin vorbei (über den neuen, tief kopierten 'newState'), und aktualisiert stattdessen den vorhandenen 'Zustand' von dem vorherigen rekursiven Aufruf. Ich kenne den Post auf SO ""Least Astonishment" and the Mutable Default Argument"), aber das scheint sich speziell auf Standardargumente zu beziehen, die hier nicht verwendet werden.
Ich verwende Python 3.6 in Visual Studio 2017.
ich etwas offensichtlich fehle? Danke
Sie haben mich gerade in die richtige Richtung gezeigt. Ich dachte, meine Definition von stateDescriptions [] in der SimState-Klasse entsprach der Deklaration einer Member-Variable, aber wie Sie bereits erwähnt haben, handelt es sich um ein statisches Klassenattribut. Ich ersetzte es durch eine __init__ Methode und das Zuweisen von self.stateDescriptions = [] und es funktioniert jetzt wie erwartet. Danke für die schnelle Antwort. Warum habe ich eine Klasse benutzt? Das war nur ein reduziertes Beispiel basierend auf dem eigentlichen Problem, das ich zu lösen versuche, wo der SimState weitaus komplexer ist. –
Macht Sinn. Wie auch immer, ich ging voran und fügte nur eine Antwort hinzu. Beachten Sie, dass Python eine ziemlich dynamische Sprache ist und dass das Objektmodell ein wenig anders ist als Ihre typische statisch typisierte OOP-Sprache. Zum Beispiel, selbst.stateDescriptions "prüft zuerst den Instanz-Namespace und wenn er ihn nicht findet, prüft er den Klassen-Namespace und alle Namespaces im MRO. Sie können jedoch * Klassenvariablen mit Instanzvariablen * überschreiben. Also, sagen Sie, Sie hatten eine Mischung aus * beiden * Ihrer ursprünglichen Definition und der modifizierten, dann erstellen Sie im Wesentlichen eine nutzlose Klassenvariable, die immer beschattet wird –