为什么在使用之前创建一个 class 的实例?

Why make an instance of a class before using it?

原问题

我在所有示例中看到很多人首先创建 class 的实例: ss = StandardScaler() 之后才使用实例中的方法:ss.fit_transform(df),而不是调用 class 本身的方法:StandardScaler().fit_transform(df).

这是因为:

  1. 有些情况下会抛出错误。
  2. 有些情况下,不会抛出错误,但会产生不同的结果(可怕!)
  3. 防止代码重复(但没关系,如果它只使用一次。)
  4. 一行代码只做一件事更好。
  5. 美学与观点。
  6. 其他原因,请告诉我!

到目前为止的一些答案

感谢您提出许多澄清要点的回答,以下是我的一些理解。如有错误请指正

我建议先创建实例的潜在原因:

有些情况,否则会抛出错误。

有些情况,没有抛出错误,却产生不同的结果(可怕!)

可以调用 class 本身,如果它只使用一次。

一行代码只做一件事更好

美学与观点

其他原因,请告诉我!

代码示例

import numpy as np
from sklearn.preprocessing import StandardScaler

# Here I'm using .interegs() without making an instance first
int_array1 = np.random.default_rng(0).integers(10, size=(4,5))

# Here I'm using .interegs() without making an instance first
int_array2 = StandardScaler().fit_transform(int_array1)

# This time instantiating before using for comparison
rng = np.random.default_rng(0)
int_array3 = rng.integers(10, size=(4,5))
ss = StandardScaler()
int_array4 = ss.fit_transform(int_array3)

print(int_array1)
print(int_array2)
print(int_array3)
print(int_array4)

无论实例化如何,输出都具有相同的结果。

[[8 6 5 2 3]
 [0 0 0 1 8]
 [6 9 5 6 9]
 [7 6 5 5 9]]
[[ 0.88354126  0.22941573  0.57735027 -0.72760688 -1.70856429]
 [-1.68676059 -1.60591014 -1.73205081 -1.21267813  0.30151134]
 [ 0.2409658   1.14707867  0.57735027  1.21267813  0.70352647]
 [ 0.56225353  0.22941573  0.57735027  0.72760688  0.70352647]]
[[8 6 5 2 3]
 [0 0 0 1 8]
 [6 9 5 6 9]
 [7 6 5 5 9]]
[[ 0.88354126  0.22941573  0.57735027 -0.72760688 -1.70856429]
 [-1.68676059 -1.60591014 -1.73205081 -1.21267813  0.30151134]
 [ 0.2409658   1.14707867  0.57735027  1.21267813  0.70352647]
 [ 0.56225353  0.22941573  0.57735027  0.72760688  0.70352647]]

这个答案将更多地从角度出发,因为它更像是一个笼统的问题。我看不出您的问题与您发布的代码有任何关系。

想想你有一个需要用于不同目的的函数定义的实例,如果你没有 class 你就无法实现它。 Class 有助于实现 init 的目的以及变量范围 .

不仅是一种良好的设计方式,还有助于调试

你误会了。您的代码的两个版本都创建了一个实例:

ss = StandardScaler()
ss.fit_transform(df)

以及

StandardScaler().fit_transform(df)

变量名(ss)与创建实例无关。 class 名称后的大括号 (()) 负责创建实例。

没有创建实例的代码看起来像

StandardScaler.fit_transform(df)
#             ^^ note the missing braces

我们称这样的方法为static.

Some other reason, please let me know!

如果对象保持 状态,您希望它的寿命更长,即对象中的内容随时间变化。

您发布的示例非常适合演示这一点,您只是没有做对:

import numpy as np
# No variable assignment
print(np.random.default_rng(0).integers(10, size=(1, 5)))
print(np.random.default_rng(0).integers(10, size=(1, 5)))
print("-"*10)
# Variable assignment
rng = np.random.default_rng(0)
print(rng.integers(10, size=(1, 5)))
print(rng.integers(10, size=(1, 5)))

这样你就可以证明随机数生成器具有状态。并且该状态确保它在下一次调用时生成新的随机数。

可能的输出:

[[8 6 5 2 3]]
[[8 6 5 2 3]]
----------
[[8 6 5 2 3]]
[[0 0 0 1 8]]

There are cases, which would throw an error otherwise.

这不应该发生。代码

ClassName().method()

也创建了一个实例,只是没有分配变量名。就像做

temp = ClassName()
temp.method()
del temp            # the variable is gone here

There are cases, which don't throw an error, but produce different results (scary!)

这不应该发生,原因和以前一样。

Prevents repetition of code (but it's ok, if its used only once.)

正如你所说:构造函数是运行一次,当你分配变量。如果您更频繁地需要变量并且不分配变量,这可能会导致构造函数 运行 多次。

当一遍又一遍地调用同一个构造函数时,DRY(干净代码原则“不要重复自己”)成为一个原因,是的。

It's better to do just one thing on one line of code.

我认为没有这样的规定。列表理解通常是相反的。

Aesthetics & opinion.

总是 :-)

and only after that using its methods: ss.fit_transform(df).

有两种类型的方法:class 方法和(普通,实例)方法。

在没有实例(class 的对象)的情况下只能调用 class 个方法。

即使这个 - ss = StandardScaler() - 对你来说看起来是空的,但它不是,它只是使用参数的默认值。但它是可定制的,您可以拥有多个在功能上略有不同的缩放器。

在对象的情况下,可以在调用之间存储一些东西,这里也是这种情况 - 例如Scaler 存储它看到的样本数量,并且 returns self(可能允许方法链接?)在 partial_fit (click for StandardScaler.partial_fit source code)