0

我最初发布的问题是缺乏的,所以这里是一个解释,我希望能让每个人都满意,并取消我收到的一些反对票。

我想在一个类中有一个智能,随着实例的创建,根据某种逻辑,决定该实例属于不同的类,该类是该类的子类之一。

更具体地说,我正在制作一个Magic Square求解器作为学习练习,并且:

  • 我想要一个MagicSquare包含 MagicSquare 逻辑的类。
  • 我想拥有该类OddMagicSquareEvenMagicSquare子类,它们将包含解决这两种类型的魔方的逻辑。
  • 我希望能够调用 a 的创建MagicSquare,提供它的大小,n并让智能MagicSquare确定要创建哪个子类的实例,而不是通用的顶级类MagicSquare

我知道确定创建哪个子类 ( OddMagicSquare/ )的EvenMagicSquare实例的智能可以在. 我希望它在里面的原因可能是一种直觉。我有一种预感,这样会更干净整洁,因为在我看来,确定某个魔方是哪种魔方的逻辑属于班级。MagicSquareMagicSquareMagicSquare

4

4 回答 4

2

不是每件事都必须是一个类。
在名词王国执行
为什么不使用工厂函数?

class Car(object):
....

class SuperCar(Car):
....

def CarFactory(max_speed):
     if max_speed > 100:
         return SuperCar()
     else: 
         return Car()
于 2013-11-01T21:14:17.597 回答
2

首先,由于您没有给出示例,因此这是熟悉的玩具最小类层次结构:

class Base(object):
    def __init__(self):
        print("I'm a base")

class Child(Base):
    def __init__(self):
        super(Child, self).__init__()
        print("I'm also a child")

现在,大概你想决定是Child在中间还是之后,Base.__init__

最好的方法是这样做;只是提前移动选择。然后你不需要做任何重新分类;您只需构建您真正想要的类,并知道它将被初始化为适合该类。

您可以使用工厂函数:

def basefactory(child=False):
    if child:
        return Child()
    else:
        return Base()

…或“替代构造函数”方法:

class Base(object):
    def __init__(self):
        print("I'm a base")
    @classmethod
    def make(cls, child=False):
        if child:
            return Child()
        else:
            return cls()

或自定义__new__方法:

class Base(object):
    def __init__(self):
        print("I'm a base")
    def __new__(cls, child=False):
        return super(Base, cls).__new__(Child if child else cls)

所有这些机制的优点是__init__按预期的方式工作。如果有人编写了一个正常的、Pythonic 的__init__方法来正确调用它的超类,它就不会陷入死循环。

如果你想明确地防止孩子被叫到__class__,那么在基础中重新设置的唯一原因是。这是一个非常罕见的情况,但如果这是你真正想要的,这很容易:__init____init__

class Base(object):
    def __init__(self, child=False):
        super(Base, self).__init__()
        if child:
            self.__class__ = Child
于 2013-11-01T21:17:00.943 回答
0

最初,我提供了一个基于self.__class____init__(). 在评论中向我解释说应该避免这种情况。

现在我提供一个基于重新定义基类的答案' __new__

class Car(object):
    """The base class."""

    def __new__(cls, max_speed):
        if cls is Car and max_speed > 100:
            return object.__new__(SuperCar, max_speed)
        else:
            return object.__new__(cls, max_speed)

    def __init__(self, max_speed):
        self.max_speed = max_speed

class SuperCar(Car):
    """The sub class."""

    pass   

负责创建实例的代码在内置 (1)__new__方法中。当调用一个类(例如my_car = Car(100))并传递该调用中给出的参数时调用它。

当调用 aCar或 a的实例化SuperCar(由于 的继承__new__)时,重新定义__new__的方法会检查要创建的实例是否为 ofcls Car并且是否max_speed大于 100。

如果是这样,它会创建一个实例SuperCar,而不是Car使用object.__new__(SuperCar, max_speed)并返回它。

否则,它调用普通的、默认的、内置的__new__来创建这个实例,使用object.__new__(cls, max_speed)并返回它。这捕获了预期的情况,not max_speed > 100以及__new__子类调用重新定义的情况SuperCar(如 in my_ferrari = SuperCar(250))。

作为记录,这是我以前的答案:

当您希望将实例的类更改为它的子类之一时,根据某些条件,在创建该实例时,您可以使用一些逻辑 in __init__,如下所示:

class Car(object):
    def __init__(self, max_speed_kph):
        if self.__class__ == Car:
            if max_speed_kph > 230:
                self.__class__ = SuperCar
                self.__init__()

class SuperCar(Car):
    def __init__(self):
        print 'this car is fast!'

self.__init__()用于重新初始化实例,因为它是新类。

脚注:

  1. 这真的是内置的吗?我在 Python 文档的“内置函数”主题中看不到它。
于 2013-11-01T20:57:07.260 回答
0

我认为你在这里滥用子类。

一个子类应该代表一个子类型——其接口明显特化的东西。在奇数与偶数幻方的情况下,情况并非如此。它们都有相同的方法,并且响应这些方法做相同的(调用者可见的)事情,等等。

如果奇数幻方和偶数幻方之间的区别不在于他们做了什么,而是他们如何做,那么它们就不是子类,只是一个具有多个策略的类。

可能有一个合理MagicSquareSolver的类型,它的用户就是MagicSquare类型,它必须与奇数和偶数子类型进行不同的交互。在这种情况下,您希望将单独的策略分为单独的类。您可以将它们基于抽象基类(这在 Python 中很少需要,但无论如何我都会为了它而展示它):

class MagicSquareSolver(metaclass=abc.ABCMeta:
    def __init__(self, square):
        self.square = square
    @abc.abstractmethod
    def solve(self):
        pass

class OddMagicSquareSolver(MagicSquareSolver):
    def __init__(self, square):
        super().__init__(square)
        do_odd_specific_stuff(self)
    def solve(self):
        do_odd_specific_solution(self)

class EvenMagicSquareSolver(MagicSquareSolver):
    def __init__(self, square):
        super().__init__(square)
        do_even_specific_stuff(self)
    def solve(self):
        do_even_specific_solution(self)

class MagicSquare:
    def __init__(self, n):
        self.n = n
        self.cells = self.make_cells()
        if n % 2:
            self.solver = OddMagicSquareSolver(self)
        else:
            self.solver = EvenMagicSquareSolver(self)
    def solve(self):
        return self.solver.solve()

但是,我认为即使这样也可能是矫枉过正。解决幻方的接口是什么,即使从方的角度来看,超越单一solve方法?例如,如果您计划将解决方案过程可视化或将其变成 GUI 游戏的助手(尽管即使在这种情况下,方法或生成器方法可能仍然是全部) ,那么该问题可能会有答案您需要;以一些开源数独游戏为例)。但很可能什么都没有。solve_one_stepsolve

这就是 yossi 的回答(以及他链接到的咆哮)得到的:如果一个类的接口只是一个命名的方法do_it(或者一个更有用的名称,它与类名本身具有完全相同的信息),并且没有状态需要存在于对该方法的调用之外,您根本没有类,您有一个函数,笨拙地隐藏起来。一些语言(尤其是 Java 和 C#)迫使你像那样笨拙,但 Python 不会;你可以这样写:

class MagicSquare:
    def __init__(self, n):
        self.n = n
        self.fill_squares()
        self.cells = self.make_cells()
    def solve(self):
        if n % 2:
            return self._solve_odd()
        else:
            return self._solve_even()
    def _solve_odd(self):
        stuff()
    def _solve_even(self):
        other_stuff()
于 2013-11-05T19:10:29.253 回答