关于oop:在python中为什么从类内部访问类变量需要”self”?

Why accessing to class variable from within the class needs “self.” in Python?

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Python ‘self’ explained

我正在学习python,我有一个关于从这个类的方法中访问类变量的问题,这是理论上的,而不是实际的。

例如,我们有:

1
2
3
4
class ExampleClass:
    x = 123
    def example_method(self):
        print(self.x)

为什么一定要写准确的self.x,而不仅仅是xx属于类的名称空间,使用它的方法也属于它。我错过了什么?这种风格背后有什么道理?

在C++中你可以写:

1
2
3
4
5
6
7
8
9
class ExampleClass {
public:
    int x;
    void example_method()
    {
        x = 123;
        cout << x;
    };
};

它会起作用的!


从python的历史记录:添加对用户定义类的支持:

Instead, I decided to give up on the idea of implicit references to
instance variables. Languages like C++ let you write this->foo to
explicitly reference the instance variable foo (in case there’s a
separate local variable foo). Thus, I decided to make such explicit
references the only way to reference instance variables. In addition,
I decided that rather than making the current object ("this") a
special keyword, I would simply make"this" (or its equivalent) the
first named argument to a method. Instance variables would just always
be referenced as attributes of that argument.

With explicit references, there is no need to have a special syntax
for method definitions nor do you have to worry about complicated
semantics concerning variable lookup. Instead, one simply defines a
function whose first argument corresponds to the instance, which by
convention is named"self." For example:

1
2
def spam(self,y):
    print self.x, y

This approach resembles something I had seen in Modula-3, which had
already provided me with the syntax for import and exception handling.
Modula-3 doesn’t have classes, but it lets you create record types
containing fully typed function pointer members that are initialized
by default to functions defined nearby, and adds syntactic sugar so
that if x is such a record variable, and m is a function pointer
member of that record, initialized to function f, then calling
x.m(args) is equivalent to calling f(x, args). This matches the
typical implementation of objects and methods, and makes it possible
to equate instance variables with attributes of the first argument.

因此,在BDFL本人的陈述中,他决定使用显性自我而非隐性自我的唯一真正原因是:

  • 它是明确的
  • 更容易实现,因为查找必须在运行时完成(而不是像其他语言那样在编译时完成),并且具有隐式的self可能会增加查找的复杂性(从而增加查找的成本)。

编辑:在python常见问题解答中也有一个答案。


以下是我在一个关于这个特征的古老答案中所做的内容:

您遇到的问题是由以下原因造成的:

A block is a piece of Python program text that is executed as a unit.
The following are blocks: a module, a function body, and a class
definition.

(...)

A scope defines the visibility of a name within a
block.

(...)

The scope of names defined in a class block is limited to
the class block; it does not extend to the code blocks of methods –
this includes generator expressions since they are implemented using a
function scope. This means that the following will fail:

class A:

1
2
3
   a = 42  

   b = list(a + i for i in range(10))

http://docs.python.org/reference/executionmodel.html#naming-and-binding

以上是指:函数体是一个代码块,方法是一个函数,那么在类定义中存在的函数体之外定义的名称不会扩展到函数体。

当我读到这篇文章时,我觉得很奇怪,但这就是Python的制作方法:

类块中定义的名称范围仅限于类块;它不扩展到方法的代码块。

这是官方文件。

.

编辑

Heltonbiker写了一个有趣的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
COLOR = 'blue'

class TellColor(object):
    COLOR = 'red'

    def tell(self):
        print self.COLOR   # references class variable
        print COLOR        # references module variable

a = TellColor()
a.tell()

> red
> blue

这让我想知道,在方法tell()中编写的指令print COLOR如何激发打印类外定义的全局对象颜色的值。我在官方文件的这一部分找到了答案:

Methods may reference global names in the same way as ordinary
functions. The global scope associated with a method is the module
containing its definition. (A class is never used as a global scope.)
While one rarely encounters a good reason for using global data in a
method, there are many legitimate uses of the global scope: for one
thing, functions and modules imported into the global scope can be
used by methods, as well as functions and classes defined in it.
Usually, the class containing the method is itself defined in this
global scope (...)

http://docs.python.org/2/tutorial/classes.html#method-objects

当解释器必须执行print self.COLOR时,由于颜色不是一个实例属性(也就是说,标识符‘颜色’不属于实例的名称空间),解释器进入实例类的名称空间,搜索标识符‘颜色’并找到它,所以它打印tellcolor.color的值。

当解释器必须执行print COLOR时,由于本指令中没有写入属性访问,它将在全局命名空间中搜索标识符"color",官方文档称其为模块的命名空间。


在python中,它似乎与模块与类范围处理有关:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
COLOR = 'blue'

class TellColor(object):
    COLOR = 'red'

    def tell(self):
        print self.COLOR   # references class variable
        print COLOR        # references module variable

a = TellColor()
a.tell()

> red
> blue


什么属性名附加到对象(及其类,以及该类的祖先)在编译时是不可决定的。因此,要么显式地进行属性查找,要么:

  • 消除局部变量(在方法中)并始终使用实例变量。这不好,因为它本质上删除了具有所有优点的局部变量(至少在方法中)。
  • 决定基本x是在运行时引用属性还是本地属性(如果没有self.x则使用一些额外的规则来决定x = ...何时添加新属性)。这会降低代码的可读性,因为您永远不知道名称应该是哪个,而且基本上会将所有方法中的每个局部变量都转换为公共接口的一部分(附加该名称的属性会更改方法的行为)。

两者都有额外的缺点,即它们需要特殊的方法套管。现在,"方法"只是一个通过类属性访问的常规函数。这对于各种各样的用例非常有用。