【问题标题】:Why make an instance of a class before using it?为什么在使用之前创建一个类的实例?
【发布时间】:2021-03-17 17:10:33
【问题描述】:

原问题

我几乎在所有示例中都看到人们首先创建一个类的实例: ss = StandardScaler() 之后才使用实例中的方法:ss.fit_transform(df),而不是在类本身上调用方法:StandardScaler().fit_transform(df)

这是因为:

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

到目前为止的一些答案

感谢您提出许多澄清问题的答案,以下是我理解的一些问题。如果我错了,请纠正我。

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

有些情况会抛出错误。

  • Thomas Weller 在下面的回答指出不应该存在,因为在类上调用该方法会创建一个临时实例 - 它只是不会存储在变量中。

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

  • Thomas Weller 在下面的回答指出不应该,因为在类上调用该方法会创建一个临时实例。

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

  • 这似乎是真的,因为没有理由将实例存储在变量中,重复也不成问题。

最好在一行代码中只做一件事。

  • 可读性比每行只做一件事更重要。在我看来,这两个版本都一样清晰易读。

审美与观点

  • 其中也涉及到其中一些。

还有其他原因,请告诉我!

  • 当然,面向对象编程在很多方面都很有用,但我的问题只涉及单独使用一个类和其他人已经为我编程的方法。
  • 我的问题不在于您是否可以将参数放入类或方法中 - 我的示例实际上是这样做的:np.random.default_rng(0).integers(10, size=(4,5))

代码示例

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]]

【问题讨论】:

  • 因为一个类的重点就是创建实例。考虑任何类,intlistdict 等...
  • 建议您阅读Object-Oriented Programming 上的维基百科文章。
  • "这里我使用 .interegs() 没有先实例化" 不,你肯定是先实例化的。

标签: python class instantiation


【解决方案1】:

这个答案将更多地从角度来看,因为它更多的是通用问题。我认为您的问题与您发布的代码没有任何关系。

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

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

【讨论】:

    【解决方案2】:

    只有在那之后才使用它的方法:ss.fit_transform(df)。

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

    只有类方法可以在没有实例(该类的对象)的情况下被调用。

    即使这 - ss = StandardScaler() - 在您看来是空的,但不是,它只是使用参数的默认值。但它是可定制的,您可以拥有多个 Scaler,它们的作用略有不同。

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

    【讨论】:

      【解决方案3】:

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

      ss = StandardScaler()
      ss.fit_transform(df)
      

      还有

      StandardScaler().fit_transform(df)
      

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

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

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

      我们称这些方法为静态

      还有其他原因,请告诉我!

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

      您发布的示例非常适合证明这一点,只是您做得不对:

      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]]
      

      在某些情况下,否则会引发错误。

      这不应该发生。代码

      ClassName().method()
      

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

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

      有些情况不会引发错误,但会产生不同的结果(可怕!)

      由于与以前相同的原因,不应发生这种情况。

      防止代码重复(但没关系,如果只使用一次。)

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

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

      最好在一行代码中只做一件事。

      我不认为有这样的规则。列表推导通常是相反的。

      美学和观点。

      总是:-)

      【讨论】:

      • "有些情况,否则会抛出错误。" “这不应该发生” - 什么?如果你调用一个没有实例的实例方法,你会得到一个错误,这是事实。 (由于 Python 的鸭子类型,您可能会传递一些行为类似于实例的东西,但这是个例外,不建议将非实例传递为 self。)
      • @h4z3 代码ClassName().method() 确实创建了一个实例,因此可以调用实例方法。它只是没有将其分配给变量。
      • 啊。 OP提到“在类本身上调用方法”,所以我假设没有实例。
      猜你喜欢
      • 2021-03-23
      • 1970-01-01
      • 2018-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-22
      • 2013-10-29
      • 1970-01-01
      相关资源
      最近更新 更多