Python中实例变量和属性的区别?

1 投票
2 回答
801 浏览
提问于 2025-04-16 20:36

在Python的文档中,关于编写扩展的部分提到:

“我们希望把实例变量作为属性暴露出来。有很多方法可以做到这一点。最简单的方法是定义成员定义:

static PyMemberDef Noddy_members[] = {
    {"first", T_OBJECT_EX, offsetof(Noddy, first), 0,
     "first name"},
    {"last", T_OBJECT_EX, offsetof(Noddy, last), 0,
     "last name"},
    {"number", T_INT, offsetof(Noddy, number), 0,
     "noddy number"},
    {NULL}  /* Sentinel */
};

然后把这些定义放在tp_members这个位置:

Noddy_members,             /* tp_members */"

不过,我们已经把实例变量放在了Noddy结构里:

 typedef struct {
    PyObject_HEAD
    PyObject *first;
    PyObject *last;
    int number;
} Noddy;

所以我想问的是,为什么要在两个地方都放这些变量。我的理解是,这样做是为了让类型和实例都有这些变量,这样在实例更新时可以保留类型的值。但是如果是这样的话,当我们改变类属性时,实例的值是怎么更新的呢?就像这样:

>>> class foo(object): x = 4
...
>>> f = foo()
>>> f.x
4
>>> foo.x = 5
>>> f.x
5

2 个回答

3

写一个C语言的扩展程序是个复杂的过程,因为你需要写C代码,还要给Python提供足够的信息,让它能像处理Python数据一样处理你的C数据。你需要提到结构体的成员两次:第一次是为了让C编译器知道怎么创建这个结构体,第二次是为了让Python知道数据在哪里,以及它们的名字是什么。

C语言没有自我检查的能力,所以你不能在程序运行时获取结构体成员的列表。PyMemberDef数组就是用来提供这些信息的。

1

好的,我做了一些研究,发现实例变量和属性是不同的,这是因为类的实例和类本身有各自独立的字典。正如文档中所说:

类实例是通过调用类对象来创建的。一个类实例有一个命名空间,这个命名空间是用字典来实现的,它是查找属性的第一个地方。如果在这里找不到某个属性,而这个实例的类中有同名的属性,那么查找会继续在类的属性中进行。

简单来说,Noddy结构体保存了实例变量,而Noddy_members保存了属性。此外:

给属性赋值或删除属性时,会更新实例的字典,而不会更新类的字典。如果类中有setattr()或delattr()方法,那么会调用这些方法,而不是直接更新实例字典。

还有:

如果找到的类属性是一个用户定义的函数对象,或者是一个未绑定的用户定义方法对象,而这个对象的关联类是实例的类(我们称之为C),或者是它的基类之一,那么它会被转换成一个绑定的用户定义方法对象。这个绑定方法的im_class属性是C,而im_self属性是这个实例。静态方法和类方法对象也会被转换,就好像它们是从类C中获取的一样;具体可以参考“类”部分。关于如何通过实例获取的类属性可能与实际存储在类的dict中的对象不同的另一种方式,可以查看“实现描述符”部分。如果没有找到类属性,而对象的类有一个getattr()方法,那么会调用这个方法来满足查找需求。

撰写回答