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

想法


或者像 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;
}
370400 次浏览

你不可能只改变那一行,你可以:

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

它创建一个列表,将引用赋给 a,然后 b 也使用引用将第一个元素设置为2,然后使用 b 引用变量进行访问。

这不是一个 bug,这是一个特性: -)

在 Python 中查看’=’运算符时,不要考虑赋值问题。你不分配任务,你绑定它们。= 是一个绑定运算符。

因此,在代码中,给值1赋一个名称: a,然后,给‘ a’中的值赋一个名称: b。然后将值2绑定到名称‘ a’。绑定到 b 的值在此操作中不会更改。

来自类 C 语言,这可能会令人困惑,但是一旦你习惯了它,你会发现它可以帮助你更清楚地阅读和推理你的代码: 名为“ b”的值不会改变,除非你明确地改变它。如果您执行“ import this”操作,您会发现,Python 的 Zen 指出,显式比隐式更好。

还要注意的是,像 Haskell 这样的函数式语言也使用这种范例,它在健壮性方面有很大的价值。

从某种角度来看,一切是 Python 中的一个指针。您的示例与 C + + 代码非常相似。

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

(一个更接近的等价物将使用某种类型的 shared_ptr<Object>而不是 int*。)

举个例子,我想 表格.data [「栏位」]及 值总是具有 同样的价值。它不完全 但我觉得有必要 不错。

可以通过在 form.data的类中重载 __getitem__来实现这一点。

我要 form.data['field']form.field.value总是有 一样的价值

这是可行的,因为它涉及修饰的名称和索引——也就是说,彻底的与您所询问的 无名氏 ab不同的构造,因为对于您的请求是完全不可能的。为什么要求一些不可能的事情 还有完全不同于(可能的)事情你实际上 想要? !

也许你没有意识到名字和装饰名字是多么的不同。当您引用一个 barename a时,您得到的正是 a上一次绑定到这个作用域中的对象(或者如果它没有被绑定到这个作用域中的话,就是一个异常)——这是 Python 如此深刻和基本的一个方面,以至于它不可能被颠覆。当你提到一个 被授予勋章名称 x.y时,你是在请求一个对象(x所指的对象)提供“ y属性”——并且为了响应这个请求,这个对象可以执行完全任意的计算(索引是非常相似的: 它也允许执行任意的计算来响应)。

现在,您的“实际的渴望”示例是神秘的,因为在每种情况下都涉及两个层次的索引或属性获取,所以您渴望的微妙之处可以通过多种方式加以介绍。例如,除了 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])

如果 任务这样的 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 中,后一个示例大体上接近于您想要的“指针”的意思——但是要理解这种微妙之处只能用于 索引和/或 授勋的名字永远不会,正如您最初要求的那样!

是的! 有一种方法可以使用一个变量作为一个指针在巨蟒!

我很抱歉地说,许多答案有一部分是错误的。原则上,每个等式(=)赋值都共享内存地址(检查 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 中,指针机制对象定义中的内存地址(“@axbbbbb...”)通常显示为。

我建议对这个话题进行调查。肯定有很多人对这个话题知道得更多。(请参阅“ ctype”模块)。希望对你有帮助。好好利用这些物品吧!你好,何塞 · 克雷斯波

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

同样,我们通过两个不同的名称 ab引用同一个对象(列表) [1]。但是现在我们可以变异这个列表,而它仍然是相同的对象,和 ab都将继续引用它

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

我编写了下面这个简单的类,作为在 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)

下面是一个使用示例(来自木星笔记本页面) :

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 要求的操作。

当然,这个例子毫无意义。但是我发现这个类在我为其开发的应用程序中很有用: 一个数学模型,其行为受众多用户可设置的数学参数支配,这些参数类型各异(因为它们依赖于命令行参数,所以在编译时不为人所知)。一旦对某些东西的访问被封装在一个 Parameter 对象中,所有这些对象都可以以统一的方式进行操作。

虽然它看起来不太像 C 或 C + + 指针,但它解决了一个问题,如果我用 C + + 编写,我会用指针来解决这个问题。

下面的代码完全模拟了 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

这是一个 python 指针(与 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

我不知道我的注释是否有帮助,但是如果你想在 python 中使用 pointers,你可以使用 dictionaries而不是变量

比方说,在你的例子中

a = {'value': 1}


b = {'value': 2}

然后你把 a 改成了2

a['value'] = 2


print(a) #{'value': 2}