def __init__(self)的作用是什么?(问题标题)

42
我很新于Python,注意到了这些帖子: Python __init__ 和 self 是什么?不使用def __init__(self)定义Python类 然而,在尝试后,我发现这两个类给出了明显相同的结果。
class A(object):
    def __init__(self):
        self.x = 'Hello'

    def method_a(self, foo):
        print self.x + ' ' + foo

(来自这个问题)

class B(object):
    x = 'Hello'
    def method_b(self,foo):
        print self.x + ' ' + foo

这两者之间有实质性的区别吗?或者更一般地说,__init__ 对类的属性有本质上的改变吗?文档中提到当实例被创建时会调用__init__。这是否意味着在实例化之前类B中的x已经确定了?

请参见以下与编程相关的内容:https://dev59.com/aHVC5IYBdhLWcg3wtzgQ - joel
3个回答

48

嘿,看这个:

class A(object):
    def __init__(self):
        self.lst = []

class B(object):
    lst = []

现在尝试:

>>> x = B()
>>> y = B()
>>> x.lst.append(1)
>>> y.lst.append(2)
>>> x.lst
[1, 2]
>>> x.lst is y.lst
True

还有这个:

>>> x = A()
>>> y = A()
>>> x.lst.append(1)
>>> y.lst.append(2)
>>> x.lst
[1]
>>> x.lst is y.lst
False

这是否意味着B类中的x在实例化之前就已经被确定了?

是的,它是一个类属性(它在实例之间共享)。而在A类中,它是一个实例属性。只是恰好字符串是不可变的,因此在您的情况下没有真正的区别(除了B类为所有实例定义了一个字符串,从而使用更少的内存)。但在我的示例中有一个巨大的区别。


3
不要忘记类是对象,因此您随时可以更改类本身的属性。class A: pass;A.lst = list(range(6)) - kojiro

9

在第一个例子中,您有类的实例变量。这个变量只能通过实例来访问(需要self)。

class A():
    def __init__(self):
        self.x = 'hello'

print A.x -> AttributeError
print A().x -> 'hello'

在第二个例子中,你有一个静态变量。你可以通过类A的名称访问此变量。
class A():
  x = 'hello'

print A.x -> 'hello'
print A().x -> 'hello'

事实上,您可以拥有一个与实例变量同名的静态变量:
class A():
    x = 'hello'
    def __init__(self):
        self.x = 'world'

print A.x -> hello
print A().x -> world

静态值在所有实例之间共享

class A():
    x = 'hello'

    @staticmethod
    def talk():
        print A.x

a = A()
print a.talk() -> hello

A.x = 'world'
print a.talk() -> world

你在这里有一篇好文章:http://linuxwell.com/2011/07/21/static-variables-and-methods-in-python/

该文章讲述了Python中的静态变量和方法。


5

正如其他人所说,这是类变量和类实例变量之间的区别。请参考以下示例。

>>> class A:
...     a = []
... 
>>> class B:
...     def __init__(self):
...         self.b = []
... 
>>> a1 = A()
>>> a1.a.append('hello')
>>> a2 = A()
>>> a2.a
['hello']
>>> b1 = B()
>>> b1.b.append('goodbye')
>>> b2 = B()
>>> b2.b
[]

对于像元组、字符串等不可变对象,很难注意到差异,但对于可变对象,它会改变所有该类实例之间共享的更改。

还要注意,关键字参数默认值也会出现相同的行为!

>>> class A:
...     def __init__(self, a=[]):
...         a.append('hello')
...         print(a)
... 
>>> A()
['hello']
>>> A()
['hello', 'hello']
>>> A()
['hello', 'hello', 'hello']

>>> class B:
...     def __init__(self, b=None):
...         if b is None:
...             b = []
...         b.append('goodbye')
...         print(b)
... 
>>> B()
['goodbye']
>>> B()
['goodbye']
>>> B()
['goodbye']

这种行为经常困扰新手Python程序员。很高兴你能早期发现这些差异!

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