类和实例变量之间的区别是什么?

时间:2022-07-18 13:22:01

What is the difference between class and instance variables in Python?

Python中的类和实例变量有什么区别?

class Complex:
    a = 1

and

class Complex:
    def __init__(self):
        self.a = 1

Using the call: x = Complex().a in both cases assigns x to 1.

使用调用:x = Complex()。a在这两种情况下都将x赋给1。

A more in-depth answer about __init__() and self will be appreciated.

关于__init__()和self的更深入的回答将受到赞赏。

2 个解决方案

#1


87  

When you write a class block, you create class attributes (or class variables). All the names you assign in the class block, including methods you define with def become class attributes.

当您编写类块时,您将创建类属性(或类变量)。在类块中分配的所有名称,包括使用def定义的方法都成为类属性。

After a class instance is created, anything with a reference to the instance can create instance attributes on it. Inside methods, the "current" instance is almost always bound to the name self, which is why you are thinking of these as "self variables". Usually in object-oriented design, the code attached to a class is supposed to have control over the attributes of instances of that class, so almost all instance attribute assignment is done inside methods, using the reference to the instance received in the self parameter of the method.

创建一个类实例之后,任何引用该实例的对象都可以在其上创建实例属性。在方法内部,“当前”实例几乎总是绑定到name self,这就是为什么您将它们视为“self variables”的原因。通常在面向对象设计中,附加到类的代码应该能够控制类实例的属性,所以几乎所有的实例属性分配都是在方法内部完成的,使用方法的self参数中接收到的实例的引用。

Class attributes are often compared to static variables (or methods) as found in languages like Java, C#, or C++. However, if you want to aim for deeper understanding I would avoid thinking of class attributes as "the same" as static variables. While they are often used for the same purposes, the underlying concept is quite different. More on this in the "advanced" section below the line.

类属性通常与Java、c#或c++等语言中的静态变量(或方法)进行比较。但是,如果您希望更深入地理解,我将避免将类属性视为与静态变量“相同”。虽然它们通常用于相同的目的,但基本的概念是完全不同的。关于这一点的更多信息,请参见下面的“高级”部分。

An example!

一个例子!

class SomeClass:
    def __init__(self):
        self.foo = 'I am an instance attribute called foo'
        self.foo_list = []

    bar = 'I am a class attribute called bar'
    bar_list = []

After executing this block, there is a class SomeClass, with 3 class attributes: __init__, bar, and bar_list.

在执行这个块之后,有一个类SomeClass,它有3个类属性:__init__、bar和bar_list。

Then we'll create an instance:

然后我们将创建一个实例:

instance = SomeClass()

When this happens, SomeClass's __init__ method is executed, receiving the new instance in its self parameter. This method creates two instance attributes: foo and foo_list. Then this instance is assigned into the instance variable, so it's bound to a thing with those two instance attributes: foo and foo_list.

当发生这种情况时,将执行SomeClass的__init__方法,在其self参数中接收新实例。该方法创建两个实例属性:foo和foo_list。然后这个实例被分配到实例变量中,所以它与这两个实例属性(foo和foo_list)绑定在一起。

But:

但是:

print instance.bar

gives:

给:

I am a class attribute called bar

How did this happen? When we try to retrieve an attribute through the dot syntax, and the attribute doesn't exist, Python goes through a bunch of steps to try and fulfill your request anyway. The next thing it will try is to look at the class attributes of the class of your instance. In this case, it found an attribute bar in SomeClass, so it returned that.

这是怎么发生的?当我们尝试通过点语法检索一个属性,而该属性不存在时,Python会经历一系列步骤来尝试并实现您的请求。接下来,它将尝试查看实例类的类属性。在本例中,它在SomeClass中找到了一个属性栏,因此它返回了这个属性栏。

That's also how method calls work by the way. When you call mylist.append(5), for example, mylist doesn't have an attribute named append. But the class of mylist does, and it's bound to a method object. That method object is returned by the mylist.append bit, and then the (5) bit calls the method with the argument 5.

这也是方法调用工作的方法。例如,当您调用listmy.append(5)时,mylist没有名为append的属性。但是mylist的类有,它被绑定到一个方法对象。该方法对象由mylist返回。附加位,然后(5)位用参数5调用方法。

The way this is useful is that all instances of SomeClass will have access to the same bar attribute. We could create a million instances, but we only need to store that one string in memory, because they can all find it.

这很有用,因为所有SomeClass的实例都可以访问相同的bar属性。我们可以创建一百万个实例,但是我们只需要将这个字符串存储在内存中,因为它们都可以找到它。

But you have to be a bit careful. Have a look at the following operations:

但你得小心一点。请看以下操作:

sc1 = SomeClass()
sc1.foo_list.append(1)
sc1.bar_list.append(2)

sc2 = SomeClass()
sc2.foo_list.append(10)
sc2.bar_list.append(20)

print sc1.foo_list
print sc1.bar_list

print sc2.foo_list
print sc2.bar_list

What do you think this prints?

你觉得这是什么图案?

[1]
[2, 20]
[10]
[2, 20]

This is because each instance has its own copy of foo_list, so they were appended to separately. But all instances share access to the same bar_list. So when we did sc1.bar_list.append(2) it affected sc2, even though sc2 didn't exist yet! And likewise sc2.bar_list.append(20) affected the bar_list retrieved through sc1. This is often not what you want.

这是因为每个实例都有自己的foo_list副本,所以它们被分别添加到。但是所有实例都可以共享同一个bar_list。因此,当我们执行sc1.bar_list.append(2)时,它会影响sc2,尽管sc2还不存在!同样,sc2.bar_list.append(20)影响了通过sc1检索的bar_list。这通常不是你想要的。


Advanced study follows. :)

先进的研究。:)

To really grok Python, coming from traditional statically typed OO-languages like Java and C#, you have to learn to rethink classes a little bit.

要真正理解Python,从传统的静态类型的oo语言(如Java和c#)开始,您必须学会重新考虑类。

In Java, a class isn't really a thing in its own right. When you write a class you're more declaring a bunch of things that all instances of that class have in common. At runtime, there's only instances (and static methods/variables, but those are really just global variables and functions in a namespace associated with a class, nothing to do with OO really). Classes are the way you write down in your source code what the instances will be like at runtime; they only "exist" in your source code, not in the running program.

在Java中,类本身并不是一个东西。当你写一个类的时候,你就会更多地声明这个类的所有实例都有相同之处。在运行时,只有实例(以及静态方法/变量,但它们实际上只是与类关联的名称空间中的全局变量和函数,与OO无关)。类是在源代码中写下实例在运行时是什么样子的方法;它们只存在于源代码中,而不存在于正在运行的程序中。

In Python, a class is nothing special. It's an object just like anything else. So "class attributes" are in fact exactly the same thing as "instance attributes"; in reality there's just "attributes". The only reason for drawing a distinction is that we tend to use objects which are classes differently from objects which are not classes. The underlying machinery is all the same. This is why I say it would be a mistake to think of class attributes as static variables from other languages.

在Python中,类没什么特别的。它是一个物体,就像其他物体一样。所以"类属性"实际上和"实例属性"是一样的;事实上,只有“属性”。区别的唯一原因是我们倾向于使用不同于类的对象。基础设备都是一样的。这就是为什么我说将类属性视为来自其他语言的静态变量是错误的。

But the thing that really makes Python classes different from Java-style classes is that just like any other object each class is an instance of some class!

但是真正使Python类与java风格的类不同的是,就像任何其他对象一样,每个类都是某个类的实例!

In Python, most classes are instances of a builtin class called type. It is this class that controls the common behaviour of classes, and makes all the OO stuff the way it does. The default OO way of having instances of classes that have their own attributes, and have common methods/attributes defined by their class, is just a protocol in Python. You can change most aspects of it if you want. If you've ever heard of using a metaclass, all that is is defining a class that is an instance of a different class than type.

在Python中,大多数类都是称为type的内建类的实例。正是这个类控制了类的一般行为,并使所有的OO内容都按照它的方式进行。具有自己属性的类的实例的默认OO方式,以及它们类定义的通用方法/属性,只是Python中的一个协议。如果你愿意,你可以改变它的大部分方面。如果您曾经听说过使用元类,那么所要做的就是定义一个与类型不同的类的实例。

The only really "special" thing about classes (aside from all the builtin machinery to make them work they way they do by default), is the class block syntax, to make it easier for you to create instances of type. This:

类唯一真正“特别”的地方是类块语法,以便您更容易地创建类型实例。这样的:

class Foo(BaseFoo):
    def __init__(self, foo):
        self.foo = foo

    z = 28

is roughly equivalent to the following:

大致相当于:

def __init__(self, foo):
    self.foo = foo

classdict = {'__init__': __init__, 'z': 28 }

Foo = type('Foo', (BaseFoo,) classdict)

And it will arrange for all the contents of classdict to become attributes of the object that gets created.

它会安排classdict的所有内容成为被创建对象的属性。

So then it becomes almost trivial to see that you can access a class attribute by Class.attribute just as easily as i = Class(); i.attribute. Both i and Class are objects, and objects have attributes. This also makes it easy to understand how you can modify a class after it's been created; just assign its attributes the same way you would with any other object!

因此,看到可以按类访问类属性变得非常简单。属性与i = Class()一样简单;i.attribute。i和类都是对象,对象有属性。这也使您很容易理解如何在创建一个类之后修改它;就像分配其他对象的属性一样!

In fact, instances have no particular special relationship with the class used to create them. The way Python knows which class to search for attributes that aren't found in the instance is by the hidden __class__ attribute. Which you can read to find out what class this is an instance of, just as with any other attribute: c = some_instance.__class__. Now you have a variable c bound to a class, even though it probably doesn't have the same name as the class. You can use this to access class attributes, or even call it to create more instances of it (even though you don't know what class it is!).

事实上,实例与用于创建它们的类没有特殊的关系。Python通过隐藏的__class__属性知道搜索实例中没有的属性的类。您可以阅读它以了解这是一个实例的类,就像其他属性一样:c = some_instance.__class__。现在,您有一个变量c绑定到一个类,即使它可能没有与类相同的名称。您可以使用它来访问类属性,或者甚至调用它来创建它的更多实例(即使您不知道它是什么类!)

And you can even assign to i.__class__ to change what class it is an instance of! If you do this, nothing in particular happens immediately. It's not earth-shattering. All that it means is that when you look up attributes that don't exist in the instance, Python will go look at the new contents of __class__. Since that includes most methods, and methods usually expect the instance they're operating on to be in certain states, this usually results in errors if you do it at random, and it's very confusing, but it can be done. If you're very careful, the thing you store in __class__ doesn't even have to be a class object; all Python's going to do with it is look up attributes under certain circumstances, so all you need is an object that has the right kind of attributes (some caveats aside where Python does get picky about things being classes or instances of a particular class).

你甚至可以赋值给i。改变什么类是一个实例!如果你这样做,没有什么特别的事情会立即发生。这不是惊天动地的。这意味着,当您查找实例中不存在的属性时,Python将查看__class__的新内容。由于这包括了大多数方法,并且方法通常期望它们所操作的实例处于某些状态,所以如果随机操作,这通常会导致错误,这非常令人困惑,但这是可以做到的。如果您非常小心,那么存储在__class__中的东西甚至不必是类对象;Python所要做的就是在某些情况下查找属性,所以您所需要的是一个具有正确类型的属性的对象(除了一些需要注意的地方,Python确实会对类或特定类的实例进行挑剔)。

That's probably enough for now. Hopefully (if you've even read this far) I haven't confused you too much. Python is neat when you learn how it works. :)

这可能就足够了。希望(如果你已经读过这篇文章)我没有把你搞糊涂。当您学习Python的工作原理时,它是很简洁的。:)

#2


9  

What you're calling an "instance" variable isn't actually an instance variable; it's a class variable. See the language reference about classes.

你所说的"实例"变量实际上不是实例变量;这是一个类变量。请参阅有关类的语言引用。

In your example, the a appears to be an instance variable because it is immutable. It's nature as a class variable can be seen in the case when you assign a mutable object:

在您的示例中,a似乎是一个实例变量,因为它是不可变的。当你分配一个可变对象时,它的性质是类变量:

>>> class Complex:
>>>    a = []
>>>
>>> b = Complex()
>>> c = Complex()
>>> 
>>> # What do they look like?
>>> b.a
[]
>>> c.a
[]
>>> 
>>> # Change b...
>>> b.a.append('Hello')
>>> b.a
['Hello']
>>> # What does c look like?
>>> c.a
['Hello']

If you used self, then it would be a true instance variable, and thus each instance would have it's own unique a. An object's __init__ function is called when a new instance is created, and self is a reference to that instance.

如果您使用self,那么它就是一个真正的实例变量,因此每个实例都有自己唯一的a。

#1


87  

When you write a class block, you create class attributes (or class variables). All the names you assign in the class block, including methods you define with def become class attributes.

当您编写类块时,您将创建类属性(或类变量)。在类块中分配的所有名称,包括使用def定义的方法都成为类属性。

After a class instance is created, anything with a reference to the instance can create instance attributes on it. Inside methods, the "current" instance is almost always bound to the name self, which is why you are thinking of these as "self variables". Usually in object-oriented design, the code attached to a class is supposed to have control over the attributes of instances of that class, so almost all instance attribute assignment is done inside methods, using the reference to the instance received in the self parameter of the method.

创建一个类实例之后,任何引用该实例的对象都可以在其上创建实例属性。在方法内部,“当前”实例几乎总是绑定到name self,这就是为什么您将它们视为“self variables”的原因。通常在面向对象设计中,附加到类的代码应该能够控制类实例的属性,所以几乎所有的实例属性分配都是在方法内部完成的,使用方法的self参数中接收到的实例的引用。

Class attributes are often compared to static variables (or methods) as found in languages like Java, C#, or C++. However, if you want to aim for deeper understanding I would avoid thinking of class attributes as "the same" as static variables. While they are often used for the same purposes, the underlying concept is quite different. More on this in the "advanced" section below the line.

类属性通常与Java、c#或c++等语言中的静态变量(或方法)进行比较。但是,如果您希望更深入地理解,我将避免将类属性视为与静态变量“相同”。虽然它们通常用于相同的目的,但基本的概念是完全不同的。关于这一点的更多信息,请参见下面的“高级”部分。

An example!

一个例子!

class SomeClass:
    def __init__(self):
        self.foo = 'I am an instance attribute called foo'
        self.foo_list = []

    bar = 'I am a class attribute called bar'
    bar_list = []

After executing this block, there is a class SomeClass, with 3 class attributes: __init__, bar, and bar_list.

在执行这个块之后,有一个类SomeClass,它有3个类属性:__init__、bar和bar_list。

Then we'll create an instance:

然后我们将创建一个实例:

instance = SomeClass()

When this happens, SomeClass's __init__ method is executed, receiving the new instance in its self parameter. This method creates two instance attributes: foo and foo_list. Then this instance is assigned into the instance variable, so it's bound to a thing with those two instance attributes: foo and foo_list.

当发生这种情况时,将执行SomeClass的__init__方法,在其self参数中接收新实例。该方法创建两个实例属性:foo和foo_list。然后这个实例被分配到实例变量中,所以它与这两个实例属性(foo和foo_list)绑定在一起。

But:

但是:

print instance.bar

gives:

给:

I am a class attribute called bar

How did this happen? When we try to retrieve an attribute through the dot syntax, and the attribute doesn't exist, Python goes through a bunch of steps to try and fulfill your request anyway. The next thing it will try is to look at the class attributes of the class of your instance. In this case, it found an attribute bar in SomeClass, so it returned that.

这是怎么发生的?当我们尝试通过点语法检索一个属性,而该属性不存在时,Python会经历一系列步骤来尝试并实现您的请求。接下来,它将尝试查看实例类的类属性。在本例中,它在SomeClass中找到了一个属性栏,因此它返回了这个属性栏。

That's also how method calls work by the way. When you call mylist.append(5), for example, mylist doesn't have an attribute named append. But the class of mylist does, and it's bound to a method object. That method object is returned by the mylist.append bit, and then the (5) bit calls the method with the argument 5.

这也是方法调用工作的方法。例如,当您调用listmy.append(5)时,mylist没有名为append的属性。但是mylist的类有,它被绑定到一个方法对象。该方法对象由mylist返回。附加位,然后(5)位用参数5调用方法。

The way this is useful is that all instances of SomeClass will have access to the same bar attribute. We could create a million instances, but we only need to store that one string in memory, because they can all find it.

这很有用,因为所有SomeClass的实例都可以访问相同的bar属性。我们可以创建一百万个实例,但是我们只需要将这个字符串存储在内存中,因为它们都可以找到它。

But you have to be a bit careful. Have a look at the following operations:

但你得小心一点。请看以下操作:

sc1 = SomeClass()
sc1.foo_list.append(1)
sc1.bar_list.append(2)

sc2 = SomeClass()
sc2.foo_list.append(10)
sc2.bar_list.append(20)

print sc1.foo_list
print sc1.bar_list

print sc2.foo_list
print sc2.bar_list

What do you think this prints?

你觉得这是什么图案?

[1]
[2, 20]
[10]
[2, 20]

This is because each instance has its own copy of foo_list, so they were appended to separately. But all instances share access to the same bar_list. So when we did sc1.bar_list.append(2) it affected sc2, even though sc2 didn't exist yet! And likewise sc2.bar_list.append(20) affected the bar_list retrieved through sc1. This is often not what you want.

这是因为每个实例都有自己的foo_list副本,所以它们被分别添加到。但是所有实例都可以共享同一个bar_list。因此,当我们执行sc1.bar_list.append(2)时,它会影响sc2,尽管sc2还不存在!同样,sc2.bar_list.append(20)影响了通过sc1检索的bar_list。这通常不是你想要的。


Advanced study follows. :)

先进的研究。:)

To really grok Python, coming from traditional statically typed OO-languages like Java and C#, you have to learn to rethink classes a little bit.

要真正理解Python,从传统的静态类型的oo语言(如Java和c#)开始,您必须学会重新考虑类。

In Java, a class isn't really a thing in its own right. When you write a class you're more declaring a bunch of things that all instances of that class have in common. At runtime, there's only instances (and static methods/variables, but those are really just global variables and functions in a namespace associated with a class, nothing to do with OO really). Classes are the way you write down in your source code what the instances will be like at runtime; they only "exist" in your source code, not in the running program.

在Java中,类本身并不是一个东西。当你写一个类的时候,你就会更多地声明这个类的所有实例都有相同之处。在运行时,只有实例(以及静态方法/变量,但它们实际上只是与类关联的名称空间中的全局变量和函数,与OO无关)。类是在源代码中写下实例在运行时是什么样子的方法;它们只存在于源代码中,而不存在于正在运行的程序中。

In Python, a class is nothing special. It's an object just like anything else. So "class attributes" are in fact exactly the same thing as "instance attributes"; in reality there's just "attributes". The only reason for drawing a distinction is that we tend to use objects which are classes differently from objects which are not classes. The underlying machinery is all the same. This is why I say it would be a mistake to think of class attributes as static variables from other languages.

在Python中,类没什么特别的。它是一个物体,就像其他物体一样。所以"类属性"实际上和"实例属性"是一样的;事实上,只有“属性”。区别的唯一原因是我们倾向于使用不同于类的对象。基础设备都是一样的。这就是为什么我说将类属性视为来自其他语言的静态变量是错误的。

But the thing that really makes Python classes different from Java-style classes is that just like any other object each class is an instance of some class!

但是真正使Python类与java风格的类不同的是,就像任何其他对象一样,每个类都是某个类的实例!

In Python, most classes are instances of a builtin class called type. It is this class that controls the common behaviour of classes, and makes all the OO stuff the way it does. The default OO way of having instances of classes that have their own attributes, and have common methods/attributes defined by their class, is just a protocol in Python. You can change most aspects of it if you want. If you've ever heard of using a metaclass, all that is is defining a class that is an instance of a different class than type.

在Python中,大多数类都是称为type的内建类的实例。正是这个类控制了类的一般行为,并使所有的OO内容都按照它的方式进行。具有自己属性的类的实例的默认OO方式,以及它们类定义的通用方法/属性,只是Python中的一个协议。如果你愿意,你可以改变它的大部分方面。如果您曾经听说过使用元类,那么所要做的就是定义一个与类型不同的类的实例。

The only really "special" thing about classes (aside from all the builtin machinery to make them work they way they do by default), is the class block syntax, to make it easier for you to create instances of type. This:

类唯一真正“特别”的地方是类块语法,以便您更容易地创建类型实例。这样的:

class Foo(BaseFoo):
    def __init__(self, foo):
        self.foo = foo

    z = 28

is roughly equivalent to the following:

大致相当于:

def __init__(self, foo):
    self.foo = foo

classdict = {'__init__': __init__, 'z': 28 }

Foo = type('Foo', (BaseFoo,) classdict)

And it will arrange for all the contents of classdict to become attributes of the object that gets created.

它会安排classdict的所有内容成为被创建对象的属性。

So then it becomes almost trivial to see that you can access a class attribute by Class.attribute just as easily as i = Class(); i.attribute. Both i and Class are objects, and objects have attributes. This also makes it easy to understand how you can modify a class after it's been created; just assign its attributes the same way you would with any other object!

因此,看到可以按类访问类属性变得非常简单。属性与i = Class()一样简单;i.attribute。i和类都是对象,对象有属性。这也使您很容易理解如何在创建一个类之后修改它;就像分配其他对象的属性一样!

In fact, instances have no particular special relationship with the class used to create them. The way Python knows which class to search for attributes that aren't found in the instance is by the hidden __class__ attribute. Which you can read to find out what class this is an instance of, just as with any other attribute: c = some_instance.__class__. Now you have a variable c bound to a class, even though it probably doesn't have the same name as the class. You can use this to access class attributes, or even call it to create more instances of it (even though you don't know what class it is!).

事实上,实例与用于创建它们的类没有特殊的关系。Python通过隐藏的__class__属性知道搜索实例中没有的属性的类。您可以阅读它以了解这是一个实例的类,就像其他属性一样:c = some_instance.__class__。现在,您有一个变量c绑定到一个类,即使它可能没有与类相同的名称。您可以使用它来访问类属性,或者甚至调用它来创建它的更多实例(即使您不知道它是什么类!)

And you can even assign to i.__class__ to change what class it is an instance of! If you do this, nothing in particular happens immediately. It's not earth-shattering. All that it means is that when you look up attributes that don't exist in the instance, Python will go look at the new contents of __class__. Since that includes most methods, and methods usually expect the instance they're operating on to be in certain states, this usually results in errors if you do it at random, and it's very confusing, but it can be done. If you're very careful, the thing you store in __class__ doesn't even have to be a class object; all Python's going to do with it is look up attributes under certain circumstances, so all you need is an object that has the right kind of attributes (some caveats aside where Python does get picky about things being classes or instances of a particular class).

你甚至可以赋值给i。改变什么类是一个实例!如果你这样做,没有什么特别的事情会立即发生。这不是惊天动地的。这意味着,当您查找实例中不存在的属性时,Python将查看__class__的新内容。由于这包括了大多数方法,并且方法通常期望它们所操作的实例处于某些状态,所以如果随机操作,这通常会导致错误,这非常令人困惑,但这是可以做到的。如果您非常小心,那么存储在__class__中的东西甚至不必是类对象;Python所要做的就是在某些情况下查找属性,所以您所需要的是一个具有正确类型的属性的对象(除了一些需要注意的地方,Python确实会对类或特定类的实例进行挑剔)。

That's probably enough for now. Hopefully (if you've even read this far) I haven't confused you too much. Python is neat when you learn how it works. :)

这可能就足够了。希望(如果你已经读过这篇文章)我没有把你搞糊涂。当您学习Python的工作原理时,它是很简洁的。:)

#2


9  

What you're calling an "instance" variable isn't actually an instance variable; it's a class variable. See the language reference about classes.

你所说的"实例"变量实际上不是实例变量;这是一个类变量。请参阅有关类的语言引用。

In your example, the a appears to be an instance variable because it is immutable. It's nature as a class variable can be seen in the case when you assign a mutable object:

在您的示例中,a似乎是一个实例变量,因为它是不可变的。当你分配一个可变对象时,它的性质是类变量:

>>> class Complex:
>>>    a = []
>>>
>>> b = Complex()
>>> c = Complex()
>>> 
>>> # What do they look like?
>>> b.a
[]
>>> c.a
[]
>>> 
>>> # Change b...
>>> b.a.append('Hello')
>>> b.a
['Hello']
>>> # What does c look like?
>>> c.a
['Hello']

If you used self, then it would be a true instance variable, and thus each instance would have it's own unique a. An object's __init__ function is called when a new instance is created, and self is a reference to that instance.

如果您使用self,那么它就是一个真正的实例变量,因此每个实例都有自己唯一的a。