为什么 类 大多通过函数实例化?
Why are classes mostly instantiated through functions?
多年来,我一直将 python 用于科学目的。我最近对 class 写作变得更加熟悉,但我觉得我缺少一些关于实例化 classes 的标准方法的东西。
假设我定义了一个 class MyClass
.
class MyClass:
def __init__(self):
pass
然后我知道我可以将 x
映射到 MyClass
的实例,只需使用
x = MyClass()
效果很好,完全符合我的预期。
但是,在我看来,当我使用来自标准库或来自 numpy
或 scipy
的代码时,我不会以相同的方式创建对象:据我所知,我一般不使用class的名字来实例化它。据我了解,我想说这意味着我既不使用 class 方法也不使用 class 的默认构造函数,而是使用在 class 之外定义的其他函数。 =27=]
例如,numpy
的 random
模块使用 class Generator
来生成随机数。但是,numpy
明确建议不要使用 class 构造函数来获取 Generator
实例,而是使用 random
模块中的 default_rng
函数。所以如果我想生成随机数,我使用
rng = numpy.random.default_rng()
创建一个 Generator
实例。这是在没有明确使用 class.
的名称的情况下完成的
在我看来,我使用的大部分代码都是按后一种方式编写的。为什么?直接调用默认 class 构造函数是否被认为是不好的做法?在模块中使用单独的函数来创建 class 实例是否被认为是更好的做法?仅仅是因为在创建 class 的实例之前通常必须进行一些预处理吗? (我猜不是,因为那样的话,为什么不在 class 的初始化中这样做呢?)
我认为 np.array 调用创建 np.ndarray 可能是通过调用另一个函数创建对象的最常见方式之一。这是对此的解释。
What is the difference between ndarray and array in numpy?
我无法回答我们使用函数来“包装”对象构造的所有情况,但我在许多情况下使用此类函数来简化对象创建,从而产生更清晰的代码。我可以说这种情况。
例如,底层 class 定义可能会暴露很多参数。在 99.9% 的情况下(比方说)要求用户为 class 的所有参数提供参数值可能没有意义。这些“虚假”参数可能是固定的,或者在大多数此类情况下可以从其他参数值推断出来(例如,在大多数情况下,参数 b
是参数 a
的 2x)。在这 99.9% 的情况下,代码变得笨拙,无法显式提供此类参数的值,因此编写了一个包装函数以使其更清晰。
可以使用默认参数来处理许多这样的情况,但是将参数值的推断推入class'init[=31=可能没有意义] 函数本身。例如,虽然像 b = 2 * a if a is None else b
这样的东西放在 init 函数中似乎是合理的,其中 a、b 是参数,但实际上可能并不那么简单(例如,b 可能与 a、c、d、 f 等或者它可能是一个 class 对象本身),或者可能有 1000 个这样的参数推断要进行。因此,将此类“胶水”代码(这是一种易于使用的自定义)分离到另一个函数中并保持基本代码(实现特定功能)干净和 to-the-point.
是合乎逻辑的。
我们要编写另一个 class 包装器而不是函数包装器吗?在这种情况下,新的 class 包装器将呈现一个简化的界面。但是在这种情况下编写 class 包装器是不必要的,因为 class 意味着很多事情,而函数仅意味着程序执行。
请注意,这主要发生在库类型代码的情况下,它具有最多的用例,您希望让大多数人使用起来最容易。大多数“用户”代码不存在此类问题,我们只是为特定应用程序编写 classes。所以在实际编写应用程序时,我们应该尽可能直接使用构造函数创建classes。
还有一些@ekhumoro 上面提到的流行的工厂设计模式与此非常相似。但是根据 text-book 定义,工厂设计模式似乎仅限于 super/sub classes(我可能是错的,这可能是无用的语义)。
不,使用普通构造函数并不是坏习惯,但有时使用替代构造函数会很有用。
使用函数作为替代构造函数来创建对象的原因:
(不完整且不分先后)
- 将对象的创建与其实现分离。
在 OOP 中通常以解耦为目标。
- 隐藏复杂性
构造函数可以有很多参数,但通常需要一个默认对象。
- 更容易read/write和理解
numpy.random.default_rng()
对比 numpy.random.Generator(numpy.random.PCG64())
- 一个工厂,它根据有时复杂的条件创建和returns一个(不同的)对象。
例如python 的 open()
returns 文本文件和二进制文件的不同对象。
在哪里实施这些?
在其他一些语言中,这些将被实现为 class 它们实例化的 class 方法,甚至是新的 class.
方法
这也可以在 python 中完成,但如果它们在模块级别作为函数实现,通常更短且使用起来更方便。
多年来,我一直将 python 用于科学目的。我最近对 class 写作变得更加熟悉,但我觉得我缺少一些关于实例化 classes 的标准方法的东西。
假设我定义了一个 class MyClass
.
class MyClass:
def __init__(self):
pass
然后我知道我可以将 x
映射到 MyClass
的实例,只需使用
x = MyClass()
效果很好,完全符合我的预期。
但是,在我看来,当我使用来自标准库或来自 numpy
或 scipy
的代码时,我不会以相同的方式创建对象:据我所知,我一般不使用class的名字来实例化它。据我了解,我想说这意味着我既不使用 class 方法也不使用 class 的默认构造函数,而是使用在 class 之外定义的其他函数。 =27=]
例如,numpy
的 random
模块使用 class Generator
来生成随机数。但是,numpy
明确建议不要使用 class 构造函数来获取 Generator
实例,而是使用 random
模块中的 default_rng
函数。所以如果我想生成随机数,我使用
rng = numpy.random.default_rng()
创建一个 Generator
实例。这是在没有明确使用 class.
在我看来,我使用的大部分代码都是按后一种方式编写的。为什么?直接调用默认 class 构造函数是否被认为是不好的做法?在模块中使用单独的函数来创建 class 实例是否被认为是更好的做法?仅仅是因为在创建 class 的实例之前通常必须进行一些预处理吗? (我猜不是,因为那样的话,为什么不在 class 的初始化中这样做呢?)
我认为 np.array 调用创建 np.ndarray 可能是通过调用另一个函数创建对象的最常见方式之一。这是对此的解释。
What is the difference between ndarray and array in numpy?
我无法回答我们使用函数来“包装”对象构造的所有情况,但我在许多情况下使用此类函数来简化对象创建,从而产生更清晰的代码。我可以说这种情况。
例如,底层 class 定义可能会暴露很多参数。在 99.9% 的情况下(比方说)要求用户为 class 的所有参数提供参数值可能没有意义。这些“虚假”参数可能是固定的,或者在大多数此类情况下可以从其他参数值推断出来(例如,在大多数情况下,参数 b
是参数 a
的 2x)。在这 99.9% 的情况下,代码变得笨拙,无法显式提供此类参数的值,因此编写了一个包装函数以使其更清晰。
可以使用默认参数来处理许多这样的情况,但是将参数值的推断推入class'init[=31=可能没有意义] 函数本身。例如,虽然像 b = 2 * a if a is None else b
这样的东西放在 init 函数中似乎是合理的,其中 a、b 是参数,但实际上可能并不那么简单(例如,b 可能与 a、c、d、 f 等或者它可能是一个 class 对象本身),或者可能有 1000 个这样的参数推断要进行。因此,将此类“胶水”代码(这是一种易于使用的自定义)分离到另一个函数中并保持基本代码(实现特定功能)干净和 to-the-point.
我们要编写另一个 class 包装器而不是函数包装器吗?在这种情况下,新的 class 包装器将呈现一个简化的界面。但是在这种情况下编写 class 包装器是不必要的,因为 class 意味着很多事情,而函数仅意味着程序执行。
请注意,这主要发生在库类型代码的情况下,它具有最多的用例,您希望让大多数人使用起来最容易。大多数“用户”代码不存在此类问题,我们只是为特定应用程序编写 classes。所以在实际编写应用程序时,我们应该尽可能直接使用构造函数创建classes。
还有一些@ekhumoro 上面提到的流行的工厂设计模式与此非常相似。但是根据 text-book 定义,工厂设计模式似乎仅限于 super/sub classes(我可能是错的,这可能是无用的语义)。
不,使用普通构造函数并不是坏习惯,但有时使用替代构造函数会很有用。
使用函数作为替代构造函数来创建对象的原因:
(不完整且不分先后)
- 将对象的创建与其实现分离。
在 OOP 中通常以解耦为目标。 - 隐藏复杂性
构造函数可以有很多参数,但通常需要一个默认对象。 - 更容易read/write和理解
numpy.random.default_rng()
对比numpy.random.Generator(numpy.random.PCG64())
- 一个工厂,它根据有时复杂的条件创建和returns一个(不同的)对象。
例如python 的open()
returns 文本文件和二进制文件的不同对象。
在哪里实施这些?
在其他一些语言中,这些将被实现为 class 它们实例化的 class 方法,甚至是新的 class.
方法
这也可以在 python 中完成,但如果它们在模块级别作为函数实现,通常更短且使用起来更方便。