在Python中用子类替换成员对象

时间:2013-08-22 19:38:17

标签: python oop object-composition

我有以下问题,我将尝试使用以下示例进行说明。

class Brick():
    def __init__(self):
        self.weight = 1

class House():
    def __init__(self, number_bricks):
        self.bricks = [Brick() for i in range(number_bricks)]

    def get_weight(self):
        return reduce(lambda x,y: x+y, [brick.weight for brick in self.bricks])

但现在假设我创建了一种新的Brick,StrongBrick,以便我创建一个房子,一个子类StrongHouse,StrongBrick在StrongHouse扮演与Brick在House中扮演的角色完全相同的角色。我怎样才能以一种很好的方式做到这一点(不只是重新输入所有的类定义)?

所以基本的想法是,如何将由某些对象组成的类更改为同一个类,但又包含原始成员对象的子类?

非常感谢你能给我的任何帮助。

5 个答案:

答案 0 :(得分:5)

你可以有一个工厂(砖厂?)并将其传递给House.__init__()

class Brick(object): pass

class StrongBrick(Brick): pass

class House(object):
    def __init__(self, brick_factory, num_bricks):
        self.bricks = [brick_factory() for i in range(num_bricks)]

house = House(Brick, 10000)
strong_house = House(StrongBrick, 10000)

正如您所看到的,甚至不需要子类化House来构建不同类型砖块的房屋。

答案 1 :(得分:2)

有多种方法可以做到这一点。您可以使相关的Brick类成为House类的属性:

class House(object):
    brick_class = Brick

    def __init__(self, number_bricks):
        self.bricks = [self.brick_class() for i in range(number_bricks)]


class StrongHouse(House):
    brick_class = StrongBrick

或者,您可以在构建House时传入要使用的Brick类:

class House(object):

    def __init__(self, brick_class, number_bricks):
        self.bricks = [brick_class() for i in range(number_bricks)]

答案 2 :(得分:2)

一个很好的模式可能是这个:

class Brick(object):
    weight = 1

class StrongBrick(Brick):
    weight = 42

class House(object):
    brick_type = Brick

    def __init__(self, number_bricks):
        self.bricks = [self.brick_type() for i in range(number_bricks)]

    def get_weight(self):
        return reduce(lambda x, y: x + y, [brick.weight for brick in self.bricks])

class StrongHouse(House):
    brick_type = StrongBrick

另一种方法是创建一个函数来创建工厂,并使用默认值为brick_type的参数:

class House(object):
    def __init__(self, number_bricks, brick_type=Brick):
        self.bricks = [brick_type() for i in range(number_bricks)]

    def get_weight(self):
        return reduce(lambda x, y: x + y, [brick.weight for brick in self.bricks])

def make_house_factory(brick_type):
    def factory(number_bricks):
        return House(number_bricks, brick_type)

    return factory

StrongHouse = make_house_factory(StrongBrick)

当然,所有这些对象都只是House的实例,即使我在这里命名StrongHouse,所以它类似于类名。

答案 3 :(得分:1)

  

但现在假设我创建了一种新的BrickStrongBrick,以便我创建一个房子,一个子类StrongHouse,其中StrongBrick扮演完全相同的角色在StrongHouse Brick House中播放def make_house_class(brick_type): class NewHouse(House): def __init__(self, number_bricks): self.bricks = [brick_type() for i in range(number_bricks)] return NewHouse 。我怎样才能以一种很好的方式做到这一点(不只是重新输入所有的类定义)?

正如所有其他答案所解释的那样,您根本不想创建此并行层次结构。但要回答您的直接问题:您可以动态创建类,因此可以创建并行层次结构,而无需复制和粘贴所有类定义。毕竟,类是第一类对象。

再一次,让我强调你几乎肯定不想这样做,我只是表明它是可能的。

StrongHouse = make_house_class(StrongBrick)
CheapHouse = make_house_class(CheapHouse)
# ...

现在,您可以静态创建所有房屋类型:

brick_types = (StrongBrick, CheapBrick)
house_types = {brick_type: make_house_class(brick_type) for brick_type in brick_types}

...或者可以从所有砖块类型的集合中动态构建它们:

FooHouse

...甚至添加一些hacky内省,只为当前模块中的每个FooBrick类型创建一个新的for name, value in globals().items(): if name.endswith('Brick') and name != 'Brick' and isinstance(value, type): globals()[name.replace('Brick', 'House')] = make_house_class(value) 类型:

def make_house_factory(brick_type):
    house_type = make_house_class(brick_type)
    def factory(number_bricks):
        return house_type(number_bricks, brick_type)    
    return factory

......甚至可以根据工厂制造商的需要动态创建它们:

def make_house_factory(brick_type):
    def factory(number_bricks):
        return make_house_class(brick_type)(number_bricks, brick_type)
    return factory

......甚至是生成的工厂:

{{1}}

答案 4 :(得分:0)

House.__init__添加参数,以便您指定Brick类型:

import functools
class Brick():
    def __init__(self):
        self.weight = 1

class StrongBrick():
    def __init__(self):
        self.weight = 10

class House():
    def __init__(self, number_bricks,brick_type=Brick):
        self.bricks = [brick_type() for i in range(number_bricks)]

    def get_weight(self):
        return reduce(lambda x,y: x+y, [brick.weight for brick in self.bricks])

#not a new class, but an alias with a different default brick_type
StrongHouse = functools.partial(House,brick_type=StrongBrick)