web-dev-qa-db-ja.com

Pythonのポインター?

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;
}
104
mpen

form.data['field']form.field.valueに常に同じ値を持たせたい

これは、装飾名とインデックス付けを伴うため、実行可能です。つまり、barenamesとは異なる構成要素barenamesaおよびbは、要求しているものであり、要求に応じて完全に不可能です。なぜ不可能なものを求めるandあなたが実際に(可能性のある)ものと完全に異なるwant?!

たぶん、あなたは、ベアネームと装飾名がどれほど劇的に異なるかを理解していないでしょう。 barename aを参照すると、aがこのスコープで最後にバインドされたオブジェクト(または、このスコープでバインドされていない場合は例外)を正確に取得しています。これは、Python破壊できない可能性があること。 decoratedname x.yを参照するときは、オブジェクト(xが参照するオブジェクト)に「y属性」を指定するように求めています。 、オブジェクトは完全に任意の計算を実行できます(また、インデックス付けは非常に似ています:また、応答で任意の計算を実行できます)。

さて、あなたの「実際の要求」の例は、それぞれ2つのレベルのインデックス付けまたは属性取得が関係しているので、不思議です。したがって、あなたが切望する微妙さは多くの方法で導入できます。たとえば、value以外に、form.fieldには他にどのような属性がありますか?それ以上の.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])

assignmentsそのようなform.field.value = 23form.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)

後者の例は、Pythonの「ポインタ」の感覚にほぼ似ていますが、そのような微妙さはインデックス付けでしか機能しないことを理解することが重要ですおよび/または装飾された名前neverあなたが最初に尋ねたように裸の名前で!

43
Alex Martelli

その行だけを変更する方法はありません。できるよ:

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

それはリストを作成し、aに参照を割り当て、次にbも、a参照を使用して最初の要素を2に設定し、b参照変数を使用してアクセスします。

45

それはバグではなく、機能です:-)

Pythonで「=」演算子を見るときは、割り当てに関して考えないでください。物を割り当てるのではなく、バインドします。 =はバインディング演算子です。

したがって、コードでは、値1に名前を付けています。次に、「a」の値に名前を付けます:b。次に、値2を名前「a」にバインドします。この操作では、bにバインドされた値は変更されません。

Cに似た言語から来るので混乱する可能性がありますが、慣れると、コードをより明確に読んで推論するのに役立つことがわかります。「b」という名前の値は、明示的に変更します。また、「これをインポート」を行うと、PythonのZenが明示的であることが暗黙的であるよりも優れていることがわかります。

Haskellなどの関数型言語もこのパラダイムを使用しており、堅牢性の点で大きな価値があることに注意してください。

33
David Harks

はい! Pythonのポインターとして変数を使用する方法があります!

回答の多くが部分的に間違っていたと言って申し訳ありません。原則として、すべての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では、Explorerパネルは通常、ポインターメカニズムオブジェクトの定義のメモリアドレス( "@axbbbbbb ...")のように見えます。

トピックで調査することをお勧めします。このトピックについてもっとよく知っている人はたくさんいます。 (「ctypes」モジュールを参照)。お役に立てば幸いです。オブジェクトの有効活用をお楽しみください!よろしく、ホセ・クレスポ

24

ある観点から見ると、everythingは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をオーバーロードします。

11
dan04
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

ご覧のとおり、abは、同じオブジェクト1を参照する単なる名前です。後でa = 2を作成する場合、名前aを別のオブジェクト2に再割り当てしますが、bではなく、1を引き続き参照します。

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

可変オブジェクトがあった場合はどうなりますか?

>> 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)

ここで、名前aおよびbによって同じリストオブジェクトを参照しています。このリストは変更できますが、同じオブジェクトのままであり、abは両方とも引き続き参照します

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

私は、効果的に、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]

もちろん、オブジェクトの辞書項目または属性に対してこの作業を行うことも簡単です。 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
Leon Avery

次のコードは、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
0
MikeTeX