【问题标题】:Import subclass from a base class in Python从 Python 中的基类导入子类
【发布时间】:2012-01-09 13:35:55
【问题描述】:

我有一个基类,它有一个创建子类实例的方法,该子类的调用与输入字符串相同。

这在以前通过将子类和基类放在同一个文件中并执行类似globals()[name] 的操作来实现。

不过,现在我已将子类拆分为其他文件。它们每个顶部都有一个import base 语句,所以我不能简单地在我的基类中导入子类,否则会有一个循环导入链。

有什么解决方法吗?

base.py 中:

from basefactory import BaseFactory
class Base:
    def __init__(self, arg1, arg2):
        ...
    def resolve(self, element):
        className = typing.getClassName(element)
        return BaseFactory.getInstance(className, element, self)

basefactory.py

from file1 import *
from file2 import *
...
class BaseFactory:
    @staticmethod
    def getInstance(name, arg1, arg2):
       subclass = globals()[name]
       return subclass(arg1, arg2)

file1.py中:

from base import Base

class subclass1(Base):
    def foo(self):
        return self.arg1

【问题讨论】:

  • resolve() 是否只是为了确定类类型,并返回一个新的自身实例?
  • 它返回一个 Base 子类的实例。
  • 将resolve移入负责返回正确实例的工厂类怎么样?

标签: python inheritance import subclass base


【解决方案1】:

您可以将失败的import 语句移至创建子类对象的方法。

【讨论】:

  • 虽然这有效,但它对性能有何影响?每次调用该方法时重新导入 60 个子类不会很昂贵吗?
  • 导入的模块缓存在sys.modules,所以我预计不会有任何重大影响。
  • 另外,我必须按照from package.filename import * 为每个文件做一些事情。将来,我必须记住每次添加新文件时都要添加更多导入。是否有更紧凑的语法来导入包的所有模块的所有成员?
  • 您可以“导入包”,然后引用对象为 package.filename.member1、package.filename2.member2 等。
  • 由于我的子类实例创建器是自动的(基于字符串),我不知道子类在哪个文件中。
【解决方案2】:

据我了解,您有:

  1. 基类
  2. 基类的一系列派生类
  3. 基类中的工厂方法,用于实例化正确类型的派生类
  4. 派生类已经拆分成文件,依赖于基类,而基类中的工厂方法又依赖于派生类

一种解决方案是为工厂方法创建一个单独的函数/类,并将其放在与基类不同的文件中。该文件可以导入基类和派生类的所有文件,而无需循环引用。

例如:

# base.py:
class baseClass():
   def __init__(self):
      self.name = "Base"

# sub1.py:
from base import baseClass
class sub1Class(baseClass):
   def __init__(self):
      self.name = "sub1"

# sub2.py:
from base import baseClass
class sub2Class(baseClass):
   def __init__(self):
      self.name = "sub2"

# factory.py:
from sub1 import sub1Class
from sub2 import sub2Class # should not create an error
mapping = {'sub1': sub1Class, 'sub2': sub2Class}

def create(baseType):
  return mapping[baseType]

其实更好的方法可能是使用type

type(name, bases, dict) 返回一个新的类型对象。这本质上是类语句的动态形式。 name 字符串是类名,成为__name__ 属性;基元组逐项列出基类并成为__bases__ 属性; dict 字典是包含类主体定义的命名空间,并成为 __dict__ 属性。例如,以下两个语句创建相同类型的对象:

>>> class X(object):
...     a = 1
...
>>> X = type('X', (object,), dict(a=1))

为什么不将 resolve 移到管理器类中呢?查看Class factory in Python 中的域类。我不确定是否需要解析...您可以直接从self.__class__.__name__ 获取类名,并使用type()isinstance() 等python 函数来检查它们是否是特定类型。

另请查看:
Can you use a string to instantiate a class in python?
Does python have an equivalent to Java Class.forName()?

【讨论】:

  • 这只是将导入错误传播到工厂类。由于工厂类只执行from sub1 import *from sub2 import *...,而每个子类执行from base import *,因此在尝试导入 sub2 后会抛出 ImportError。试图捕获异常并传递它意味着将跳过所有其他导入。
  • 除非你把工厂类放在它自己的文件里。
  • 另一个解决方案可能是更好地控制子类,并创建一个指定字符串和派生类之间映射的字典,并将 60 个基类中的每一个显式导入工厂类。
  • 我这样做了——我的工厂类从每个子类(导入基类)中导入所有内容。同时,我在单独文件中的基类导入了工厂类。这仍然会导致循环导入。
  • 基类不需要知道工厂类。也许您可以发布一些关于它们如何交织在一起的代码?
【解决方案3】:

我不知道我是否理解你, 如果你需要子类,为什么要把它们从类中取出?然后让我们进入课堂,无论你需要什么,你都可以from class import subclass

【讨论】:

  • 我有超过 60 个子类。之前,当它们与我的基类在同一个文件中时,该文件超过 800 LOC。找到东西或多或少是不可能的。
  • 60 个子类!!它们是干什么用的?
猜你喜欢
  • 2016-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-23
相关资源
最近更新 更多