为什么同一语句会打印出两个不同的值?

3

当我试图理解Python中的self概念时,我遇到了一个我认为很有帮助的例子。但是有一部分让我困惑。为什么print a.i输出两个不同的值?在第一种情况下,输出为5,这对我来说很有意义。但是几行之后,同样的print a.i语句输出123

def say_hi():
    return 'hi!'

i = 789

class MyClass(object):

    i = 5

    def prepare(self):
        i = 10
        self.i = 123
        print i

    def say_hi(self):
        return 'Hi there!'

    def say_something(self):
        print say_hi()

    def say_something_else(self):
        print self.say_hi()

输出

>>> print say_hi()
hi!
>>> print i
789
>>> a = MyClass()
>>> a.say_something()
hi!
>>> a.say_something_else()
Hi there!
>>> print a.i
5
>>> a.prepare()
10
>>> print i
789
>>> print a.i
123

你调用 a.prepare(),其中你有 self.i = 123 - njzk2
3个回答

5

您正在使用具有相同名称的全局、局部和实例属性:

def say_hi():        # This is the global function 'say_hi'
    return 'hi!'    

i = 789              # This is the global 'i'

class MyClass(object):

    i = 5  # This is a class attribute 'i'

    def prepare(self):
        i = 10           # Here, you are creating a new 'i' (local to this function)
        self.i = 123     # Here, you are changing the instance attribute 'i'
        print i          # Here, you are printing the new'ed 'i' (now with value 10)

    def say_hi(self):         # This is the object method 'say_hi' function
        return 'Hi there!'

    def say_something(self):
        print say_hi()         # Here, you are calling the global 'say_hi' function

    def say_something_else(self):
        print self.say_hi()    # Here, you are calling the object method 'say_hi' function

因此输出是正确的。
>>> print say_hi()          # global
hi!
>>> print i                 # global
789
>>> a = MyClass()
>>> a.say_something()       # say_something calls the global version
hi!
>>> a.say_something_else()  # say_something_else calls the object version
Hi there!
>>> print a.i               # class attribute 'i'
5
>>> a.prepare()             # prints the local 'i' and change the class attribute 'i'
10
>>> print i                 # global 'i' is not changed at all
789
>>> print a.i               # class attribute 'i' changed to 123 by a.prepare()
123

我会使用实例属性这个术语(而不是对象属性——在Python中,属性是一个非常具体的东西),但就范围和实例属性而言,这是正确的答案。 - Tony Suffolk 66
更加令人困惑的是,第一个 a.i 不是实例属性,而是类属性。prepare方法通过创建实例属性来屏蔽它。 - Yann Vernier
类属性'i'被'a.prepare()'更改为123:不是的。'a.prepare()'将类属性更改为10,并创建实例属性'self.i = 123'。 - njzk2
@njzk2:在prepare函数中,被设置为10的i是一个局部变量。prepare函数没有访问类属性。与C++不同,Python的方法不会继承其类的命名空间;这可以通过self来访问。 - Yann Vernier
@YannVernier:好的,我的错,确实需要访问 MyClass.i 才能更改类属性。 - njzk2
显示剩余2条评论

4

prepare 方法中的以下语句之前:

self.i = 123

self.i 引用了一个类属性 MyClass.i(因为实例属性未设置)。

一旦执行了 self.i = .. 语句,self.i 就引用了一个新值 123。(这不会影响类属性 MyClass.i,而是创建了一个新的实例属性)


self.i 指的是实例属性 - MyClass.i 指的是类属性 - 两者非常不同。 - Tony Suffolk 66
@TonySuffolk66,当没有实例属性名为i时,self.i指的是类属性。 - falsetru
好的 - 我接受你是正确的......我希望没有人会编写使用那个的生产代码.... - Tony Suffolk 66
@TonySuffolk66,它通常被使用,甚至在标准库中也是如此。例如,https://hg.python.org/cpython/file/90b07d422bd9/Lib/ftplib.py#l156 https://hg.python.org/cpython/file/90b07d422bd9/Lib/ftplib.py#l156 - falsetru
@TonySuffolk66,FYI,在Data models - Python documentation中搜索“class attributes”(第二次出现)。 - falsetru

2

您正在prepare()函数中将类变量i更改为123:

    self.i = 123

接下来,您可以通过执行print a.i来调用类变量,结果将打印123。


但是我认为self指向的是实例,而不是类?换句话说,self.i会改变实例a中的值,而不是类变量。 - user3720101
self.i(或a.i)不是指类变量(或属性),它是实例属性。 - Tony Suffolk 66

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接