类变量和实例变量有什么区别? [英] What is the difference between class and instance variables?

查看:38
本文介绍了类变量和实例变量有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

类复杂:一 = 1

类复杂:def __init__(self):自我.a = 1

使用调用:x = Complex().a 在两种情况下都将 x 分配给 1.

关于 __init__()self 的更深入的回答将不胜感激.

解决方案

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

创建类实例后,任何引用该实例的东西都可以在其上创建实例属性.在方法内部,当前"实例几乎总是绑定到名称 self,这就是为什么您将这些视为自变量"的原因.通常在面向对象的设计中,附加到类的代码应该控制该类的实例的属性,因此几乎所有实例属性分配都是在方法内部完成的,使用对 中收到的实例的引用self 方法的参数.

类属性通常与 Java、C# 或 C++ 等语言中的静态变量(或方法)进行比较.但是,如果您想更深入地理解,我会避免将类属性视为与静态变量相同".虽然它们通常用于相同的目的,但其基本概念却大不相同.在该行下方的高级"部分中详细介绍了这一点.

一个例子!

class SomeClass:def __init__(self):self.foo = '我是一个名为 foo 的实例属性'self.foo_list = []bar = '我是一个名为 bar 的类属性'bar_list = []

执行该块后,有一个SomeClass类,有3个类属性:__init__barbar_list.

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

instance = SomeClass()

当这种情况发生时,SomeClass__init__ 方法被执行,在它的 self 参数中接收新实例.此方法创建两个实例属性:foofoo_list.然后这个实例被分配到 instance 变量中,所以它绑定到具有这两个实例属性的事物:foofoo_list.

但是:

打印实例.bar

给出:

我是一个名为bar的类属性

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

顺便说一下,这也是方法调用的工作方式.例如,当您调用 mylist.append(5) 时,mylist 没有名为 append 的属性.但是 mylistclass 可以,并且它绑定到一个方法对象.该方法对象由 mylist.append 位返回,然后 (5) 位调用带有参数 5 的方法.

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

但是你必须要小心一点.看看下面的操作:

sc1 = SomeClass()sc1.foo_list.append(1)sc1.bar_list.append(2)sc2 = SomeClass()sc2.foo_list.append(10)sc2.bar_list.append(20)打印 sc1.foo_list打印 sc1.bar_list打印 sc2.foo_list打印 sc2.bar_list

你觉得这会打印什么?

<代码>[1][2, 20][10][2, 20]

这是因为每个实例都有自己的 foo_list 副本,因此它们被单独附加.但是所有实例共享对同一个bar_list的访问.因此,当我们执行 sc1.bar_list.append(2) 时,它会影响 sc2,即使 sc2 还不存在!同样,sc2.bar_list.append(20) 影响了通过 sc1 检索到的 bar_list.这通常不是您想要的.

<小时>

高级研究如下.:)

要真正了解 Python,来自 Java 和 C# 等传统静态类型的面向对象语言,您必须学会重新思考类.

在 Java 中,类本身并不是真正的事物.当您编写一个类时,您更多的是声明该类的所有实例都具有的一系列共同点.在运行时,只有实例(和静态方法/变量,但它们实际上只是与类关联的命名空间中的全局变量和函数,实际上与 OO 无关).类是您在源代码中写下实例在运行时的样子的方式;它们只存在"在您的源代码中,而不存在于正在运行的程序中.

在 Python 中,类并没有什么特别之处.它是一个对象,就像其他任何东西一样.所以类属性"其实和实例属性"是一回事;实际上只有属性".区分的唯一原因是我们倾向于使用属于类的对象与不是类的对象.底层机器都是一样的.这就是为什么我说将类属性视为来自其他语言的静态变量是错误的.

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

在 Python 中,大多数类都是名为 type 的内置类的实例.正是这个类控制了类的共同行为,并使所有面向对象的东西都按照它的方式进行.拥有类的实例的默认 OO 方式具有自己的属性,并且具有由其类定义的通用方法/属性,这只是 Python 中的一种协议.如果需要,您可以更改它的大部分内容.如果您曾经听说过使用 元类,那么这就是定义一个类,该类是与 type 不同的类的实例.

关于类的唯一真正特别"的东西(除了所有使它们按照默认方式工作的内置机制)是类块语法,它使您可以更轻松地创建 的实例输入.这:

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

大致相当于以下内容:

def __init__(self, foo):self.foo = fooclassdict = {'__init__': __init__, 'z': 28 }Foo = type('Foo', (BaseFoo,) classdict)

并且它会将classdict的所有内容都安排成被创建的对象的属性.

因此,通过 Class.attribute 访问类属性就像 i = Class(); 一样容易,这几乎是微不足道的.i. 属性.iClass 都是对象,对象都有属性.这也便于理解在创建类后如何修改它;只需以与任何其他对象相同的方式分配其属性即可!

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

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

现在可能已经足够了.希望(如果您已经读到这里)我没有让您感到困惑.当您了解 Python 的工作原理时,它是很整洁的.:)

What is the difference between class and instance variables in 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.

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

解决方案

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.

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.

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.

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.

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.

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.

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.

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.

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.


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.

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.

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.

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!

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.

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.

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!

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!).

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).

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. :)

这篇关于类变量和实例变量有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆