파이썬의 포인터?


124

파이썬에 포인터가 없다는 것을 알고 있지만 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

이데온


또는 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와 비슷한 질문을 할 수있을 것입니다 (하지만 더 생산적임) :이 작업을 수행하려는 실제 코드를 보여줄 수 있습니까? 아마도 당신의 취향에 더 맞는 다른 언어로도? 해결하려는 문제가보다 Python적인 솔루션에 적합 할 가능성이 높으며 "I want pointers"에 초점을 맞추는 것이 실제 답을 모호하게 만드는 것입니다.
Ned Batchelder 2010-06-24

8
많은 상상력이 필요하지 않습니다. 저는 이것을하고 싶은 수십 가지 이유를 생각할 수 있습니다. 파이썬과 같이 포인터가없는 언어에서 수행되는 방식이 아닙니다. Matt의 답변에서와 같이 변하지 않는 용기에 포장해야합니다.
Glenn Maynard

14
파이썬으로 스왑 함수를 작성하지 않을 것입니다. 당신은 a, b = b, a.
dan04 2010-06-24

3
-1 : 질문의 구조는 (a) 무의미하고 (b) 아무도 그것을 합리적으로 만드는 예를 제공 할 수없는 것 같습니다. "수십 가지 이유"가 있다고 말하는 것은 예제를 게시하는 것과 다릅니다.
S.Lott

1
@Mark : 또한, 나는 "반대"하지 않습니다. 혼란 스러워요. 저는 이것이 무엇인지, 왜 그렇게 중요하다고 생각하는지 이해하는 방법을 찾고있는 질문입니다.
S.Lott 2010-06-25

답변:


48

내가 원하는 form.data['field']form.field.value항상 동일한 값이

이것은 데코 레이팅 된 이름과 인덱싱을 포함하기 때문에 가능합니다. 즉, 베어 네임 과 는 완전히 다른 구조이며, 요청하는 것은 완전히 불가능합니다. 실제로 원하는 것과 불가능 하고 완전히 다른 것을 요청하는 이유는 무엇 입니까?! ab

이름과 장식 된 이름이 얼마나 크게 다른지 모를 수도 있습니다. 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
나는 "barenames"에 이르기까지 모든 일에 적용 할 수있는 일반적인 해결책을 원했기 때문에 내가했던 방식으로 물었다. 당시에는 특별한 경우를 염두에 두지 않았기 때문이다. 이 프로젝트의 이전 반복에 문제가 있으며 다시 시작하고 싶지 않았습니다. 어쨌든 이것은 좋은 대답입니다! 그러나 그 신기함은 덜 평가됩니다.
mpen 2010-06-24

2
@Mark, Q에 대한 댓글을 보면 "불확실성"이 널리 퍼진 반응이라는 것을 알 수 있습니다. 가장 많이 득표 한 A는 "하지 마세요, 극복하세요"라고 말한 다음 "그냥있는 그대로"가됩니다. 원래 사양에 대해 놀랍게 반응하는 Python 지식이있는 사람들을 "감사"하지 못할 수도 있지만, 그 자체로 상당히 놀랍습니다 .-) .
Alex Martelli

30
우리가 외국 종을하는 경우로 예,하지만 당신은 ... 파이썬 지식의 나의 부족에 놀라게 보인다 : P
mpen

51

그 라인 만 바꾸는 방법은 없습니다. 넌 할 수있어:

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

그러면 목록이 생성되고 참조가 a에 할당 된 다음 b도 a 참조를 사용하여 첫 번째 요소를 2로 설정 한 다음 b 참조 변수를 사용하여 액세스합니다.


17
이것이 바로 제가 파이썬과 이러한 동적 언어에 대해 싫어하는 불일치입니다. (그래, 당신은 참조보다는 속성을 변경하고 있기 때문에 정말 "일관성"아니다 그러나 나는 아직도 좋아하지 않는다)
mpen

10
@ 마크 : 참으로. 나는 코드에서 "버그"를 검색하고 하드 카피되지 않은 목록으로 인해 발생했음을 발견하기 위해 아마도 몇 시간을 소비 한 수많은 사람들을 알고 있습니다.
houbysoft

14
불일치가 없습니다. 그리고 그것은 거대한 정적 대 동적 논쟁과는 아무런 관련이 없습니다. 동일한 Java ArrayList에 대한 두 개의 참조 인 경우 동일한 모듈로 구문이됩니다. 불변 객체 (튜플과 같은)를 사용하는 경우 다른 참조를 통해 변경되는 객체에 대해 걱정할 필요가 없습니다.
Matthew Flaschen

나는 이것을 여러 번 사용했는데, 가장 일반적으로 2.x의 "nonlocal"부족을 해결하기 위해 사용했습니다. 할 수있는 가장 예쁘지는 않지만 핀치에 잘 작동합니다.
Glenn Maynard

1
당신이 할당하는 개체 때문에 전혀 일치하지 않습니다 ab목록, 목록에없는 값입니다. 변수는 할당을 변경하지 않고 객체는 동일한 객체입니다. 정수 (각각 다른 개체)를 변경하는 경우와 같이 변경된 경우 a이제 다른 개체에 할당 b되며 따라야하는 메시지가 없습니다 . 여기서는 a재 할당되는 것이 아니라 할당 된 객체 내부의 값이 변경됩니다. b여전히 해당 객체에 바인딩되어 있으므로 해당 객체와 그 안의 값에 대한 변경 사항을 반영합니다.
arkigos

34

버그가 아니라 기능입니다 :-)

파이썬에서 '='연산자를 볼 때 할당 측면에서 생각하지 마십시오. 당신은 물건을 할당하지 않고 묶습니다. =는 바인딩 연산자입니다.

따라서 코드에서 값 1에 이름을 지정합니다. 그런 다음 'a'의 값에 이름을 지정합니다. b. 그런 다음 값 2를 이름 'a'에 바인딩합니다. b에 바인딩 된 값은이 작업에서 변경되지 않습니다.

C와 유사한 언어에서 왔기 때문에 혼란 스러울 수 있지만 익숙해지면 코드를 더 명확하게 읽고 추론하는 데 도움이됩니다. 이름이 'b'인 값은 변경되지 않습니다. 명시 적으로 변경하십시오. 그리고 'import this'를 수행하면 Zen of Python이 Explicit가 암시 적보다 낫다는 것을 알 수 있습니다.

또한 Haskell과 같은 기능적 언어도 견고성 측면에서 큰 가치를 지닌이 패러다임을 사용합니다.


39
알다시피, 저는 이런 답을 수십 번 읽었지만 이해 한 적이 없습니다. 의 동작은 a = 1; b = a; a = 2;Python, C 및 Java에서 정확히 동일합니다. b는 1입니다. "=가 할당이 아니라 바인딩"에 초점을 맞춘 이유는 무엇입니까?
네드 BATCHELDER

4
당신은 일을 할당합니다. 그것이 할당 문 이라고 불리는 이유 입니다. 당신이 말하는 구별이 말이되지 않습니다. 그리고 이것은 컴파일 된 v. 해석 또는 정적 v. 동적과 관련이 없습니다. Java는 정적 유형 검사 기능이있는 컴파일 된 언어이며 포인터도 없습니다.
Matthew Flaschen

3
C ++는 어떻습니까? "b"는 "a"에 대한 참조 일 수 있습니다. 할당과 바인딩의 차이를 이해하는 것은 Mark가 자신이하고 싶은 일을 할 수없는 이유와 Python과 같은 언어가 어떻게 설계되었는지 완전히 이해하는 데 중요합니다. 개념적으로 (반드시 구현할 필요는 없음) "a = 1"은 "a"라는 이름의 메모리 블록을 1로 덮어 쓰지 않습니다. 이미 존재하는 객체 "1"에 이름 "a"를 할당하는데, 이것은 C에서 일어나는 것과 근본적으로 다릅니다. 이것이 바로 포인터가 파이썬에 존재할 수없는 이유입니다. 원래 변수는 "할당"되었습니다.
Glenn Maynard

1
@dw : 나는 그것에 대해 생각하는 이런 방식을 좋아한다! "바인딩"은 좋은 단어입니다. @Ned : 출력은 예, 동일하지만, C에서 "1"의 값은 모두 복사 ab파이썬 반면, 그들은 둘 다 참조 받는 사람 같은 "1"(나는 생각한다). 따라서 1의 값을 변경할 수 있다면 (객체와 마찬가지로) 다를 수 있습니다. 이상한 권투 / 개봉 문제로 이어진다 고 들었습니다.
mpen

4
Python과 C의 차이점은 "할당"이 의미하는 것이 아닙니다. "변수"가 의미하는 바입니다.
dan04

28

예! 파이썬에서 변수를 포인터로 사용하는 방법이 있습니다!

많은 답변이 부분적으로 틀렸다고 말해서 유감입니다. 원칙적으로 모든 equal (=) 할당은 메모리 주소를 공유하지만 (id (obj) 함수 확인) 실제로는 그렇지 않습니다. equal ( "=") 동작이 마지막 용어에서 메모리 공간의 복사본으로 작동하는 변수가 있으며, 대부분은 단순 객체 (예 : "int"객체)에서 작동하고 다른 변수 (예 : "list", "dict"객체)에서 작동합니다. .

다음은 포인터 할당의 예입니다.

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, ...과 같은 간단한 객체)이고 일부는 그들 사이에 할당 될 때 포인터입니다 (예 : dict1 = dict2). 그들을 인식하는 방법? 그들과 함께이 실험을 시도해보세요. 변수 탐색기 패널이있는 IDE에서는 일반적으로 포인터 메커니즘 개체의 정의에서 메모리 주소 ( "@axbbbbbb ...")로 나타납니다.

주제에 대해 조사 할 것을 제안합니다. 이 주제에 대해 확실히 아는 많은 사람들이 있습니다. ( "ctypes"모듈 참조). 도움이 되었기를 바랍니다. 물건을 잘 사용하십시오! 감사합니다. José Crespo


그래서 함수에 대한 참조로 변수를 전달하기 위해 사전을 사용해야하고 int 또는 문자열을 사용하여 참조로 변수를 전달할 수 없습니까?
Sam

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

당신이 볼 수 있듯이 ab 두 가지의 이름에 해당 참조입니다 같은 불변의 객체 (INT) 1. 나중에 작성 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

한 관점에서 보면 모든 것이 파이썬의 포인터입니다. 귀하의 예제는 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수업이 아닙니다. 하나로 만들어야합니까, 아니면 즉시 재정의 할 수 있습니까? (그것은 단지 파이썬 딕셔너리 일뿐입니다) 또한, 데이터는 form필드에 접근 하기 위해 다시 참조를 가져야합니다 .
mpen 2010-06-24

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

파이썬에서 포인터를 에뮬레이트하는 방법으로 다음과 같은 간단한 클래스를 효과적으로 작성했습니다.

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 항목이나 개체의 속성에 대해이 작업을 수행하는 것도 쉽습니다. globals ()를 사용하여 OP가 요청한 작업을 수행하는 방법도 있습니다.

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가 요청한 것을 수행하는 것이 가능하다는 것을 보여줍니다 (권장하지 않는 경우).

물론 그 예는 상당히 무의미합니다. 하지만이 클래스는 내가 개발 한 응용 프로그램에서 유용하다는 것을 발견했습니다. 동작이 다양한 유형의 사용자 설정 가능한 수학적 매개 변수에 의해 제어되는 수학적 모델 (명령 줄 인수에 의존하기 때문에 알려지지 않음) 컴파일 타임에). 그리고 어떤 것에 대한 액세스가 Parameter 객체에 캡슐화되면 이러한 모든 객체를 균일 한 방식으로 조작 할 수 있습니다.

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

나는 당신이 이상한 방식으로 값을 상자에 넣었다고 생각합니다.
mpen 19.08.26

의미 : "영리한 방법"또는 "영리하지 않은 방법"?
MikeTeX

Uhhh ... 난수로 색인 된 글로벌 스토리지의 유효한 사용 사례를 찾기 위해 고군분투하고 있습니다.
mpen

사용 예 : 저는 알고리즘 엔지니어이고 프로그래머와 함께 작업해야합니다. 저는 Python으로 작업하고 C ++로 작업합니다. 때때로 그들은 그들에게 알고리즘을 작성 해달라고 요청하고 나는 그들의 편의를 위해 가능한 한 C ++에 가깝게 작성합니다. 포인터는 예를 들어 바이너리 트리 등에 유용합니다.
MikeTeX

참고 : 전역 저장소가 문제가되는 경우 클래스 자체 수준에서 전역 변수로 포함 할 수 있습니다. 이는 아마도 더 우아 할 것입니다.
MikeTeX
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.