6

我用 Python 编程已经有一段时间了,并且已经涵盖了一些 Python 风格的知识,但在如何正确使用类方面仍然存在问题。在阅读面向对象的讲座时,我经常发现像单一责任原则这样的规则

“单一职责原则说,一个类应该有一个,而且只有一个,改变的理由”

读到这里,我可能会想到将一个班级分成两个班级,例如:

class ComplicatedOperations(object):
    def __init__(self, item):
        pass

    def do(self):
        ...

    ## lots of other functions

class CreateOption(object):
    def __init__(self, simple_list):
        self.simple_list = simple_list

    def to_options(self):
        operated_data = self.transform_data(self.simple_list)
        return self.default_option() + operated_data

    def default_option(self):
        return [('', '')]

    def transform_data(self, simple_list):
        return [self.make_complicated_operations_that_requires_losts_of_manipulation(item)
                        for item in simple_list]

    def make_complicated_operations_that_requires_losts_of_manipulation(self, item):
            return ComplicatedOperations(item).do()

对我来说,这引发了许多不同的问题。喜欢:

  • 什么时候应该在类函数中使用类变量或传递参数?
  • 类应该ComplicatedOperations是一个类还是只是一堆函数?
  • 是否应该使用该__init__方法来计算最终结果。这是否使该课程难以测试。
  • pythonists的规则是什么?

回答后编辑:

所以,阅读奥古斯托的理论,我最终会得到这样的结果:

class ComplicatedOperations(object):
    def __init__(self):
        pass

    def do(self, item):
        ...

    ## lots of other functions

def default_option():
    return [('', '')]

def complicate_data(item):
    return ComplicatedOperations().do(item)

def transform_data_to_options(simple_list):
    return default_option() + [self.complicate_data(item)
                    for item in simple_list]

(还纠正了 default_option 的一个小错误。)

4

4 回答 4

1

一个类通常代表一种对象。类实例是该类型的特定对象。一个经典的例子是一个Animal类。acat将是Animal. 类变量(我假设您的意思是那些属于实例而不是类对象本身的变量),应该用于实例的属性。在这种情况下,例如,colour可以是一个类属性,它可以设置为cat.colour = "white"bear.colour = "brown"。如果值可能来自类外的某个来源,则应使用参数。如果Animal该类有一个sleep方法,它可能需要知道动物睡眠的持续时间和姿势。 duration这将是该方法的一个参数,因为它与动物没有关系,但是 posture将是一个类变量,因为它是由动物决定的。

在 python 中,一个类通常用于将一组共享状态的函数和变量组合在一起。继续上面的例子,一个特定的动物有一个在它的方法之间共享的状态,并且由它的属性定义。如果您的类只是一组不依赖于类状态的函数,那么它们也可以很容易地成为单独的函数。

如果__init__用于计算最终结果(由于__init__无法返回结果,必须将其存储在类的属性中),那么您还不如使用函数。然而,一种常见的模式是__init__通过类的其他几个(有时是私有的)方法进行大量处理。这样做的原因是,如果将大型复杂功能分解为更小的、不同的任务,然后每个任务都可以单独测试,那么它们通常更容易测试。但是,这通常仅在无论如何都需要类时才进行。

处理整个业务的一种方法是首先确定您需要什么功能。当你有一组函数或变量都作用于或应用于同一个对象时,是时候将它们移动到一个类中了。请记住,面向对象编程 (OOP) 是一种适用于某些任务的设计方法,但在本质上并不优于函数式编程(事实上,一些程序员会认为相反!),因此没有必要使用类,除非实际上存在需要。

于 2013-02-11T15:41:54.890 回答
1

类是一种组织结构。所以,如果你不使用它们来组织,那你就做错了。:)

您可以使用它们来组织几种不同的东西:

  1. 将数据与使用所述数据的方法捆绑在一起,定义代码将与该数据交互的一个点
  2. 将类似函数捆绑在一起,提供易于理解的 API,因为“每个人都知道”所有数学函数都在数学对象中
  3. 提供方法之间的定义通信,设置具有定义接口的操作“传送带”。每一个操作都是一个黑盒子,可以任意改变,只要符合标准
  4. 抽象一个概念。这可以包括围绕数据库访问等中心思想的子类、数据、方法等等。然后这个类成为一个组件,您可以在其他项目中使用,只需最少的重组

如果您不需要像上面那样做一些组织性的事情,那么您应该追求简单性并以程序/功能风格进行编程。Python 是关于拥有一个工具箱,而不是一把锤子。

于 2013-02-11T15:45:29.370 回答
1

什么时候应该使用类变量或在类函数中传递参数

在您的示例中,我将传递item给该do方法。此外,这与任何语言的编程有关,只给一个类它需要的信息(最小权限),并通过参数(依赖注入)传递不是算法内部的所有内容,所以,如果ComplicatedOperations不需要项目初始化自身,不要将其作为 init 参数提供,如果需要 item 来完成它的工作,请将其作为参数提供。

该类应该ComplicatedOperations是一个类还是只是一堆函数

我会说,取决于。如果您正在使用各种操作,并且它们绝对共享某种接口或合同。如果操作反映了一些概念,并且所有方法都与类相关,那么当然可以。但如果它们是松散且不相关的,您可能只使用函数或重新考虑单一责任并将方法拆分为其他类

是否应该使用 init 方法来计算最终结果。这是否使该课程难以测试。

不,init 方法用于初始化,您应该在单独的方法上完成它的工作。

作为旁注,由于缺乏上下文,我不明白CreateOption's 的作用是什么。如果它只是如上所示使用,您还不如将其删除...

于 2013-02-11T15:26:09.360 回答
1

我个人认为类是概念。我会定义一个 Operation 类,它的行为类似于操作,因此包含一个 do() 方法,以及可能使其唯一的所有其他方法/属性。

正如 mgilson 正确所说,如果您无法定义和隔离任何概念,那么简单的函数式方法可能会更好。

要回答您的问题:

  • 当实例之间共享某个属性时,您应该使用类属性(在 Python 中,类属性在编译时初始化,因此不同的对象将看到相同的值。通常类属性应该是常量)。使用实例属性在其方法中使用特定于对象的属性,而无需传递它们。这并不意味着你应该把所有的东西都放在 self 中,而只是你认为你的对象的特征。使用传递的变量具有与您的对象无关的值,并且可能取决于外部对象的状态(或取决于程序的执行)。
  • 如上所述,我会保留一个类 Operation 并使用一组 Operation 对象来进行计算。
  • init 方法只会实例化对象并进行对象正确行为所需的所有处理(换句话说,使其读取使用)。
  • 想想你试图建模的想法。
于 2013-02-11T15:30:57.630 回答