2010-06-24 8 views
72

Ich weiß, Python hat Zeiger nicht hat, aber gibt es eine Möglichkeit, diese Ausbeute 2 stattZeiger in Python?

>>> a = 1 
>>> b = a # modify this line somehow so that b "points to" a 
>>> a = 2 
>>> b 
1 

zu haben?


Hier ist ein Beispiel: Ich möchte form.data['field'] und form.field.value immer den gleichen Wert haben. Es ist nicht unbedingt notwendig, aber ich denke, es wäre nett.

+17

Vielleicht kann ich ähnlich eine Frage stellen zu S.Lott ist (aber produktiver): Sie können uns zeigen, ein echter Code, wo du das machen wolltest? Vielleicht sogar in einer anderen Sprache, die mehr nach Ihrem Geschmack ist? Es ist wahrscheinlich, dass das Problem, das Sie zu lösen versuchen, sich für eine pythonsche Lösung eignet, und das Fokussieren auf "Ich will Zeiger" verschleiert die wahre Antwort. –

+6

Es braucht nicht viel Fantasie; Ich kann mir Dutzende Gründe vorstellen, dies zu tun. Es ist einfach nicht so, wie es in zeigerlosen Sprachen wie Python gemacht wird; Sie müssen es in einen Container einpacken, der nicht invariant ist, wie in Matts Antwort. –

+0

@Ned: Aktualisiert Q. Ein anderes Beispiel wäre für eine Funktion, 'swap (a, b)'. Nicht, dass ich das jetzt brauche. – mpen

Antwort

34

Ich möchte form.data['field'] und form.field.value immer den gleichen Wert

Dies ist möglich, haben, weil sie ergänzten Namen und Indizierung beinhaltet - das heißt, komplett verschiedene Konstrukte von den Barnamesa und b, die Sie fragen, und für Ihre Anfrage ist absolut unmöglich. Warum fragen Sie nach etwas Unmöglichem und völlig anders als die (mögliche) Sache, die Sie eigentlich wollen?!

Vielleicht wissen Sie nicht, wie drastisch verschiedene Barnamen und dekorierte Namen sind. Wenn Sie auf einen Barnamen a verweisen, erhalten Sie genau das Objekt a, an das in diesem Bereich zuletzt gebunden wurde (oder eine Ausnahme, wenn es in diesem Bereich nicht gebunden war) - dies ist ein so tiefer und grundlegender Aspekt von Python Es kann unmöglich untergraben werden. Wenn Sie sich auf einen verzierten Namen x.y beziehen, bitten Sie ein Objekt (das Objekt x bezieht sich), bitte "das y Attribut" zu liefern - und als Antwort auf diese Anforderung kann das Objekt völlig willkürliche Berechnungen durchführen (und Indexierung ist ziemlich ähnlich: Es erlaubt auch, dass beliebige Berechnungen als Antwort ausgeführt werden).

Nun ist Ihr "tatsächliches Desiderata" -Beispiel mysteriös, da in jedem Fall zwei Ebenen der Indexierung oder des Attribut-Erhaltens involviert sind, so dass die Subtilität, die Sie sich wünschen, auf viele Arten eingeführt werden könnte. Welche anderen Attribute soll form.field haben, neben value? Ohne daß weiteren .value Berechnungen würden Möglichkeiten umfassen:

class Form(object): 
    ... 
    def __getattr__(self, name): 
     return self.data[name] 

und

class Form(object): 
    ... 
    @property 
    def data(self): 
     return self.__dict__ 

Die Anwesenheit von .value legt die erste Form Kommissionierung, sowie einen Art-of-nutzlosen wrapper:

class KouWrap(object): 
    def __init__(self, value): 
     self.value = value 

class Form(object): 
    ... 
    def __getattr__(self, name): 
     return KouWrap(self.data[name]) 

Wenn Zuweisungen wie form.field.value = 23 soll auch t gesetzt werden er Eintrag in form.data, dann muss der Wrapper komplexer geworden in der Tat, und nicht alles, was nutzlos:

class MciWrap(object): 
    def __init__(self, data, k): 
     self._data = data 
     self._k = k 
    @property 
    def value(self): 
     return self._data[self._k] 
    @value.setter 
    def value(self, v) 
     self._data[self._k] = v 

class Form(object): 
    ... 
    def __getattr__(self, name): 
     return MciWrap(self.data, name) 

Letzteres Beispiel etwa so nah wie es geht, in Python, das Gefühl der „Zeiger“, wie Sie scheinen zu wollen - aber es ist wichtig zu verstehen, dass solche Feinheiten können nur mit Indizierung und/oder dekoriert Namen, nie mit Barnamen arbeiten, wie Sie ursprünglich gefragt!

+10

Ich habe es so gefragt, wie ich es getan habe, weil ich gehofft hatte, eine allgemeine Lösung zu finden, die für alles bis auf "Barnames" funktionieren würde und ich hatte damals keinen bestimmten Fall im Sinn - nur dass ich darin gelandet war Dieses Problem mit der vorherigen Iteration dieses Projekts und ich wollte nicht noch einmal darauf stoßen. Wie auch immer, das ist eine großartige Antwort! Die Ungläubigkeit wird jedoch weniger geschätzt. – mpen

+2

@Mark, schauen Sie sich die Kommentare zu Ihrem Q an und Sie werden sehen, dass "Ungläubigkeit" eine weit verbreitete Reaktion ist - und das von Ihnen gewählte A sagt Ihnen "tu es nicht, komm vorbei", gefolgt von einem das heißt "es ist einfach wie es ist". Während Sie "Python-kenntnisreiche" Leute, die mit Erstaunen auf Ihre ursprünglichen Spezifikationen reagieren, nicht "schätzen" können, sind sie in sich selbst erstaunlich ;-). –

+14

Ja, aber Sie scheinen über meinen Mangel an Python-Wissen erstaunt zu sein ... als wären wir eine fremde Spezies: P – mpen

31

Es gibt keine Möglichkeit, nur diese Zeile zu ändern. Sie tun können:

a = [1] 
b = a 
a[0] = 2 
b[0] 

, die eine Liste erstellt, weist den Verweis auf ein, dann auch b, die eine Referenz verwendet das erste Element auf 2 zu setzen, dann die b Referenzgröße Zugriffe verwenden.

+7

Das ist genau die Art von Inkonsistenz, die ich an Python und diesen dynamischen Sprachen hasse. (Ja, ja, es ist nicht wirklich "inkonsistent", weil Sie ein Attribut anstelle der Referenz ändern, aber ich mag es immer noch nicht) – mpen

+3

@Mark: In der Tat. Ich kenne unzählige (gut, einige) Leute, die möglicherweise Stunden damit verbrachten, nach einem "Bug" in ihrem Code zu suchen und dann zu finden, dass er durch eine Liste verursacht wurde, die nicht kopiert wurde. – houbysoft

+10

Es gibt keine Inkonsistenz. Und es hat nichts mit der großen statischen v. Dynamischen Debatte zu tun. Wenn es sich um zwei Verweise auf dieselbe Java ArrayList handelt, wäre dies die gleiche Modulo-Syntax. Wenn Sie unveränderliche Objekte (wie Tupel) verwenden, müssen Sie sich keine Sorgen machen, dass das Objekt durch eine andere Referenz geändert wird. –

27

Es ist kein Fehler, es ist ein Feature :-)

Wenn Sie den Blick auf ‚=‘ Operator in Python, denken Sie nicht in Bezug auf die Zuordnung. Sie ordnen keine Dinge zu, Sie binden sie. = ist ein bindender Operator.

In Ihrem Code geben Sie also dem Wert 1 einen Namen: a. Dann geben Sie dem Wert in "a" einen Namen: b. Dann binden Sie den Wert 2 an den Namen 'a'. Der an b gebundene Wert ändert sich in dieser Operation nicht.

Aus C-ähnlichen Sprachen kann dies verwirrend sein, aber sobald Sie sich daran gewöhnt haben, finden Sie, dass es Ihnen hilft, Ihren Code klarer zu lesen und zu begründen: der Wert, der den Namen 'b' hat nicht ändern, wenn Sie es nicht explizit ändern. Und wenn Sie dies "importieren", werden Sie feststellen, dass das Zen von Python besagt, dass Explizit besser als implizit ist.

Beachten Sie auch, dass auch funktionale Sprachen wie Haskell dieses Paradigma verwenden, mit großem Wert in Bezug auf Robustheit.

+26

Wissen Sie, ich habe solche Antworten Dutzende Male gelesen, und ich habe es nie verstanden. Das Verhalten von a = 1; b = a; a = 2; 'ist genau das gleiche in Python, C und Java: b ist 1. Warum dieser Fokus auf" = ist keine Zuweisung, es ist verbindlich "? –

+2

Sie ordnen Dinge zu. Deshalb wird es als [Zuweisungsanweisung] (http://docs.python.org/reference/simple_stmts.html) bezeichnet. Die Unterscheidung, die Sie angeben, macht keinen Sinn. Und das hat nichts mit compiled v. Interpreted oder static v. Dynamic zu tun. Java ist eine kompilierte Sprache mit statischer Typprüfung, und es hat auch keine Zeiger. –

+3

Was ist mit C++? "b" könnte ein Verweis auf "a" sein. Das Verständnis des Unterschieds zwischen Zuweisung und Bindung ist entscheidend, um zu verstehen, warum Mark nicht das tun kann, was er gerne tun würde, und wie Sprachen wie Python entworfen werden. Konzeptionell (nicht notwendigerweise in der Implementierung) überschreibt "a = 1" den Speicherblock mit dem Namen "a" nicht mit 1; Es weist dem bereits existierenden Objekt "1" einen Namen "a" zu, der sich grundlegend von dem unterscheidet, was in C passiert. Deshalb können Zeiger als Konzept in Python nicht existieren - sie würden beim nächsten Mal alt werden ursprüngliche Variable wurde "zugewiesen". –

11

Von einem Standpunkt, alles ist ein Zeiger in Python. Ihr Beispiel funktioniert ähnlich wie der C++ - Code.

int* a = new int(1); 
int* b = a; 
a = new int(2); 
cout << *b << endl; // prints 1 

(A näher Äquivalent würde irgendeine Art von shared_ptr<Object> statt int* verwenden.)

Hier ist ein Beispiel: Ich möchte form.data [ 'Feld'] und form.field.value immer den gleichen Wert haben. Es ist nicht komplett notwendig, aber ich denke, es wäre schön.

Sie können dies tun, indem Sie __getitem__ in form.data Klasse überladen.

+0

'form.data' ist keine Klasse. Ist es notwendig, eins zu machen, oder kann ich es im laufenden Betrieb außer Kraft setzen? (Es ist nur ein Python-Diktat) Außerdem müssten die Daten einen Verweis auf 'form' haben, um auf die Felder zugreifen zu können ... was die Implementierung hässlich macht. – mpen

13

Ja! Es gibt eine Möglichkeit, eine Variable als Zeiger in Python zu verwenden!

Es tut mir leid zu sagen, dass viele der Antworten teilweise falsch waren. Im Prinzip teilt jede gleiche (=) Zuweisung die Speicheradresse (überprüfe die ID (obj) -Funktion), aber in der Praxis ist es nicht so. Es gibt Variablen, deren Gleichheitsverhalten ("=") im letzten Term als eine Kopie des Speicherraums funktioniert, meistens in einfachen Objekten (zB "int" -Objekt), und andere in denen nicht (zB "list", "dict" -Objekte) .Hier

ist ein Beispiel für Zeiger assignation

dict1 = {'first':'hello', 'second':'world'} 
dict2 = dict1 # pointer assignation mechanism 
dict2['first'] = 'bye' 
dict1 
>>> {'first':'bye', 'second':'world'} 

Hier ist ein Beispiel für Kopie assignation

a = 1 
b = a # copy of memory mechanism. up to here id(a) == id(b) 
b = 2 # new address generation. therefore without pointer behaviour 
a 
>>> 1 

Pointer assignation ist ein ziemlich nützliches Werkzeug für Aliasing ohne die Verschwendung von zusätzlichen Speicher in bestimmten Situationen zum Ausführen von komfortablem Code,

class cls_X(): 
    ... 
    def method_1(): 
     pd1 = self.obj_clsY.dict_vars_for_clsX['meth1'] # pointer dict 1: aliasing 
     pd1['var4'] = self.method2(pd1['var1'], pd1['var2'], pd1['var3']) 
    #enddef method_1 
    ... 
#endclass cls_X 

aber ein hav sich dieser Verwendung bewusst sein, um Code-Fehler zu vermeiden.

Abschließend, einige Variablen sind standardmäßig barenames (einfache Objekte wie int, float, str, ...), und einige sind Zeiger, wenn zwischen ihnen zugewiesen (z. B. dict1 = dict2). Wie erkenne ich sie? Versuchen Sie einfach dieses Experiment mit ihnen. In IDEs mit variablen Explorer-Panel erscheint normalerweise die Speicheradresse ("@axbbbbbb ...") in der Definition von Zeiger-Mechanismus-Objekten.

Ich schlage vor, untersuchen in dem Thema. Es gibt viele Leute, die sicher viel mehr über dieses Thema wissen. (Siehe "Ctypes" -Modul). Ich hoffe es ist hilfreich. Genießen Sie die gute Verwendung der Objekte! Grüße, José Crespo

2
id(1) 
1923344848 # identity of the location in my memory  
>> a = 1 
>> b = a # or equivalently b = 1, because 1 is immutable 
>> id(a) 
1923344848 
>> id(b) 
1923344848 

Wie Sie a und b sind nur Namen sehen können, die 1 auf das gleiche Objekt verweisen. Wenn Sie später a = 2 schreiben, neu zuordnen Sie den Namen a auf ein anderes Objekt 2, aber nicht die b, die 1 Referenzierung wird auch weiterhin:

>> id(2) 
1923344880 
>> a = 2 
>> id(a) 
1923344880 # same as id(2) 
>> id(b) 
1923344848 # same as id(1) 

Was passiert, wenn Sie ein veränderbares Objekt hatte?

>> id([1]) 
328817608 
>> id([1]) 
328664968 # different 
>> a = [1] 
>> id(a) 
328817800 
>> id(a) 
328817800 # same as before 
>> b = a # not equivalent to b = [1] 
>> id(b) 
328817800 # same as id(a) 

Nun Sie verweisen auf das gleiche Liste Objekt mit dem Namen a und b. Sie können diese Liste mutieren, aber es wird das gleiche Objekt bleiben, und a und b werden beide weiterhin darauf verweisen

>> a[0] = 2 
>> a 
[2] 
>> b 
[2] 
>> id(a) 
328817800 # same as before 
>> id(b) 
328817800 # same as before