问题:Python中的指针?
我知道Python没有指针,但是有办法提高Yield 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;
}
I know Python doesn’t have pointers, but is there a way to have this yield 2
instead
>>> a = 1
>>> b = a # modify this line somehow so that b "points to" a
>>> a = 2
>>> b
1
?
Here’s an example: I want form.data['field']
and form.field.value
to always have the same value. It’s not completely necessary, but I think it would be nice.
In PHP, for example, I can do this:
<?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
Output:
GeorgeGeorgeRalphRalph
ideone
Or like this in C++ (I think this is right, but my C++ is rusty):
#include <iostream>
using namespace std;
int main() {
int* a;
int* b = a;
*a = 1;
cout << *a << endl << *b << endl; # 1 1
return 0;
}
回答 0
我希望form.data['field']
并
form.field.value
始终拥有相同的价值
这是可行的,因为它涉及修饰的名称和索引-即,与裸名完全不同的结构以及您所要询问的内容,对于您的请求来说是完全不可能的。为什么要索要不可能的东西,而这又与您实际想要的(可能的)东西完全不同? a
b
也许您不知道裸名和修饰名有多么大的不同。当您引用裸名时a
,您将确切地知道该对象a
在该范围内最后绑定到该对象(如果未在该范围内绑定则为exceptions),这是Python如此深入和基本的方面,它可以不可能被颠覆。当您引用修饰的名称时x.y
,您正在要求一个对象(该对象所x
引用的)请提供“ y
属性”-响应该请求,该对象可以执行完全任意的计算(并且索引非常相似:它还允许作为响应执行任意计算)。
现在,您的“实际需求”示例很神秘,因为在每种情况下都涉及两个级别的索引编制或属性获取,因此可以通过多种方式引入您渴望的精妙之处。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中与您似乎想要的“指针”意义大致相近-但至关重要的是要了解这样的微妙只能与索引和/或修饰的名称一起使用,决不能像您最初要求的那样使用裸名!
I want form.data['field']
and
form.field.value
to always have the
same value
This is feasible, because it involves decorated names and indexing — i.e., completely different constructs from the barenames a
and b
that you’re asking about, and for with your request is utterly impossible. Why ask for something impossible and totally different from the (possible) thing you actually want?!
Maybe you don’t realize how drastically different barenames and decorated names are. When you refer to a barename a
, you’re getting exactly the object a
was last bound to in this scope (or an exception if it wasn’t bound in this scope) — this is such a deep and fundamental aspect of Python that it can’t possibly be subverted. When you refer to a decorated name x.y
, you’re asking an object (the object x
refers to) to please supply “the y
attribute” — and in response to that request, the object can perform totally arbitrary computations (and indexing is quite similar: it also allows arbitrary computations to be performed in response).
Now, your “actual desiderata” example is mysterious because in each case two levels of indexing or attribute-getting are involved, so the subtlety you crave could be introduced in many ways. What other attributes is form.field
suppose to have, for example, besides value
? Without that further .value
computations, possibilities would include:
class Form(object):
...
def __getattr__(self, name):
return self.data[name]
and
class Form(object):
...
@property
def data(self):
return self.__dict__
The presence of .value
suggests picking the first form, plus a kind-of-useless wrapper:
class KouWrap(object):
def __init__(self, value):
self.value = value
class Form(object):
...
def __getattr__(self, name):
return KouWrap(self.data[name])
If assignments such form.field.value = 23
is also supposed to set the entry in form.data
, then the wrapper must become more complex indeed, and not all that useless:
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)
The latter example is roughly as close as it gets, in Python, to the sense of “a pointer” as you seem to want — but it’s crucial to understand that such subtleties can ever only work with indexing and/or decorated names, never with barenames as you originally asked!
回答 1
您无法更改那条线。你可以做:
a = [1]
b = a
a[0] = 2
b[0]
这将创建一个列表,将引用分配给a,然后再将b分配给a,使用a引用将第一个元素设置为2,然后使用b引用变量进行访问。
There’s no way you can do that changing only that line. You can do:
a = [1]
b = a
a[0] = 2
b[0]
That creates a list, assigns the reference to a, then b also, uses the a reference to set the first element to 2, then accesses using the b reference variable.
回答 2
这不是一个错误,这是一个功能 :-)
当您在Python中查看’=’运算符时,不要以赋值的方式思考。您不分配东西,而是绑定它们。=是绑定运算符。
因此,在代码中,您给值1命名:然后,为“ a”中的值命名:b。然后,将值2绑定到名称’a’。绑定到b的值在此操作中不会更改。
来自类似C的语言,这可能会造成混淆,但是一旦您习惯了它,就会发现它可以帮助您更清晰地阅读和推理代码:除非您明确地更改它。而且,如果您执行“导入此操作”,您会发现Python的Zen指出显式要好于隐式。
还要注意的是,诸如Haskell之类的功能语言也使用此范例,就健壮性而言具有极大的价值。
It’s not a bug, it’s a feature :-)
When you look at the ‘=’ operator in Python, don’t think in terms of assignment. You don’t assign things, you bind them. = is a binding operator.
So in your code, you are giving the value 1 a name: a. Then, you are giving the value in ‘a’ a name: b. Then you are binding the value 2 to the name ‘a’. The value bound to b doesn’t change in this operation.
Coming from C-like languages, this can be confusing, but once you become accustomed to it, you find that it helps you to read and reason about your code more clearly: the value which has the name ‘b’ will not change unless you explicitly change it. And if you do an ‘import this’, you’ll find that the Zen of Python states that Explicit is better than implicit.
Note as well that functional languages such as Haskell also use this paradigm, with great value in terms of robustness.
回答 3
是! 有一种方法可以将变量用作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中,指针机制对象的定义中通常显示为内存地址(“ @axbbbbbb …”)。
我建议对该主题进行调查。肯定有很多人对此主题有更多的了解。(请参见“ ctypes”模块)。希望对您有所帮助。享受物品的良好使用!问候,何塞·克雷斯波
Yes! there is a way to use a variable as a pointer in python!
I am sorry to say that many of answers were partially wrong. In principle every equal(=) assignation shares the memory address (check the id(obj) function), but in practice it is not such. There are variables whose equal(“=”) behaviour works in last term as a copy of memory space, mostly in simple objects (e.g. “int” object), and others in which not (e.g. “list”,”dict” objects).
Here is an example of pointer assignation
dict1 = {'first':'hello', 'second':'world'}
dict2 = dict1 # pointer assignation mechanism
dict2['first'] = 'bye'
dict1
>>> {'first':'bye', 'second':'world'}
Here is an example of copy assignation
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
Pointer assignation is a pretty useful tool for aliasing without the waste of extra memory, in certain situations for performing comfy code,
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
but one have to be aware of this use in order to prevent code mistakes.
To conclude, by default some variables are barenames (simple objects like int, float, str,…), and some are pointers when assigned between them (e.g. dict1 = dict2). How to recognize them? just try this experiment with them. In IDEs with variable explorer panel usually appears to be the memory address (“@axbbbbbb…”) in the definition of pointer-mechanism objects.
I suggest investigate in the topic. There are many people who know much more about this topic for sure. (see “ctypes” module). I hope it is helpful. Enjoy the good use of the objects! Regards, José Crespo
回答 4
>> 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
只是引用同一不变对象(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
>> 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
As you can see a
and b
are just two different names that reference to the same immutable object (int) 1
. If later you write a = 2
, you reassign the name a
to a different object (int) 2
, but the b
continues referencing to 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)
What would happen if you had a mutable object instead, such as a list [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)
Again, we are referencing to the same object (list) [1]
by two different names a
and b
. However now we can mutate this list while it remains the same object, and a
, b
will both continue referencing to it
>> a[0] = 2
>> a
[2]
>> b
[2]
>> id(a)
328817800 # same as before
>> id(b)
328817800 # same as before
回答 5
从一个角度来看,一切都是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
类中重载来实现。
From one point of view, everything is a pointer in Python. Your example works a lot like the C++ code.
int* a = new int(1);
int* b = a;
a = new int(2);
cout << *b << endl; // prints 1
(A closer equivalent would use some type of shared_ptr<Object>
instead of int*
.)
Here’s an example: I want
form.data[‘field’] and
form.field.value to always have the
same value. It’s not completely
necessary, but I think it would be
nice.
You can do this by overloading __getitem__
in form.data
‘s class.
回答 6
这是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
This is a python pointer (different of 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
回答 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]
当然,对对象的字典项或属性进行这项工作也很容易。甚至可以使用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 ++编写的话,我将使用指针解决该问题。
I wrote the following simple class as, effectively, a way to emulate a pointer in 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)
Here’s an example of use (from a jupyter notebook page):
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]
Of course, it is also easy to make this work for dict items or attributes of an object. There is even a way to do what the OP asked for, using 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)
This will print out 2, followed by 3.
Messing with the global namespace in this way is kind of transparently a terrible idea, but it shows that it is possible (if inadvisable) to do what the OP asked for.
The example is, of course, fairly pointless. But I have found this class to be useful in the application for which I developed it: a mathematical model whose behavior is governed by numerous user-settable mathematical parameters, of diverse types (which, because they depend on command line arguments, are not known at compile time). And once access to something has been encapsulated in a Parameter object, all such objects can be manipulated in a uniform way.
Although it doesn’t look much like a C or C++ pointer, this is solving a problem that I would have solved with pointers if I were writing in 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
The following code emulates exactly the behavior of pointers in 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
Here are examples of use:
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)
But it’s now time to give a more professional code, including the option of deleting pointers, that I’ve just found in my personal library:
# 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