Подтвердить что ты не робот

Указатели в 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;
}
4b9b3361

Ответ 1

Я хочу form.data['field'] и form.field.value, чтобы всегда иметь такое же значение

Это возможно, потому что оно включает декорированные имена и индексирование - т.е. полностью различные конструкции из barenames 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)

Последний пример примерно так же близок, как и в Python, к понятию "указатель", как вам кажется, - но важно понять, что такие тонкости могут когда-либо работать только с индексированием и/или декорированием имена, никогда с именами, которые вы изначально задавали!

Ответ 2

Вы не можете сделать это, изменяя только эту строку. Вы можете сделать:

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

Создает список, назначает ссылку на a, а затем b также использует ссылку для установки первого элемента в 2, затем обращается к использованию с помощью ссылочной переменной b.

Ответ 3

Это не ошибка, это особенность: -)

Когда вы смотрите на оператор '=' в Python, не думайте в терминах назначения. Вы не назначаете вещи, вы их связываете. = является оператором связывания.

Итак, в вашем коде вы даете значение 1 имя: a. Затем вы указываете значение в 'a': b. Затем вы привязываете значение 2 к имени "a". Значение, связанное с b, не изменяется в этой операции.

Исходя из C-подобных языков, это может сбивать с толку, но как только вы привыкнете к этому, вы обнаружите, что он помогает вам более четко читать и рассуждать о вашем коде: значение, которое имеет имя "b", не будет измените, если вы явно не измените его. И если вы выполните "импорт этого", вы обнаружите, что Zen of Python утверждает, что Explicit лучше, чем неявный.

Обратите также внимание на то, что функциональные языки, такие как Haskell, также используют эту парадигму с большой ценностью с точки зрения надежности.

Ответ 4

Да! есть способ использовать переменную как указатель в python!

Мне жаль, что многие ответы были частично неправильными. В принципе, каждая равная (=) передача разделяет адрес памяти (проверьте функцию id (obj)), но на практике это не так. Существуют переменные, поведение которых равно ( "=" ) работает в последнем члене как копия пространства памяти, главным образом в простых объектах (например, объект "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" ). Надеюсь, это полезно. Наслаждайтесь хорошим использованием объектов! С уважением, Хосе Креспо

Ответ 5

С одной точки зрения все является указателем на Python. Ваш пример очень похож на код С++.

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.

Ответ 6

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

Как видите, a и b - это просто имена, которые ссылаются на один и тот же объект 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. Вы можете изменить этот список, но он останется тем же объектом, и a и b будут продолжать ссылаться на него

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

Ответ 7

Я написал следующий простой класс для эффективной эмуляции указателя в 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]

Конечно, также легко сделать эту работу для надиктованных предметов или атрибутов объекта. Есть даже способ сделать то, о чем просил 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.

Взаимодействие с глобальным пространством имен таким образом, очевидно, является ужасной идеей, но оно показывает, что возможно (если нецелесообразно) сделать то, о чем просил ФП.

Пример, конечно, довольно бессмысленный. Но я обнаружил, что этот класс полезен в приложении, для которого я его разработал: математическая модель, поведение которой определяется многочисленными настраиваемыми пользователем математическими параметрами, различных типов (которые, поскольку они зависят от аргументов командной строки, неизвестны во время компиляции). И как только доступ к чему-либо был инкапсулирован в объекте Parameter, всеми такими объектами можно манипулировать единообразным образом.

Хотя это не очень похоже на указатель C или C++, это решает проблему, которую я бы решил с помощью указателей, если бы писал в C++.

Ответ 8

Следующий код точно имитирует поведение указателей в 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