3

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

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 中扮演的角色完全相同。我怎样才能以一种好的方式做到这一点(不仅仅是重新输入所有的类定义)?

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

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

4

5 回答 5

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甚至不需要子类化就可以用不同类型的砖块建造房屋。

于 2013-08-22T19:41:38.000 回答
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

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

class House(object):

    def __init__(self, brick_class, number_bricks):
        self.bricks = [brick_class() for i in range(number_bricks)]
于 2013-08-22T19:41:47.110 回答
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在这里命名是为了让它类似于类名。

于 2013-08-22T19:41:48.513 回答
1

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

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

再次强调,您几乎肯定不想这样做,而我只是表明这是可能的。

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}

… 甚至添加一些 hacky introspection 来为当前模块中的FooHouse每个类型创建一个新类型: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
于 2013-08-22T21:01:25.417 回答
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) 
于 2013-08-22T20:14:39.210 回答