解释Python变量作用域

8

我正在自学Python,我正在将一些示例代码翻译成这个:

class Student(object):
    def __init__( self, name, a,b,c ):
        self.name = name
        self.a = a
        self.b = b
        self.c = c

    def average(self):
        return ( a+b+c ) / 3.0 

这基本上是我预期的类定义。

在主方法中,稍后我将创建一个实例并将其命名为a

if __name__ == "__main__" :
    a = Student( "Oscar", 10, 10, 10 )

这就是我发现在“main”中声明的变量“a”可供方法“average”使用的方式,为使该方法正常运行,我必须键入“self.a + self.b + self.c”,保持原有的HTML标签格式。
这样做的原理是什么?

2
顺便提一下,始终继承object以便使用新式类,即class Student(object): - Mike Graham
@Mike 在 Python 2.x 中,子类化 object 是良好的编程实践,但在 Python 3 中,这是不必要的,因为所有类都是“新式”的。我意识到 Python 3 的普及率较低,并且假设 OP 使用的是 Python 2.x 是合理的,但希望有一天这将是一个错误的假设。 - gotgenes
3个回答

10
裸名(如abc)总是有局部或全局的范围(除了嵌套函数,它们在您的代码中不存在)。这样做的原因是进一步增加作用域会不必要地使事情更加复杂——例如,如果在self.a = a中裸名a可以被定为您想要的含义(等同于self.a),那么赋值本身就没有意义(将一个名称分配给自己),因此需要进一步复杂的规则。
当您希望与裸名的简单、直接和优化行为不同的东西时,只需使用限定名称(如self.a)即可,这是迄今为止最简单的方法——完全可行,没有任何复杂的规则,并允许编译器有效地优化事物(例如,裸名的作用域总是在词法上确定,而不是依赖于环境的动态变化特征)。因此,除了对其他具有更复杂作用域规则的语言的怀旧之外,实际上没有理由使裸名的语义变得更加复杂。

我差不多懂了...但是我还不太明白,如果我在我的方法中声明一个本地变量a会发生什么...(我猜我只需要打一点代码就能找出来)。马上回来。 - OscarRyz
我回来了..所以当我声明一个局部变量a时,它会优先于全局变量a(正如预期的那样)。我想我唯一能使用全局变量的方法是..不要给我的局部变量取相同的名字,对吗? - OscarRyz
@Oscar Reyes:如果你想使用这样的变量,请将它作为参数传递给方法,例如 student.average(a),其中 def average(self, para)。然后你可以在方法内部使用 para,并且它将具有你调用方法时提供的值。但这不是 Python 特定的,任何编程语言都是这样的... - Felix Kling
@Felix 我并不是真的想使用它,我只是在想如果我无意中将本地变量命名为全局变量会发生什么。它做了预期的事情,本地变量优先于全局变量 :) - OscarRyz
@Oscar Reyes:好的,我刚刚以这种方式阅读了您的评论; 无论如何,也许这对您也有趣:https://dev59.com/Q3RC5IYBdhLWcg3wOeWB - Felix Kling

2

有几个原因,但主要的原因来自Python之禅:“显式优于隐式”。在像C++这样的语言中,类上的方法总是有一个隐式参数this,每次调用该方法时都会将其推送到堆栈上。在这种情况下,当实例变量b和全局变量b同时存在时,用户可能只是引用b,而不知道另一个变量也会被使用。因此,Python强制您明确指定作用域以避免混淆。

话虽如此,还有其他原因。例如,我可能会在类外定义函数,然后在运行时将其附加到类上。例如:

def log(self):
    print "some library function requires all objects to have a log method"
    print "unfortunately we're using the Student class, which doesn't have one"
    print "this class is defined in a separate library, so we can't add the method"
    print "fortunately, we can just add the method dynamically at runtime"

Student.log = log

这里的关键是self是显式的,这使得我们可以轻松地在类外定义一个函数,然后将其附加到该类上。我并不经常做这种事情,但当我需要时,它非常有用。

以下是一个更复杂的例子; 假设我们想要在另一个类中定义一个类,例如用于单元测试的目的:

class SomeUnitTests(TestCase):
    def test_something(self):
        class SomeMockObject(SomeActualObject):
            def foo(self2):
                self.assertEqual(self2.x, SOME_CONSTANT)

        some_lib.do_something_with(SomeMockObject)

这里存在一个明确的自我(我们可以称之为任何名字,它不必是self),允许我们区分内部和外部类的self。再次强调,我并不经常这样做,但当我这样做时,它非常有用。


2
有道理,除非没有显式的全局变量(或者是吗?) - OscarRyz
有一个全局关键字,你可以在函数内部使用它来声明一个变量为全局作用域:http://docs.python.org/release/2.5.2/ref/global.html - Eli Courtwright

-1

所有实例变量都应该使用self来调用


另外补充一点,我认为你的回答可能是正确的,只是太模糊(和不完整)以至于需要一个负评。 - KevinDTimm

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