关于python:在__init__内部或外部声明数据属性有什么区别

What is the difference between declaring data attributes inside or outside __init__

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

Possible Duplicate:
Python: Difference between class and instance attributes

我试图在Python中了解OOP,当它在类中声明变量时,我有点困惑。我应该在__init__程序内部还是外部申报?有什么区别?

以下代码工作正常:

1
2
3
4
5
6
7
8
9
10
11
12
# Declaring variables within __init__
class MyClass:
    def __init__(self):
        country =""
        city =""
    def information(self):
        print"Hi! I'm from %s, (%s)"%(self.city,self.country)

me = MyClass()
me.country ="Spain"
me.city ="Barcelona"
me.information()

但是在__init__过程之外声明变量也有效:

1
2
3
4
5
6
7
8
9
10
11
# Declaring variables outside of __init__
class MyClass:
    country =""
    city =""
    def information(self):
        print"Hi! I'm from %s, (%s)"%(self.city,self.country)

me = MyClass()
me.country ="Spain"
me.city ="Barcelona"
me.information()


在第一个示例中,您定义的是实例属性。第二,类属性。

类属性在该类的所有实例之间共享,实例属性由该特定实例"拥有"。

示例差异

为了理解这些差异,我们使用一个例子。

我们将定义一个具有实例属性的类:

1
2
3
4
5
class MyClassOne:
    def __init__(self):
        self.country ="Spain"
        self.city ="Barcelona"
        self.things = []

一个具有类属性:

1
2
3
4
class MyClassTwo:
    country ="Spain"
    city ="Barcelona"
    things = []

以及一个打印这些对象信息的函数:

1
2
3
def information(obj):
    print"I'm from {0}, ({1}). I own: {2}".format(
                obj.city, obj.country, ','.join(obj.things))

让我们创建2个MyClassOne对象,将其中一个对象改为米兰,并给米兰"一些东西":

1
2
3
4
5
6
foo1 = MyClassOne()
bar1 = MyClassOne()

foo1.city ="Milan"
foo1.country ="Italy"
foo1.things.append("Something")

当我们在foo1bar1上调用information()时,我们得到您期望的值:

1
2
3
4
5
>>> information(foo1)
I'm from Milan, (Italy). I own: Something

>>> information(bar1)
I'
m from Barcelona, (Spain). I own:

但是,如果我们要做完全相同的事情,但是使用MyClassTwo的实例,您将看到类属性在实例之间是共享的。

1
2
3
4
5
6
foo2 = MyClassTwo()
bar2 = MyClassTwo()

foo2.city ="Milan"
foo2.country ="Italy"
foo2.things.append("Something")

然后打电话给information()

1
2
3
4
>>> information(foo2)
I'm from Milan, (Italy). I own: Something
>>> information(bar2)
I'
m from Barcelona, (Spain). I own: Something

如您所见,things正在实例之间共享。things是对每个实例都可以访问的列表的引用。因此,如果您从任何实例附加到内容,那么其他所有实例都将看到相同的列表。

之所以在字符串变量中看不到这种行为,是因为实际上您正在为实例分配一个新的变量。在这种情况下,引用由实例"拥有",而不是在类级别共享。为了举例说明,让我们为bar2分配一个新的列表:

1
bar2.things = []

这将导致:

1
2
3
4
>>> information(foo2)
I'm from Milan, (Italy). I own: Something
>>> information(bar2)
I'
m from Barcelona, (Spain). I own:


你的两个版本的代码是非常不同的。在Python中,有两个不同的实体:类和类实例。实例是在执行以下操作时创建的:

1
new_instance = my_class()

您可以通过self将属性绑定到__init__内的实例(self是新实例)。

1
2
3
class MyClass(object):
    def __init__(self):
        self.country =""  #every instance will have a `country` attribute initialized to""

关于self__init__,没有什么特别的。self是常用的名称,用于表示传递给每个方法的实例(默认情况下)。

1
a.method()  #-> Inside the class where `method` is defined, `a` gets passed in as `self`

这里唯一特别的是,在构造类时调用__init__

1
a = MyClass()  #implicitly calls `__init__`

还可以将属性绑定到类(将其放在__init__之外):

1
2
class MyClass(object):
    country =""  #This attribute is a class attribute.

在任何时候,您都可以通过以下方法将新属性绑定到实例:

1
2
my_instance = MyClass()
my_instance.attribute = something

或类的新属性,方法是:

1
MyClass.attribute = something

现在它变得有趣了。如果一个实例没有被请求的属性,那么python将查看该属性的类并返回它(如果它在那里)。因此,类属性是类的所有实例共享一段数据的一种方式。

考虑:

1
2
3
4
5
6
7
8
9
10
11
12
def MyClass(object):
    cls_attr = []
    def __init__(self):
        self.inst_attr = []

a = MyClass()
a.inst_attr.append('a added this')
a.cls_attr.append('a added this to class')
b = MyClass()
print (b.inst_attr) # []  <- empty list, changes to `a` don't affect this.
print (b.cls_attr) # ['a added this to class'] <- Stuff added by `a`!
print (a.inst_attr) #['a added this']


在类作用域中定义变量(在任何方法之外)时,它将成为类属性。在方法范围中定义值时,它将成为方法局部变量。如果将值赋给self的属性(或任何其他引用对象的标签),它将成为(或修改)实例属性。