我一直在阅读OOP,并试图理解self
和__init__
的概念,我认为我找到了一个有意义的解释(至少对我来说(。这是一篇关于使用OOP概念构建线性回归估计器的文章。
文章链接
class MyLinearRegression:
def __init__(self, fit_intercept=True):
self.coef_ = None
self.intercept_ = None
self._fit_intercept = fit_intercept
外行的解释如下:
在高层,
__init__
提供了如何构建MyLinearRegression
实例的方法。。。由于CCD_ 5的实例可以采用用户赋予它的任何名称,我们需要一种方法将用户的实例名称链接回类,这样我们就可以完成某些任务。将self
视为一个变量,它的唯一工作是学习特定实例的名称
所以我认为这是有道理的。我不明白为什么在定义新方法时再次使用self
。
def predict(self, X):
"""
Output model prediction.
Arguments:
X: 1D or 2D numpy array
"""
# check if X is 1D or 2D array
if len(X.shape) == 1:
X = X.reshape(-1,1)
return self.intercept_ + np.dot(X, self.coef_)
在此版本中。self
指的是什么?
self
(通常是实例方法的第一个参数;名称self
是常规名称(指的是其方法已被调用的实例本身。在您的示例中,该特定方法的intercept_
属性将在return
语句中访问。
考虑以下示例:
class C:
def m(self):
print(self.a)
c1 = C()
c1.a = 1
c2 = C()
c2.a = 2
c1.m() # prints 1, value of "c1.a"
c2.m() # prints 2, value of "c2.a"
我们有一个类C
,我们实例化了两个对象。实例c1
和实例CCD_。我们为任一实例的属性a
分配一个不同的值,然后调用一个方法m
,该方法访问其实例的属性a
并打印它
创建类MyLinearRegression
的实例时,即
linear_regression = MyLinearRegression(fit_intercept=True)
linear_regression对象已使用以下属性初始化:
linear_regression.coef_ = None
linear_regression.intercept_ = None
linear_regression._fit_intercept = fit_intercept
请注意,类定义中的"self"是如何指代我们创建的对象实例(即linear_regression(的
类方法"预测"可以称为如下:
linear_regression.predict(X)
Python在这里添加了语法糖,因此在引擎盖下,上面的函数调用转换如下:
MyLinearRegression.predict(linear_regression, X)
以"linear_regression"为例,并将其插入"self"的位置。
注意:为了获得额外的参考,您可以通过以下方式查看任何对象的所有属性/方法:
print(dir(<insert_object_here>))
我希望这能有所帮助。
如果使用self
作为函数的第一个参数,则意味着只有此class
中的instance
才能调用此函数。class
中的函数可分为class method
、instance method
和static method
。
class method
:它是一个可以由实例和类调用的方法。通常它与属于类而不是实例的变量一起使用。
instance method
:这是一个只能由类的实例调用的方法。通常它与属于实例的变量一起使用。
static method
:它是一个可以由实例和类调用的方法。通常,它与既不属于类也不属于实例的变量一起使用。
class X:
x = 2
def __init__(self):
self.x = 1
def instance_method(self):
return self.x
@classmethod
def class_method(cls):
return cls.x
print(X.instance_method()) # raises a TypeError
print(X().instance_method()) # not raises a TypeError, prints 1
print(X.class_method()) # not raises a TypeError, prints 2
我认为在classes:页面的随机备注中参考python文档对self
的看法可能会有所帮助
通常,方法的第一个参数称为
self
。这不过是一种约定:self
这个名称对Python来说绝对没有什么特殊的意义。然而,请注意,由于不遵循约定,您的代码可能对其他Python程序员的可读性较差,而且还可以想象,类浏览器程序可能会依赖于这样的约定来编写。
这是一个重要的区别,因为predict
是否在类中是有区别的。让我们重新访问示例的扩展版本:
class MyLinearRegression:
def __init__(self, fit_intercept=True):
self.coef_ = None
self.intercept_ = None
self._fit_intercept = fit_intercept
def predict(self, X):
"""
Output model prediction.
Arguments:
X: 1D or 2D numpy array
"""
# check if X is 1D or 2D array
if len(X.shape) == 1:
X = X.reshape(-1,1)
return self.intercept_ + np.dot(X, self.coef_)
mlr = MyLinearRegression()
mlr.predict(SomeXData)
当调用mlr.predict()
时,mlr
实例作为函数predict
的第一个参数传入。这样predict
函数就可以引用它在其中定义的类。需要注意的是,__init__
并不是相对于self
的特殊函数。所有成员函数都接受对调用该函数的对象实例的引用作为其第一个参数。
这不是唯一的方法。考虑另一个例子:
class MyLinearRegression:
def __init__(self, fit_intercept=True):
self.coef_ = None
self.intercept_ = None
self._fit_intercept = fit_intercept
def predict(self, X):
"""
Output model prediction.
Arguments:
X: 1D or 2D numpy array
"""
# check if X is 1D or 2D array
if len(X.shape) == 1:
X = X.reshape(-1,1)
return self.intercept_ + np.dot(X, self.coef_)
mlr = MyLinearRegression()
predict(mlr, SomeXData)
predict
的签名没有改变,只是函数的调用方式。这就是为什么self
作为一个参数名称并不特别。我们可以将任何类传递给predict
,它仍然会运行,尽管可能会出现错误。