Python中的__init__和self是什么?

969

我正在学习Python编程语言,遇到了一些我不太理解的内容。

在像这样的方法中:

def method(self, blah):
    def __init__(?):
        ....
    ....

self是Python中的一个惯用词,用于表示对象本身。它不是强制性的,但是在方法中使用它可以让你访问和操作该对象的属性和方法。

__init__是Python中特殊的方法之一,用于在创建类的实例时初始化对象的属性。这个方法不是强制性的,但它通常被用来设置对象的初始状态。

我认为它们都是面向对象编程(OOP)的概念,但我并不是很了解。


2
我知道这个问题已经有超过一百万的浏览量了,但它从根本上是一个糟糕的问题,在一开始就应该被关闭。事实上,这是两个基本上不相关的问题,两个问题都分别通过良好质量的问答对进行了回答。尽管如此,它比这两个问题都要旧,但是。更不用说提供的代码示例毫无意义,而且这是在编辑器猜测了所需格式之后的结果。加上整个问题的文本只是......混乱不堪。 - Karl Knechtel
18个回答

702

在这段代码中:

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

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

...self变量表示对象本身的实例。大多数面向对象的语言将其作为隐藏参数传递给定义在对象上的方法; Python不会这样做。您必须显式声明它。当您创建类A的一个实例并调用其方法时,它将自动传递,例如...

a = A()               # We do not pass any argument to the __init__ method
a.method_a('Sailor!') # We only pass a single argument

__init__方法在Python中大致上表示构造函数。当您调用A()时,Python为您创建一个对象,并将其作为第一个参数传递给__init__方法。任何附加参数(例如A(24, 'Hello'))也将被作为参数传递--在这种情况下会引发异常,因为构造函数不希望它们出现。


16
如果你把x = 'Hello'放在__init__外但在类里面,会怎样呢?它是否像Java一样,还是会变成只初始化一次的静态变量? - Jayen
22
这就像是一个静态变量。它只初始化一次,并且可以通过 A.xa.x 访问。请注意,对特定类的覆盖不会影响基类,所以 A.x = 'foo'; a.x = 'bar'; print a.x; print A.x 将会先打印出 bar 然后是 foo - Chris B.
185
值得指出的是,第一个参数不一定非得叫作 self,这只是习惯上的命名。 - Henry Gomersall
23
类声明中的 object 有什么作用?我注意到有些类有它,有些则没有。 - Chris
18
@Chris 这是为了保持与 Python 2 兼容性。在 Python 3 中不需要显式继承 object,因为它会自动发生。 - Gabriel
显示剩余3条评论

309

没错,你说的对,这些是面向对象编程的概念。

__init__是一个类的构造函数。其中self参数指代了该对象的实例(就像C++中的this)。

class Point:
    def __init__(self, x, y):
        self._x = x
        self._y = y

__init__方法在为对象分配内存后被调用:

x = Point(1,2)

如果你想将值与对象一起持久化,那么在对象的方法中使用self参数是很重要的。例如,如果你像这样实现__init__方法:

class Point:
    def __init__(self, x, y):
        _x = x
        _y = y

你的xy参数将被存储在栈上的变量中,并且当init方法超出范围时将被丢弃。将这些变量设置为self._xself._y会将这些变量设置为Point对象的成员(在对象的生命周期内可以访问)。

请注意,本回答中对“构造函数”一词的使用有所澄清。在Python中,“构造函数”的职责实际上分别由两个方法完成。这些方法是__new__(负责分配内存)和__init__(如本文所述,负责初始化新创建的实例)。


5
那么,有没有任何理由不希望将self放在构造函数中?为什么你需要明确指定它呢?如果它是隐式的,那不是更好吗? - Aaron Franke
4
据我所知,init 不是构造函数,它是在创建任何对象时将被执行的第一个方法,init 用于设置对象。在 Python 中,new 才是构造函数。 - Deepanshu
1
对于阅读此答案的人来说,它是完全错误的。__init__不会在内存中分配空间。它被期望用于初始化实例属性。在内存中分配空间的是__new__ - black
8
回答的文本实际上是:“当为对象分配内存时,__init__方法被调用。”它并没有说__init__方法分配内存,而是说在分配内存时会调用该方法。也许可以通过将“when”改为“after”来进行澄清 :) - RedBlueThing
@AaronFranke:根据Python之禅(在交互式解释器中运行import this),“显式优于隐式”。在这种情况下,允许这样做有一些好处:1)方法只是在类中定义的函数,需要作为绑定方法使用隐式的self传递或作为带有显式self传递的普通函数调用。2)制作classmethodstaticmethod意味着您想要能够重命名和省略self。3)允许将函数附加到类定义后,因此无法在上下文中更改行为。 - ShadowRanger

279

一个简单的例子

希望能有所帮助,这里是我用来理解类内声明变量和在__init__函数中声明变量之间区别的一个简单示例:

class MyClass(object):
    i = 123
    def __init__(self):
        self.i = 345
     
a = MyClass()
print(a.i)
print(MyClass.i)

输出:

345
123

7
有趣的观察,但我认为你可能走错了方向。使用print MyClass.i时,更像是在调用“静态”变量i。而使用a.i时,是在调用成员变量i。对我来说,__init__只是一个构造函数,在创建类的对象时首先执行。 - captainspi
54
我尝试通过这个例子“向自己解释”的是 - 由__init__设置的变量是传递给类的实例的,而不是具有相同名称的类本身的“静态”变量... - Dave Everitt
1
我认为值得注意的是,你创建的第一个i(i=123)是一个对象变量,而第二个i(self.i = 345)是一个实例变量。虽然实例变量可以在类的每个实例中具有不同的值,但类变量在从该类创建的所有实例中都是相同的。(当然,也可以在代码中更改类变量)。因此,“静态”变量的正确术语是对象变量,而实例变量是在类方法内声明的(在这种情况下是__init__方法,但当然也可以是另一个方法)。 - BertHobe
1
这是什么意思/目的? - Ben
这更多关乎内部运作,可能会有一些意外情况 - 请参见我上面的回复评论 :-) - Dave Everitt

49

简而言之:

  1. self表示本身,它指的是调用该方法的对象。也就是说,如果有N个对象调用该方法,则self.a将为每个对象的变量a创建一个单独的实例。可以想象每个对象都有变量a的N个副本
  2. __init__是其他面向对象编程语言(如C++/Java)中所谓的构造函数。基本思想是它是一个特殊的方法,在创建该类的对象时会自动调用。

40

类对象 支持两种操作:属性引用和实例化

属性引用 使用Python中所有属性引用的标准语法:obj.name。有效的属性名称是在创建类对象时存在于类命名空间中的所有名称。因此,如果类定义如下:

class MyClass:
    """A simple example class"""
    i = 12345

    def f(self):
        return 'hello world'

MyClass.iMyClass.f是有效的属性引用,分别返回一个整数和一个函数对象。类属性也可以被赋值,所以您可以通过赋值来改变MyClass.i的值。__doc__也是一个有效的属性,返回属于类的文档字符串:"一个简单的示例类"。

类实例化使用函数符号。就好像类对象是一个无参数函数,它返回类的新实例一样。例如:

x = MyClass()
实例化操作(调用类对象)会创建一个空对象。许多类喜欢创建具有特定初始状态的实例化对象。因此,一个类可以定义一个名为__init__()的特殊方法,例如:
def __init__(self):
    self.data = []

当一个类定义了 __init__() 方法时,类实例化会自动为新创建的类实例调用 __init__()。因此,在这个例子中,可以通过以下方式获得一个新的已初始化的实例:

x = MyClass()

当然,__init__()方法可能具有更高的灵活性所需的参数。在这种情况下,传递给类实例化操作符的参数将传递给__init__()。例如,

class Complex:
    def __init__(self, realpart, imagpart):
        self.r = realpart
        self.i = imagpart

x = Complex(3.0, -4.5)
x.r, x.i

以下是我最后从官方文档中得到的帮助。


这是我的示例

class Bill():
    def __init__(self,apples,figs,dates):
        self.apples = apples
        self.figs = figs
        self.dates = dates
        self.bill = apples + figs + dates
        print ("Buy",self.apples,"apples", self.figs,"figs 
                and",self.dates,"dates. 
                Total fruitty bill is",self.bill," pieces of fruit :)")

创建 Bill 类的实例时:
purchase = Bill(5,6,7)

你将获得:
> Buy 5 apples 6 figs and 7 dates. Total fruitty bill is 18  pieces of
> fruit :)

38

__init__方法就像一个构造函数。如果您希望类函数像非静态方法一样运行,那么您需要将"self"作为第一个参数传递给任何类函数。"self"是您的类的实例变量。


3
"self" 定义了一个方法是否为静态方法!哇,这对于这个 Java 程序员来说真是大开眼界。 - liwevire

30

尝试使用这段代码。希望它能帮助像我一样的 C 程序员学习 Py。

#! /usr/bin/python2

class Person:

    '''Doc - Inside Class '''

    def __init__(self, name):
        '''Doc - __init__ Constructor'''
        self.n_name = name        

    def show(self, n1, n2):
        '''Doc - Inside Show'''
        print self.n_name
        print 'Sum = ', (n1 + n2)

    def __del__(self):
        print 'Destructor Deleting object - ', self.n_name

p=Person('Jay')
p.show(2, 3)
print p.__doc__
print p.__init__.__doc__
print p.show.__doc__

输出:

Jay

Sum = 5

Doc - 类内部

Doc - __init__ 构造函数

Doc - 内部展示

析构函数正在删除对象 - Jay


我不知道是不是只有我,但读这段代码有点困惑。也许是因为我还是一个初学者的 Python 程序员,不知道。 - Linny
我不太清楚__doc__是什么意思...它是否提取文档?如果我这样做:'''test - my doc''',我可以使用:print.p.__test__吗?还是doc是一个特定的关键字? - Thomas
@Thomas doc 是用于记录详细文档的关键字。三引号用于编写文档,而文档是可选的实体。 - Jayzcode

26

我自己也曾经难以理解这个问题,即使在这里阅读了答案。

为了正确理解__init__方法,您需要了解self。

self参数

__init__方法接受的参数包括:

def __init__(self, arg1, arg2):

但我们实际上只传递了两个参数:

instance = OurClass('arg1', 'arg2')

多传递的参数从哪里来了?

当我们访问对象的属性时,我们是通过名称(或引用)来访问的。这里 instance 是指向我们新对象的引用。我们使用 instance.printargs 访问实例对象的 printargs 方法。

为了在 __init__ 方法中访问对象属性,我们需要一个对象的引用。

每当调用一个方法时,都会将对主对象的引用作为第一个参数传递。根据惯例,您总是将此第一个参数称为 self。

这意味着在 __init__ 方法中,我们可以执行以下操作:

self.arg1 = arg1
self.arg2 = arg2

我们在对象上设置属性。 您可以通过以下方式验证:

instance = OurClass('arg1', 'arg2')
print instance.arg1
arg1

像这样的值称为对象属性。 在这里,__init__方法设置了实例的arg1和arg2属性。

来源:http://www.voidspace.org.uk/python/articles/OOP.shtml#the-init-method


22

请注意,self实际上可以是任何有效的Python标识符。例如,我们可以像Chris B的示例一样编写:

class A(object):
    def __init__(foo):
        foo.x = 'Hello'

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

使用self和不用它的效果完全相同。但是,建议使用self,因为其他Python程序员更容易识别它。


1
我同意在代码中使用self而不是foo,这有助于其他程序员查看代码。 - Linny

20

self是做什么的?它意味着什么?它是否强制要求?

每个类方法(包括init)的第一个参数始终是对该类当前实例的引用。按照惯例,该参数始终命名为self。在init方法中,self指的是新创建的对象;在其他类方法中,它指的是调用方法的实例。

Python不强制使用“self”。您可以给它任何名称。但请记住,在方法定义中,第一个参数是对对象的引用。Python会自动为您添加self参数;在调用方法时,您无需包含它。如果在init方法中没有提供self,则会出现错误。

TypeError: __init___() takes no arguments (1 given)

什么是 init 方法?为什么需要它?(等等)

init 是初始化的缩写。它是一个构造函数,在创建类的实例时调用,不是必需的。但通常我们的做法是编写 init 方法以设置对象的默认状态。如果您不想初始设置任何对象状态,则不需要编写此方法。


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