我是Python和编程的新手,我刚刚进入OOP.在Python中,函数命名空间中定义的任何内容仅在该命名空间内有效; 一旦在那个空间之外,变量就会被遗忘:
def foo():
a = 3
print(a)
NameError: name 'a' is not defined
Run Code Online (Sandbox Code Playgroud)
据我所知,除了从函数返回数据外,函数内部的任何信息都会在函数调用结束时丢失,这就是我的问题.请使用以下代码:
class Example:
def __init__(self, name):
self.name = name
def foo(self):
self.name = 'John'
bar = Example('Jake')
bar.foo()
print(bar.name)
'John'
Run Code Online (Sandbox Code Playgroud)
我的问题是:为什么你不必在方法之后返回对象?在正常函数中,任何变量都被遗忘,但在方法中,似乎数据实际上附加到对象本身,例如尽管首先在另一个方法中foo()
引用,self.name
但该方法能够引用self.name
.它是否正确?还是有更好的技术解释?
首先,您不必在方法中返回值,而不是在python中,而不是在许多其他编程语言中.例如:
def append_a(lst):
lst.append('a')
bob = []
append_a(bob)
print(bob)
['a']
Run Code Online (Sandbox Code Playgroud)
上面我们不返回函数中的任何内容,但我们用它来修改现有的数据结构,这在任何地方都很常见.
其次,在你的第二个例子中,你创建了一个Example类的实例,当你看到self.something
你正在查看类的一个成员时,与其他语言不同,其中成员通常只声明一次,在python中你可以动态添加成员.
因此,在查看时,bar.name
您正在查看类的成员,它在实例上的值bar
.如果您查看其他实例,则值将不同.
class Example:
def __init__(self, name):
self.name = name
def foo(self):
self.name = 'John'
bar = Example('Jake')
bob = Example('Bob')
bar.foo()
print(bar.name)
print(bob.name)
John
Bob
Run Code Online (Sandbox Code Playgroud)
要理解这一点,您需要了解 self 是如何工作的。您可以在这里了解更多信息: Understanding self in python
简而言之,self 指的是调用对象。调用self.variable
是指与调用对象关联的变量。Python 足够聪明,可以在不存在的情况下创建一个。
在类内部调用与使用对象引用self.variable
调用相同object.variable
考虑以下示例来证明这一点:
class Example:
def print_x(self):
print(self.x)
obj = Example()
obj.x = 5; # Create a new attribute of the object and assign it a value 5
print(obj.x) # Outputs 5
obj.print_x() # Outputs 5
Run Code Online (Sandbox Code Playgroud)
在您的示例中,我添加了一些打印语句来帮助您了解程序在执行过程中的状态:
class Example:
def __init__(self, name):
print(dir(self)) # Printing object contents before initializing name
self.name = name # New attribute 'name' created
print(dir(self)) # Printing object contents after initializing name
def foo(self):
print("Before foo, self.name = "+ self.name)
self.name = 'John'
print("After foo, self.name = "+ self.name)
bar = Example('Jake')
bar.foo()
print(bar.name)
Run Code Online (Sandbox Code Playgroud)
上述代码的输出是
['__doc__', '__init__', '__module__', 'foo']
['__doc__', '__init__', '__module__', 'foo', 'name']
Before foo, self.name = Jake
After foo, self.name = John
John
Run Code Online (Sandbox Code Playgroud)
我将引导您完成这段代码。当我们第一次创建 bar 时,__init__()
会调用该方法。这里我们使用 打印对象的内容dir(self)
。输出['__doc__', '__init__', '__module__', 'foo']
表明该对象只有一个成员,即“foo”方法。
现在我们创建一个名为“name”的新属性,并为其分配值“Jake”。因此,该对象现在有另一个成员,即“name”属性,如下一个 dir(self) 的输出所示['__doc__', '__init__', '__module__', 'foo', 'name']
现在我们调用 foo 方法并打印该方法之前和之后的值。在 foo 中更改之前name
,与该对象关联的值为name
“Jake”。然而,name
改变后, 的值为self.name
“John”。这由
Before foo, self.name = Jake
After foo, self.name = John`
Run Code Online (Sandbox Code Playgroud)
接下来我们通过打印来验证通过更改所做的更改确实改变了inself.name
的值,这给了我们预期的输出,name
bar
bar.name
John
现在回到你的问题,self.name
当我们超出范围时,某些方法中的普通变量是否会丢失。self
基本上可以在类内的任何地方使用来引用调用对象(在本例中为 bar)。它用于操作该调用对象。现在由于bar
在范围内,我们可以打印它的name
属性。
在普通函数中,任何变量都会被忘记,但在方法中,数据似乎实际上附加到对象本身
self
操作对象中的属性并且不限于方法的范围。