Python中的指针?

mpe*_*pen 110 python pointers

我知道Python没有指针,但有没有办法有这个收益率2,而不是

>>> a = 1
>>> b = a # modify this line somehow so that b "points to" a
>>> a = 2
>>> b
1
Run Code Online (Sandbox Code Playgroud)


这是一个例子:我希望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
Run Code Online (Sandbox Code Playgroud)

输出:

GeorgeGeorgeRalphRalph
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*hen 48

你无法改变那条线.你可以做:

a = [1]
b = a
a[0] = 2
b[0]
Run Code Online (Sandbox Code Playgroud)

这会创建一个列表,将引用分配给a,然后b也使用a引用将第一个元素设置为2,然后使用b引用变量进行访问.

  • 没有不一致的地方.它与伟大的静态与动态辩论毫无关系.如果它们是对同一Java ArrayList的两个引用,则它将是相同的模数语法.如果使用不可变对象(如元组),则不必担心通过另一个引用更改对象. (14认同)
  • 这正是我讨厌Python和这些动态语言的那种不一致.(是的,它不是真的"不一致",因为你正在改变一个属性而不是引用,但我仍然不喜欢它) (13认同)
  • @Mark:的确如此.我知道无数(好吧,一些)人花了几个小时在他们的代码中搜索"bug",然后发现它是由一个没有被硬复制的列表引起的. (10认同)
  • 这根本不矛盾,因为您分配给“a”和“b”的对象是列表,而不是列表中的值。变量没有改变赋值,对象是同一个对象。如果它改变了,就像改变整数(每个都是不同的对象)一样,“a”现在将被分配给另一个对象,并且没有任何东西提示“b”效仿。这里,“a”没有被重新分配,而是它被分配到的对象内部的值正在改变。由于“b”仍然绑定到该对象,因此它将反映该对象及其内部值的更改。 (3认同)

Ale*_*lli 47

我希望form.data['field']form.field.value始终拥有相同的价值

这是可行的,因为它涉及修饰的名和索引-即,完全由不同结构barenames ab你问一下,并为您的要求是完全不可能的.为什么要求一些不可能的东西,你真正想要的(可能的)东西完全不同?

也许你没有意识到裸名和装饰名称有多么不同.当你引用一个裸名时a,你得到的是该对象a最后一次绑定到这个范围内(或者如果它没有绑定在这个范围内的例外) - 这是Python的一个深层次的基本方面,它可以可能会被颠覆.当你引用一个装饰名称时x.y,你要求一个对象(该对象x引用)请提供" y属性" - 并且为了响应该请求,该对象可以执行完全任意的计算(并且索引非常相似:它还允许在响应中执行任意计算).

现在,您的"实际需求"示例是神秘的,因为在每种情况下都涉及两个级别的索引或属性获取,因此您渴望的微妙之处可以通过多种方式引入.还有什么其他属性form.field,例如,除此之外value?没有进一步的.value计算,可能性包括:

class Form(object):
   ...
   def __getattr__(self, name):
       return self.data[name]
Run Code Online (Sandbox Code Playgroud)

class Form(object):
   ...
   @property
   def data(self):
       return self.__dict__
Run Code Online (Sandbox Code Playgroud)

的存在.value表明采摘第一种形式,加上那种-的无用的包装器:

class KouWrap(object):
   def __init__(self, value):
       self.value = value

class Form(object):
   ...
   def __getattr__(self, name):
       return KouWrap(self.data[name])
Run Code Online (Sandbox Code Playgroud)

如果这样的赋值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)
Run Code Online (Sandbox Code Playgroud)

后一个例子在Python中大致与它似乎想要的"指针"一样接近 - 但是理解这些细微之处只能用于索引和/或装饰名称是至关重要的,从不像你最初问的那样有了昵称!

  • 是的,但你似乎对我缺乏Python知识感到惊讶......好像我们是一个外国物种:P (27认同)
  • 我问我的方式,因为我希望得到一个通用的解决方案,适用于所有的东西,直到"裸名",我当时没有特定的案例 - 只是我遇到了这个这个项目的上一次迭代的问题,我不想再遇到.无论如何,这是一个很好的答案!然而,这种怀疑不太受欢迎. (21认同)
  • @Mark,看看关于你的Q的评论,你会发现"怀疑"是一个普遍的反应 - 而最高投票的A告诉你"不要这样做,克服它",然后是一个说"它就是这样".虽然你可能不会"欣赏"蟒蛇知识渊博的人对你的原始规格感到惊讶,但他们自己却非常惊人;-). (2认同)

Dav*_*rks 32

这不是一个错误,这是一个功能 :-)

当你在Python中查看'='运算符时,不要考虑赋值.你没有分配东西,你绑定它们.=是绑定运算符.

所以在你的代码中,你给值1一个名字:a.然后,您将'a'中的值赋予名称:b.然后,您将值2绑定到名称"a".绑定到b的值在此操作中不会更改.

来自类C语言,这可能会令人困惑,但是一旦你习惯了它,你会发现它可以帮助你更清楚地阅读和推理你的代码:名称为'b'的值不会改变,除非你明确地改变它.如果你做'导入',你会发现Python的Zen声明Explicit比隐含更好.

还要注意,Haskell等函数式语言也使用这种范式,在鲁棒性方面具有很大价值.

  • 你知道,我已经读过这样的答案数十次了,而且我从来都不理解它.`a = 1的行为; b = a; a = 2;`在Python,C和Java中是完全相同的:b是1.为什么这么关注"=不是赋值,它是绑定"? (35认同)
  • 你确实分配东西.这就是为什么它被称为[赋值语句](http://docs.python.org/reference/simple_stmts.html).你所说的区别没有意义.这与编译的v.解释或静态v.动态无关.Java是一种带有静态类型检查的编译语言,它也没有指针. (4认同)
  • Python和C之间的区别不在于"赋值"的含义.这就是"变量"的含义. (4认同)
  • 那么C++呢?"b"可能是对"a"的引用.理解赋值和绑定之间的区别对于完全理解为什么Mark不能做他想做的事以及如何设计像Python这样的语言至关重要.从概念上讲(不一定在实现中),"a = 1"不会用1覆盖名为"a"的内存块; 它为已经存在的对象"1"指定了一个名称"a",这与C中发生的情况根本不同.这就是为什么指针作为一个概念在Python中不存在 - 它们会在下次出现时变得陈旧原始变量是"已分配". (3认同)

Jos*_*ios 27

是! 有一种方法可以在python中使用变量作为指针!

很抱歉,许多答案都是错误的.原则上,每个相等(=)赋值共享内存地址(检查id(obj)函数),但实际上并非如此.有些变量的相等("=")行为在上一个术语中作为内存空间的副本工作,主要是在简单对象(例如"int"对象)中,而其他变量则不在(例如"list","dict"对象) .

这是一个指针分配的例子

dict1 = {'first':'hello', 'second':'world'}
dict2 = dict1 # pointer assignation mechanism
dict2['first'] = 'bye'
dict1
>>> {'first':'bye', 'second':'world'}
Run Code Online (Sandbox Code Playgroud)

以下是复制分配的示例

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
Run Code Online (Sandbox Code Playgroud)

指针分配是一种非常有用的混叠工具,在不浪费额外内存的情况下,在某些情况下执行舒适的代码,

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
Run Code Online (Sandbox Code Playgroud)

但是必须要注意这种用法以防止代码错误.

总而言之,默认情况下,一些变量是裸名(简单对象,如int,float,str,...),有些变量是指定它们之间的指针(例如dict1 = dict2).如何识别它们?试试这个实验.在具有可变资源管理器面板的IDE中,通常看起来是指针机制对象定义中的内存地址("@axbbbbbb ...").

我建议调查一下这个话题.有很多人肯定会对这个话题有更多的了解.(参见"ctypes"模块).我希望它有所帮助.享受好物品的使用!此致JoséCrespo

  • 所以我必须使用字典通过引用函数来传递变量,并且不能使用 int 或字符串通过引用传递变量? (2认同)
  • **这是错误的。** Python 中只有一种类型的变量赋值。您的示例实际演示的是字典是可变的,而整数是不可变的。当您执行 dict2 = dict1 时,两个变量现在引用内存中的同一地址,您可以使用 id() 函数确认这一点。对于 int 示例 b = a,**完全相同**。不同之处在于,在第二种情况下,您**重新分配标识符** b,但在第一种情况下,您**改变对象** dict2 (与 dict1 相同)。对于每种类型, foo = bar 的行为**完全相同**。 (2认同)

dan*_*n04 12

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

int* a = new int(1);
int* b = a;
a = new int(2);
cout << *b << endl;   // prints 1
Run Code Online (Sandbox Code Playgroud)

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

这是一个例子:我希望form.data ['field']和form.field.value始终具有相同的值.这不是完全必要的,但我认为这会很好.

您可以通过__getitem__form.data类中重载来完成此操作.


And*_*dyK 9

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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的那样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)
Run Code Online (Sandbox Code Playgroud)

如果你有一个可变对象会发生什么?

>> 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)
Run Code Online (Sandbox Code Playgroud)

现在,您通过名称a和引用相同的列表对象b.您可以改变此列表,但它将保持相同的对象,a并且b将继续引用它

>> a[0] = 2
>> a
[2]
>> b
[2]
>> id(a)
328817800  # same as before
>> id(b)
328817800  # same as before
Run Code Online (Sandbox Code Playgroud)

  • 感谢您介绍 id 函数。这解决了我的很多疑惑。 (2认同)

Hen*_*nry 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
Run Code Online (Sandbox Code Playgroud)