Python 继承:从 Base class 转换为 Derived class

Python inheritance: convert from Base class to Derived class

我对 Python 还是很陌生,所以请多多包涵。这是我的问题:

我有一个基地class,我们称它为体育游戏:

class Game:
    def __init__(self):
        self.home_team = None
        self.away_team = None

我为每项运动派生了多个 classes,但让我们以棒球为例:

class BaseballGame(Game):
    def __init__(self):
        self.home_pitcher = None
        self.away_pitcher = None

到目前为止一切都很好。然而,我在一个单独的 python 模块中有另一个实用函数,它将生成并填充在给定日期为该运动进行的所有游戏的列表。

def fetch_game_data:
    games = []
    games_found_online = code_that_fetches_online_games
    for online_game in games_found_online:
        new_game = Game()
        new_game.home_team = ...
        new_game.away_team = ...
        games.append(new_game)
    return games

它显然比使用 BeautifulSoup 进行大量解析要复杂得多,但您明白了。我的问题是这个函数 return 是一个 Base class 的列表,但我需要一个 Derived class 的列表。派生的 classes 将调用此函数来填充列表并对其进行操作。在我看来,我有两个选择:

  1. 我可以实现一个可怕的循环依赖并让 fetch_game_data 函数知道所有派生的 classes 并调用派生的 class 构造函数而不是基础 class 构造函数。派生的 classes 已经需要导入 fetch_data 模块,但现在 fetch_data 模块必须导入所有派生的 classes 才能了解它们的构造函数。更糟糕的是 fetch_data 模块不需要触及任何派生的 class 字段——它只填充基础 class 字段。循环依赖只是为了让我可以创建对象。
    1. 我可以实现将基础 class 游戏向下转换为派生 class 游戏(如 BaseballGame)的代码。然后,当 fetch_game_data 函数 return 包含所有游戏时,我可以将它们全部转换为 Derived class 对象并继续我的工作。不幸的是,我还没有看到太多关于如何实现它的方法。我尝试只更改 class 变量,但代码会抱怨,因为 Derived class 变量不存在。

我考虑过但很快失败的另一个选择是将现有的派生 class 对象列表发送到 fetch_game_data 函数中,而不是创建新的 Game 对象,它只会填充现有的那些。问题是我不知道我需要多少个游戏对象。 fetch_game_data函数通过解析网页来决定需要多少场比赛。我想我可以发送最大数量的比赛,但使用 number_of_teams/2,但是如果棒球中有双头球怎么办?这很快就会崩溃。我想我可以编写一个函数来获取游戏数据和 return 当天的游戏数量。然后我可以填充该大小的派生游戏列表并将其发送以进行填充。但是我必须再次获取所有网页数据并再次解析它以填充列表。

只有糟糕的选择!我希望有一个简单而优雅的解决方案,到目前为止我还没有找到。我愿意接受任何建议,包括重新设计(如果有意义的话)。

谢谢!

Python 无法将对象转换为另一个 class(即使是 subclass)。

创建游戏对象时必须使用具体的 class。它可以在工厂方法中完成(例如create_game),像这样:

def create_game(online_game):
    if online_game.type == 'baseball':
        return BaseballGame()
    else:
        return Game()

def fetch_game_data:
    games = []
    games_found_online = code_that_fetches_online_games
    for online_game in games_found_online:
        new_game = create_game(online_game)
        new_game.home_team = ...
        new_game.away_team = ...
        games.append(new_game)
    return games

我正在从 C++ 移植现有代码,我遇到了类似的问题。

我有一个通用的 class X 和类型特定的 classes 例如XInt、XStr 等。这些 class 之间的差异不仅仅是值的类型。在 C++ 中很简单:我有 virtual X::compare(X const& other).

它在 XInt 中被覆盖。在覆盖的方法中,我首先处理 'other' 不是 XInt 的情况,然后执行 static_cast<XInt const&>(other).

python显然不可能。所以这是我的解决方案。我添加了一个 non-virtual non-public 函数来与 XInt 进行实际比较并且没有注释参数的类型:

def _compare(self, other) -> int:
     <do-actual-comparison>

def compare(self, other: X) -> int:
     <handle cases where other is not XInt>
     return self._compare_worker(other)

还没有测试它,但 mypy 没有抱怨,而且由于 python 中的鸭子输入,它似乎可以工作。也许类似的东西对你有用。

实例启动后可以转换class,示例如下:


class A:
    def __repr__(self):
        return "class A"
    def convert(self):
        'convert A to B class'
        self.__class__ = B
    def hello(self):
        print('hello from A')
        
        
class B(A):
    ""
    def __repr__(self):
        return "class B"
            
    def hello(self):
        print('hello from B')
        

a = A()

print(a)
a.hello()
a.convert()
print(a)
a.hello()

# output:
>>> "class A"
>>> "hello from A"
>>> "class B"
>>> "hello from B"

在您的情况下,您可以在创建实例后将 class Game 转换为您想要的任何子 class。