ตัวชี้ใน Python?


124

ฉันรู้ว่า Python ไม่มีพอยน์เตอร์ แต่มีวิธีให้ผลตอบแทนนี้2แทนได้ไหม

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

?


นี่คือตัวอย่าง: ฉันต้องการform.data['field']และform.field.valueมีค่าเท่ากันเสมอ มันไม่จำเป็นอย่างสมบูรณ์ แต่ฉันคิดว่ามันคงจะดี


ตัวอย่างเช่นใน PHP ฉันสามารถทำได้:

<?php

class Form {
    public $data = [];
    public $fields;

    function __construct($fields) {
        $this->fields = $fields;
        foreach($this->fields as &$field) {
            $this->data[$field['id']] = &$field['value'];
        }
    }
}

$f = new Form([
    [
        'id' => 'fname',
        'value' => 'George'
    ],
    [
        'id' => 'lname',
        'value' => 'Lucas'
    ]
]);

echo $f->data['fname'], $f->fields[0]['value']; # George George
$f->data['fname'] = 'Ralph';
echo $f->data['fname'], $f->fields[0]['value']; # Ralph Ralph

เอาท์พุท:

GeorgeGeorgeRalphRalph

ideone


หรือแบบนี้ใน C ++ (ฉันคิดว่าถูกต้อง แต่ C ++ ของฉันเป็นสนิม):

#include <iostream>
using namespace std;

int main() {
    int* a;
    int* b = a;
    *a = 1;
    cout << *a << endl << *b << endl; # 1 1

    return 0;
}

28
บางทีฉันอาจถามคำถามที่คล้ายกับ S.Lott's (แต่มีประสิทธิผลมากกว่า): คุณช่วยแสดงรหัสจริงที่คุณต้องการทำให้เราดูได้ไหม บางทีอาจเป็นภาษาอื่นที่ตรงกับรสนิยมของคุณมากกว่า? เป็นไปได้ว่าปัญหาที่คุณกำลังพยายามแก้ไขจะนำไปสู่โซลูชัน Pythonic ที่มากขึ้นและการมุ่งเน้นไปที่ "ฉันต้องการคำชี้" นั้นบดบังคำตอบที่แท้จริง
Ned Batchelder

8
ไม่ต้องใช้จินตนาการมากนัก ฉันคิดได้หลายเหตุผลที่อยากทำสิ่งนี้ ไม่ใช่แค่การทำในภาษาที่ไม่มีตัวชี้เช่น Python คุณต้องห่อไว้ในภาชนะที่ไม่คงที่เหมือนในคำตอบของ Matt
Glenn Maynard

14
คุณจะไม่เขียนฟังก์ชัน swap ใน Python a, b = b, aคุณต้องการเขียน
dan04

3
-1: โครงสร้างในคำถามคือ (ก) ไร้เหตุผลและ (b) ดูเหมือนจะไม่มีใครสามารถให้ตัวอย่างที่สมเหตุสมผลได้ การบอกว่ามี "หลายสิบเหตุผล" ไม่เหมือนกับการโพสต์ตัวอย่าง
ล็อต

1
@ มาร์ค: นอกจากนี้ฉันไม่ "ไม่เห็นด้วย" ฉันสับสน. ฉันกำลังถามคำถามกำลังมองหาวิธีที่จะเข้าใจว่านี่คืออะไรและทำไมคุณถึงคิดว่ามันสำคัญมาก
S.Lott

คำตอบ:


48

ฉันต้องการform.data['field']และ form.field.valueมีค่าเท่ากันเสมอ

สิ่งนี้เป็นไปได้เนื่องจากมีการตกแต่งชื่อและการจัดทำดัชนีกล่าวคือโครงสร้างที่แตกต่างอย่างสิ้นเชิงจากชื่อเปล่า aและbที่คุณกำลังถามถึงและสำหรับคำขอของคุณนั้นเป็นไปไม่ได้เลย ทำไมขอสิ่งที่เป็นไปไม่ได้และแตกต่างจากสิ่งที่คุณต้องการจริงๆ!

บางทีคุณอาจไม่รู้ว่าชื่อเปล่าและชื่อที่ตกแต่งแตกต่างกันอย่างมาก เมื่อคุณอ้างถึง barename aคุณจะได้รับวัตถุที่aถูกผูกไว้ครั้งสุดท้ายในขอบเขตนี้ (หรือข้อยกเว้นหากไม่ได้ถูกผูกไว้ในขอบเขตนี้) - นี่เป็นลักษณะที่ลึกซึ้งและเป็นพื้นฐานของ Python ที่สามารถทำได้ อาจไม่ถูกล้มล้าง เมื่อคุณอ้างถึงชื่อที่ได้รับการตกแต่งx.yคุณกำลังขอให้ออบเจ็กต์ (วัตถุxอ้างถึง) โปรดyระบุ "แอตทริบิวต์" - และเพื่อตอบสนองต่อคำขอนั้นวัตถุสามารถทำการคำนวณโดยพลการโดยสิ้นเชิง (และการจัดทำดัชนีก็ค่อนข้างคล้ายกัน: นอกจากนี้ยังอนุญาตให้ทำการคำนวณตามอำเภอใจเพื่อตอบสนอง)

ตอนนี้ตัวอย่าง "desiderata ที่แท้จริง" ของคุณเป็นเรื่องลึกลับเพราะในแต่ละกรณีมีการจัดทำดัชนีหรือการรับแอตทริบิวต์สองระดับเข้ามาเกี่ยวข้องดังนั้นความละเอียดอ่อนที่คุณต้องการจึงสามารถนำมาใช้ได้หลายวิธี มีคุณลักษณะอะไรอีกบ้างที่form.fieldสมมติว่ามีนอกจากvalueนี้ หากไม่มีการ.valueคำนวณเพิ่มเติมความเป็นไปได้จะรวมถึง:

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

และ

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

การปรากฏตัวของคำ.valueแนะนำในการเลือกรูปแบบแรกรวมถึงกระดาษห่อหุ้มที่ไร้ประโยชน์:

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

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

หากการมอบหมายดังกล่าวform.field.value = 23ควรจะตั้งค่ารายการในform.dataกระดาษห่อนั้นจะต้องซับซ้อนมากขึ้นอย่างแน่นอนและไม่ใช่สิ่งที่ไร้ประโยชน์ทั้งหมด:

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)

ตัวอย่างหลังเป็นประมาณใกล้ที่สุดเท่าที่จะได้รับในหลามกับความรู้สึกของ "ชี้" ในขณะที่คุณดูเหมือนจะต้องการ - แต่มันเป็นสิ่งสำคัญที่จะเข้าใจว่ารายละเอียดปลีกย่อยเช่นสามารถทำงานที่เคยเพียง แต่มีการจัดทำดัชนีและ / หรือชื่อการตกแต่ง , ไม่เคยด้วยนามแฝงตามที่คุณถามในตอนแรก!


23
ฉันถามแบบที่ฉันทำเพราะฉันหวังว่าจะได้วิธีแก้ปัญหาทั่วไปที่ใช้ได้กับทุกอย่างจนถึง "ชื่อเปล่า" และตอนนั้นฉันไม่ได้คำนึงถึงกรณีใดเป็นพิเศษ - เพียงแค่ฉันเจอปัญหานี้ ปัญหาเกี่ยวกับการทำซ้ำก่อนหน้านี้ของโครงการนี้และฉันไม่ต้องการพบอีกครั้ง อย่างไรก็ตามนี่คือคำตอบที่ยอดเยี่ยม! อย่างไรก็ตามความไม่เชื่อนั้นได้รับการชื่นชมน้อยกว่า
mpen

2
@ มาร์คดูความคิดเห็นใน Q ของคุณแล้วคุณจะเห็นว่า "ความไม่เชื่อ" เป็นปฏิกิริยาที่เกิดขึ้นอย่างกว้างขวางและ A ที่ได้รับการโหวตสูงสุดจะบอกคุณว่า "อย่าทำเลยเอาชนะมันไป" ตามด้วยคำว่า ไปว่า "มันเป็นอย่างไร" แม้ว่าคุณจะไม่ "ชื่นชม" คนที่มีความรู้เกี่ยวกับ Python ที่ตอบสนองด้วยความประหลาดใจกับข้อกำหนดดั้งเดิมของคุณ แต่พวกเขาก็ค่อนข้างน่าประหลาดใจในตัวเอง ;-)
Alex Martelli

30
ใช่ แต่ดูเหมือนคุณจะประหลาดใจที่ฉันขาดความรู้ Python ... ราวกับว่าเราเป็นสัตว์ต่างถิ่น: P
mpen

51

ไม่มีทางที่คุณจะเปลี่ยนเฉพาะบรรทัดนั้นได้ คุณทำได้:

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

ที่สร้างรายการกำหนดการอ้างอิงให้กับ a จากนั้น b ก็ใช้การอ้างอิงเพื่อตั้งค่าองค์ประกอบแรกเป็น 2 จากนั้นเข้าถึงโดยใช้ตัวแปรอ้างอิง b


17
นั่นคือความไม่ลงรอยกันที่ฉันเกลียดเกี่ยวกับ Python และภาษาไดนามิกเหล่านี้ (ใช่แล้วมันไม่ "ไม่สอดคล้องกัน" จริงๆเพราะคุณกำลังเปลี่ยนแอตทริบิวต์แทนที่จะเป็นข้อมูลอ้างอิง แต่ฉันก็ยังไม่ชอบ)
mpen

10
@ มาร์ค: แน่นอน ฉันรู้จักคนนับไม่ถ้วน (ดีไม่กี่คน) ที่ใช้เวลาหลายชั่วโมงในการค้นหา "จุดบกพร่อง" ในโค้ดของพวกเขาแล้วพบว่ามันเกิดจากรายการไม่ได้รับการคัดลอกยาก
houbysoft

14
ไม่มีความไม่ลงรอยกัน และไม่มีอะไรเกี่ยวข้องกับการอภิปรายแบบไดนามิก v. หากเป็นการอ้างอิงสองรายการไปยัง Java ArrayList เดียวกันก็จะเหมือนกันไวยากรณ์ของโมดูโล หากคุณใช้วัตถุที่ไม่เปลี่ยนรูป (เช่นสิ่งทอ) คุณไม่ต้องกังวลว่าวัตถุจะถูกเปลี่ยนแปลงผ่านการอ้างอิงอื่น
Matthew Flaschen

ฉันใช้สิ่งนี้หลายครั้งโดยทั่วไปเพื่อแก้ไขปัญหาการขาด "nonlocal" ของ 2.x ไม่ใช่สิ่งที่สวยที่สุดที่ต้องทำ แต่ก็ใช้ได้ดีในการหยิก
Glenn Maynard

1
สิ่งนี้ไม่สอดคล้องกันเลยเนื่องจากวัตถุที่คุณกำหนดaและbเป็นรายการไม่ใช่ค่าในรายการ ตัวแปรไม่ได้เปลี่ยนการกำหนดวัตถุเป็นวัตถุเดียวกัน หากมีการเปลี่ยนแปลงเช่นในกรณีของการเปลี่ยนจำนวนเต็ม (ซึ่งแต่ละรายการเป็นวัตถุที่แตกต่างกัน) aตอนนี้จะถูกกำหนดให้กับวัตถุอื่นและไม่มีอะไรแจ้งbให้ปฏิบัติตาม ที่นี่aไม่ได้ถูกกำหนดใหม่ แต่ค่าภายในออบเจ็กต์ที่กำหนดจะเปลี่ยนไป เนื่องจากbยังคงถูกผูกไว้กับวัตถุนั้นจึงจะสะท้อนให้เห็นถึงวัตถุนั้นและการเปลี่ยนแปลงของค่าภายในนั้น
arkigos

34

ไม่ใช่บั๊ก แต่เป็นคุณสมบัติ :-)

เมื่อคุณดูตัวดำเนินการ '=' ใน Python อย่าคิดในแง่ของการกำหนด คุณไม่ได้กำหนดสิ่งต่างๆคุณผูกมัดพวกเขา = เป็นตัวดำเนินการที่มีผลผูกพัน

ดังนั้นในรหัสของคุณคุณกำลังตั้งชื่อให้กับค่า 1: a. จากนั้นคุณจะให้ค่าในชื่อ "a": b จากนั้นคุณจะผูกค่า 2 กับชื่อ 'a' ค่าที่ผูกไว้กับ b จะไม่เปลี่ยนแปลงในการดำเนินการนี้

มาจากภาษาคล้าย C อาจทำให้สับสนได้ แต่เมื่อคุณคุ้นเคยกับมันแล้วคุณจะพบว่ามันช่วยให้คุณอ่านและหาเหตุผลเกี่ยวกับรหัสของคุณได้ชัดเจนยิ่งขึ้น: ค่าที่มีชื่อ 'b' จะไม่เปลี่ยนแปลงเว้นแต่คุณ เปลี่ยนแปลงอย่างชัดเจน และถ้าคุณ 'นำเข้าสิ่งนี้' คุณจะพบว่า Zen of Python ระบุว่า Explicit นั้นดีกว่าโดยนัย

โปรดทราบว่าภาษาที่ใช้งานได้เช่น Haskell ก็ใช้กระบวนทัศน์นี้ด้วยซึ่งคุ้มค่ามากในแง่ของความทนทาน


39
คุณรู้ไหมฉันอ่านคำตอบแบบนี้หลายสิบครั้งแล้วและไม่เคยเข้าใจเลย ลักษณะการทำงานของa = 1; b = a; a = 2;Python, C และ Java เหมือนกันทุกประการ: b คือ 1 เหตุใดจึงเน้นที่ "= ไม่ใช่การกำหนดจึงมีผลผูกพัน"
Ned Batchelder

4
คุณมอบหมายสิ่งต่างๆ นั่นเป็นเหตุผลว่าทำไมจึงเรียกว่าคำสั่งที่ได้รับมอบหมาย ความแตกต่างที่คุณระบุไม่สมเหตุสมผล และสิ่งนี้ไม่เกี่ยวข้องกับคอมไพล์ v. ตีความหรือสแตติก v. ไดนามิก Java เป็นภาษาคอมไพล์ที่มีการตรวจสอบประเภทแบบคงที่และไม่มีพอยน์เตอร์ด้วย
Matthew Flaschen

3
แล้ว C ++ ล่ะ? "b" อาจอ้างอิงถึง "a" การทำความเข้าใจความแตกต่างระหว่างการมอบหมายและการผูกเป็นสิ่งสำคัญในการทำความเข้าใจอย่างถ่องแท้ว่าเหตุใด Mark จึงไม่สามารถทำในสิ่งที่ต้องการทำได้และวิธีการออกแบบภาษาเช่น Python ตามแนวคิด (ไม่จำเป็นต้องใช้ในการนำไปใช้งาน) "a = 1" ไม่ได้เขียนทับบล็อกของหน่วยความจำที่ชื่อ "a" ด้วย 1; มันกำหนดชื่อ "a" ให้กับวัตถุที่มีอยู่แล้ว "1" ซึ่งโดยพื้นฐานแล้วแตกต่างจากสิ่งที่เกิดขึ้นใน C นั่นเป็นเหตุผลที่ตัวชี้เป็นแนวคิดไม่สามารถมีอยู่ใน Python ได้ - พวกมันจะค้างในครั้งต่อไป ตัวแปรดั้งเดิมคือ "กำหนดมากกว่า"
Glenn Maynard

1
@dw: ฉันชอบวิธีคิดแบบนี้! "ผูกพัน" เป็นคำพูดที่ดี @Ned: ผลลัพธ์เหมือนกันใช่ แต่ใน C ค่าของ "1" จะถูกคัดลอกไปยังทั้งคู่aและbใน Python ทั้งคู่อ้างถึง"1" เดียวกัน (ฉันคิดว่า) ดังนั้นหากคุณสามารถเปลี่ยนค่าของ 1 (เช่นเดียวกับวัตถุ) มันก็จะแตกต่างกัน ซึ่งนำไปสู่ปัญหาการชกมวย / การแกะกล่องแปลก ๆ ฉันได้ยิน
mpen

4
ความแตกต่างระหว่าง Python และ C ไม่ใช่ความหมายของ "การมอบหมาย" มันคือความหมายของ "ตัวแปร"
dan04

28

ใช่ มีวิธีใช้ตัวแปรเป็นตัวชี้ใน python!

ฉันขอโทษที่ต้องบอกว่าหลายคำตอบผิดบางส่วน ตามหลักการแล้วทุก ๆ การกำหนด (=) ที่เท่ากันจะใช้ที่อยู่หน่วยความจำร่วมกัน (ตรวจสอบฟังก์ชัน id (obj)) แต่ในทางปฏิบัติมันไม่เป็นเช่นนั้น มีตัวแปรที่พฤติกรรมเท่ากัน ("=") ทำงานในคำสุดท้ายเป็นสำเนาของพื้นที่หน่วยความจำส่วนใหญ่อยู่ในวัตถุธรรมดา (เช่น "int" object) และอื่น ๆ ที่ไม่มี (เช่น "list", "dict" objects) .

นี่คือตัวอย่างของการกำหนดตัวชี้

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

นี่คือตัวอย่างของการกำหนดสำเนา

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

การกำหนดตัวชี้เป็นเครื่องมือที่มีประโยชน์มากสำหรับการใช้นามแฝงโดยไม่ต้องเสียหน่วยความจำเพิ่มเติมในบางสถานการณ์สำหรับการใช้โค้ดที่สะดวกสบาย

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

แต่เราต้องระวังการใช้งานนี้เพื่อป้องกันความผิดพลาดของโค้ด

เพื่อสรุปโดยค่าเริ่มต้นตัวแปรบางตัวเป็นชื่อเปล่า (ออบเจ็กต์ธรรมดาเช่น int, float, str, ... จะจดจำได้อย่างไร? เพียงแค่ลองทดสอบกับพวกเขา ใน IDE ที่มีพาเนล explorer ตัวแปรมักจะเป็นที่อยู่หน่วยความจำ ("@axbbbbbb ... ") ในนิยามของวัตถุกลไกตัวชี้

ฉันขอแนะนำให้ตรวจสอบในหัวข้อนี้ มีหลายคนที่รู้มากขึ้นเกี่ยวกับหัวข้อนี้อย่างแน่นอน (ดูโมดูล "ctypes") ฉันหวังว่ามันจะเป็นประโยชน์ เพลิดเพลินไปกับการใช้ประโยชน์จากวัตถุ! ขอแสดงความนับถือJosé Crespo


ดังนั้นฉันต้องใช้พจนานุกรมเพื่อส่งผ่านตัวแปรโดยการอ้างอิงถึงฟังก์ชันและฉันไม่สามารถส่งผ่านตัวแปรโดยการอ้างอิงโดยใช้ int หรือสตริงได้?
แซม

13
>> id(1)
1923344848  # identity of the location in memory where 1 is stored
>> id(1)
1923344848  # always the same
>> a = 1
>> b = a  # or equivalently b = 1, because 1 is immutable
>> id(a)
1923344848
>> id(b)  # equal to id(a)
1923344848

ที่คุณสามารถดูaและbเป็นเพียงสองชื่อแตกต่างกันที่การอ้างอิงถึงเดียวกันวัตถุไม่เปลี่ยนรูป 1(int) หากต่อมาคุณเขียนa = 2คุณโอนชื่อaให้เป็นที่แตกต่างกันของวัตถุ (int) 2แต่bยังคงอ้างอิงไปที่1:

>> id(2)
1923344880
>> a = 2
>> id(a)
1923344880  # equal to id(2)
>> b
1           # b hasn't changed
>> id(b)
1923344848  # equal to id(1)

อะไรจะเกิดขึ้นถ้าคุณมีวัตถุที่ไม่แน่นอนแทนเช่นรายการ[1]?

>> id([1])
328817608
>> id([1])
328664968  # different from the previous id, because each time a new list is created
>> a = [1]
>> id(a)
328817800
>> id(a)
328817800 # now same as before
>> b = a
>> id(b)
328817800  # same as id(a)

อีกครั้งเรากำลังอ้างถึงวัตถุเดียวกัน (รายการ) [1]ด้วยชื่อที่แตกต่างกันสองชื่อaและb. แต่ตอนนี้เราสามารถเปลี่ยนแปลงรายการนี้ในขณะที่มันยังคงเป็นวัตถุเดียวกันและa, bทั้งสองจะยังคงอ้างอิงกับมัน

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

1
ขอบคุณสำหรับการแนะนำฟังก์ชัน id สิ่งนี้ช่วยแก้ข้อสงสัยของฉันได้มาก
haudoing

12

จากมุมมองเดียวทุกอย่างเป็นตัวชี้ใน Python ตัวอย่างของคุณทำงานเหมือนกับรหัส C ++

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

(สิ่งที่ใกล้เคียงกว่าจะใช้บางประเภทshared_ptr<Object>แทนint*)

นี่คือตัวอย่าง: ฉันต้องการให้ form.data ['field'] และ form.field.value มีค่าเท่ากันเสมอ มันไม่จำเป็นอย่างสมบูรณ์ แต่ฉันคิดว่ามันคงจะดี

คุณสามารถทำได้โดยการโอเวอร์โหลด__getitem__ในform.dataคลาสของ


form.dataไม่ใช่ชั้นเรียน จำเป็นหรือไม่ที่จะต้องทำให้เป็นหนึ่งเดียวหรือฉันสามารถลบล้างได้ทันที? (เป็นเพียง python dict) นอกจากนี้ข้อมูลจะต้องมีการอ้างอิงกลับไปformเพื่อเข้าถึงฟิลด์ ... ซึ่งทำให้การใช้งานนี้น่าเกลียด
mpen

1

นี่คือตัวชี้หลาม (ต่างจาก c / c ++)

>>> a = lambda : print('Hello')
>>> a
<function <lambda> at 0x0000018D192B9DC0>
>>> id(a) == int(0x0000018D192B9DC0)
True
>>> from ctypes import cast, py_object
>>> cast(id(a), py_object).value == cast(int(0x0000018D192B9DC0), py_object).value
True
>>> cast(id(a), py_object).value
<function <lambda> at 0x0000018D192B9DC0>
>>> cast(id(a), py_object).value()
Hello

0

ฉันเขียนคลาสง่ายๆต่อไปนี้เป็นวิธีเลียนแบบตัวชี้ใน python อย่างมีประสิทธิภาพ:

class Parameter:
    """Syntactic sugar for getter/setter pair
    Usage:

    p = Parameter(getter, setter)

    Set parameter value:
    p(value)
    p.val = value
    p.set(value)

    Retrieve parameter value:
    p()
    p.val
    p.get()
    """
    def __init__(self, getter, setter):
        """Create parameter

        Required positional parameters:
        getter: called with no arguments, retrieves the parameter value.
        setter: called with value, sets the parameter.
        """
        self._get = getter
        self._set = setter

    def __call__(self, val=None):
        if val is not None:
            self._set(val)
        return self._get()

    def get(self):
        return self._get()

    def set(self, val):
        self._set(val)

    @property
    def val(self):
        return self._get()

    @val.setter
    def val(self, val):
        self._set(val)

นี่คือตัวอย่างการใช้งาน (จากหน้าสมุดบันทึก jupyter):

l1 = list(range(10))
def l1_5_getter(lst=l1, number=5):
    return lst[number]

def l1_5_setter(val, lst=l1, number=5):
    lst[number] = val

[
    l1_5_getter(),
    l1_5_setter(12),
    l1,
    l1_5_getter()
]

Out = [5, None, [0, 1, 2, 3, 4, 12, 6, 7, 8, 9], 12]

p = Parameter(l1_5_getter, l1_5_setter)

print([
    p(),
    p.get(),
    p.val,
    p(13),
    p(),
    p.set(14),
    p.get()
])
p.val = 15
print(p.val, l1)

[12, 12, 12, 13, 13, None, 14]
15 [0, 1, 2, 3, 4, 15, 6, 7, 8, 9]

แน่นอนว่ามันเป็นเรื่องง่ายที่จะทำให้สิ่งนี้ใช้งานได้กับรายการ dict หรือคุณสมบัติของวัตถุ ยังมีวิธีทำตามที่ OP ขอโดยใช้ globals ():

def setter(val, dict=globals(), key='a'):
    dict[key] = val

def getter(dict=globals(), key='a'):
    return dict[key]

pa = Parameter(getter, setter)
pa(2)
print(a)
pa(3)
print(a)

สิ่งนี้จะพิมพ์ออกมา 2 ตามด้วย 3

การยุ่งกับเนมสเปซทั่วโลกด้วยวิธีนี้เป็นความคิดที่ไม่ชัดเจน แต่ก็แสดงให้เห็นว่าเป็นไปได้ (หากมองไม่เห็น) ที่จะทำตามที่ OP ขอ

ตัวอย่างคือแน่นอนว่าไม่มีจุดหมาย แต่ฉันพบว่าคลาสนี้มีประโยชน์ในแอปพลิเคชันที่ฉันพัฒนาขึ้น: แบบจำลองทางคณิตศาสตร์ที่พฤติกรรมถูกควบคุมโดยพารามิเตอร์ทางคณิตศาสตร์ที่ผู้ใช้ตั้งค่าได้จำนวนมากประเภทต่างๆ (ซึ่งขึ้นอยู่กับอาร์กิวเมนต์บรรทัดคำสั่งจึงไม่เป็นที่รู้จัก ในเวลารวบรวม) และเมื่อการเข้าถึงบางสิ่งบางอย่างถูกห่อหุ้มไว้ในออบเจ็กต์พารามิเตอร์แล้ววัตถุเหล่านั้นทั้งหมดจะสามารถจัดการได้อย่างสม่ำเสมอ

แม้ว่ามันจะดูไม่เหมือนตัวชี้ C หรือ C ++ มากนัก แต่นี่เป็นการแก้ปัญหาที่ฉันจะแก้ไขด้วยตัวชี้หากฉันเขียนด้วย C ++


0

รหัสต่อไปนี้จำลองพฤติกรรมของพอยน์เตอร์ใน C:

from collections import deque # more efficient than list for appending things
pointer_storage = deque()
pointer_address = 0

class new:    
    def __init__(self):
        global pointer_storage    
        global pointer_address

        self.address = pointer_address
        self.val = None        
        pointer_storage.append(self)
        pointer_address += 1


def get_pointer(address):
    return pointer_storage[address]

def get_address(p):
    return p.address

null = new() # create a null pointer, whose address is 0    

ตัวอย่างการใช้งานมีดังนี้

p = new()
p.val = 'hello'
q = new()
q.val = p
r = new()
r.val = 33

p = get_pointer(3)
print(p.val, flush = True)
p.val = 43
print(get_pointer(3).val, flush = True)

แต่ตอนนี้ถึงเวลาให้รหัสที่เป็นมืออาชีพมากขึ้นรวมถึงตัวเลือกในการลบพอยน์เตอร์ที่ฉันเพิ่งพบในห้องสมุดส่วนตัวของฉัน:

# C pointer emulation:

from collections import deque # more efficient than list for appending things
from sortedcontainers import SortedList #perform add and discard in log(n) times


class new:      
    # C pointer emulation:
    # use as : p = new()
    #          p.val             
    #          p.val = something
    #          p.address
    #          get_address(p) 
    #          del_pointer(p) 
    #          null (a null pointer)

    __pointer_storage__ = SortedList(key = lambda p: p.address)
    __to_delete_pointers__ = deque()
    __pointer_address__ = 0 

    def __init__(self):      

        self.val = None 

        if new.__to_delete_pointers__:
            p = new.__to_delete_pointers__.pop()
            self.address = p.address
            new.__pointer_storage__.discard(p) # performed in log(n) time thanks to sortedcontainers
            new.__pointer_storage__.add(self)  # idem

        else:
            self.address = new.__pointer_address__
            new.__pointer_storage__.add(self)
            new.__pointer_address__ += 1


def get_pointer(address):
    return new.__pointer_storage__[address]


def get_address(p):
    return p.address


def del_pointer(p):
    new.__to_delete_pointers__.append(p)

null = new() # create a null pointer, whose address is 0

ฉันคิดว่าคุณได้กำหนดค่าในรูปแบบแปลก ๆ
เปิด

คุณหมายถึง: "วิธีที่ฉลาด" หรือ "วิธีที่ไม่ฉลาด"?
MikeTeX

เอ่อ ... ฉันกำลังดิ้นรนเพื่อดูกรณีการใช้งานที่ถูกต้องสำหรับพื้นที่เก็บข้อมูลส่วนกลางที่จัดทำดัชนีด้วยตัวเลขสุ่ม
mpen

ตัวอย่างการใช้งาน: ฉันเป็นวิศวกรอัลกอริทึมและต้องทำงานร่วมกับโปรแกรมเมอร์ ฉันทำงานกับ Python และทำงานกับ C ++ บางครั้งพวกเขาขอให้ฉันเขียนอัลกอริทึมสำหรับพวกเขาและฉันเขียนให้ใกล้เคียงกับ C ++ มากที่สุดเพื่อความสะดวก พอยน์เตอร์มีประโยชน์เช่นสำหรับต้นไม้ไบนารีเป็นต้น
MikeTeX

หมายเหตุ: หากพื้นที่จัดเก็บข้อมูลทั่วโลกรบกวนคุณสามารถรวมไว้เป็นตัวแปรส่วนกลางที่ระดับของคลาสซึ่งน่าจะสวยงามกว่า
MikeTeX
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.