【问题标题】:How to access class within a method in Python如何在 Python 的方法中访问类
【发布时间】:2019-08-12 23:50:48
【问题描述】:

我有一个函数make_package 返回一个类Package。在另一个文件中,我想导入类Package,所以我可以进行类型检查。我的问题是,如何导入函数内部的类?

以下不是确切的代码,但结构相似。

# project/package.py
def make_package(ori, dest):
    class Package:
        origin = ori
        destination = dest
        def __init__(self, item):
            self.item = item
        def to_str(self):
            return self.origin + '-' + self.destination + ' : ' + self.item
    return Package

# project/shipment.py
# Don't know how to import Package from package.py
class Shipment:
    def __init__(self, **kwargs):
        self.shipment = dict()
        for container in kwargs.keys():
            self.shipment[container] = list()
            for item in kwargs[key]:
                if type(item) != Package:
                    raise TypeError('Item must be packed in Package.')
                self.shipment[container].append(item.to_str())

# project/main.py
from .package import make_package
from .shipment import Shipment
us_fr = make_package('US', 'FR')
fr_cn = make_package('FR', 'CN')
shipment = Shipment(container1=[us_fr('item1'), fr_cn('item2')], container2=[us_fr('item3'), fr_cn('item4')])
print(shipment.shipment)
# {
#     'container1' : [
#         'US-FR : item1',
#         'FR-CN' : 'item2'
#     ],
#     'container2' : [
#         'US-FR : item3',
#         'FR-CN' : 'item4'
#     ]
# }

我知道实现类型检查的一种方法是在Shipment 中使用make_package 创建一个虚拟 变量,然后将type(item)type(dummy) 进行比较。然而,它似乎更像是一个黑客。不知道有没有更好的办法?

【问题讨论】:

  • 为什么类在函数里面?为什么不将其留在全局范围内?你想通过把它放在函数中来实现什么?
  • 最好的解决方案是将Package移到函数之外。除非您将来打算将make_package 用作工厂函数,否则您甚至不需要该函数。
  • 因为我希望类从函数中继承 oridest 值,但只使用 item 创建对象。这样我可以多次重用变量,例如us_fr,因为我以后会更多地使用它。如果我把课程放在外面,我可能每次都必须传递 3 个参数__init__(ori, dest, item)。后来使代码变得超级混乱。
  • @KevinWelch 在我的真实代码中,有更多的类变量Package 继承自make_package 的参数。如果我每次都使用所有这些参数创建 Package 对象,它会变得非常混乱。我觉得如果我可以先使用make_package 分配这些参数,然后只在创建新实例时传入item,那会更干净,因为item 是唯一每次都不同的变量。
  • @Phoenix-C: 会使用functools.partial 绑定公共参数以使只需要item 的构造函数工作吗?这将使Package 保持为单例,同时仍然让您拥有使用通用数据构造它的简化接口。

标签: python python-3.x nested class-factory


【解决方案1】:

没有办法从函数外部“导入”你的类,因为实际上没有Package 类型。每次调用make_package() 时,它创建了一个新类型,它恰好与所有其他Package 类型具有相同的名称。但是,它仍然是一个独特的类型,因此它永远不会与另一个 Package 类型进行比较,即使 origindestination 相同。

您可以让所有Package 类型都继承自“标记”类,然后使用isinstance 来检查项目是否为包:

# project/package.py
class PackageMarker:
    pass

def make_package(ori, dest):
    class Package(PackageMarker):
        ...  # Package body here
    return Package

# project/shipment.py
class Shipment:
    def __init__(self, **kwargs):
        self.shipment = dict()
        for container in kwargs.keys():
            self.shipment[container] = list()
            for item in kwargs[key]:
                if not isinstance(item, PackageMarker):
                    raise TypeError('Item must be packed in Package.')
                self.shipment[container].append(item.to_str())

在我看来,您使用此工厂函数创建类的方式令人困惑,但也许我只是需要更多上下文来了解您为什么要这样做。

如果你想知道我将如何重构它,我会删除工厂函数,并执行以下操作:

# project/package.py
class Package:
    def __init__(self, route, item):
        self.route = route
        self.item = item

    def to_str(self):
        return self.route.to_str() + ' : ' + self.item

# project/route.py
class Route:
    def __init__(self, origin, destination):
        self.origin = origin
        self.destination = destination

    def to_str(self):
        return self.origin + ' - ' + self.destination

# project/shipment.py
from .package import Package

class Shipment:
    def __init__(self, **kwargs):
        self.shipment = dict()
        for container in kwargs.keys():
            self.shipment[container] = list()
            for item in kwargs[key]:
                if not isinstance(item, Package):
                    raise TypeError('Item must be packed in Package.')
                self.shipment[container].append(item.to_str())

# project/main.py
from .package import Package
from .route import Route
from .shipment import Shipment
us_fr = Route('US', 'FR')
fr_cn = Route('FR', 'CN')
shipment = Shipment(
    container1=[Package(us_fr, 'item1'), Package(fr_cn, 'item2')],
    container2=[Package(us_fr, 'item3'), Package(fr_cn, 'item4')]
)

【讨论】:

  • 所以我写这个包是为了我的公司内部使用(所以把main.py当作测试脚本)。我在外面没有父类的原因是因为这样,其他人可以编写一个扩展PackageMarker 的自定义类,它会绕过类型检查。我真的很想将类型限制为仅Package。我愿意接受重写整个内容的建议。
  • @Phoenix-C 鉴于此,我添加了一个可能的实现,它更彻底地改变了您的原始设计。在我看来,这个版本就我们在 OOP 中使用的传统建模而言更容易理解。
猜你喜欢
  • 1970-01-01
  • 2020-11-06
  • 2022-10-14
  • 2010-10-16
  • 1970-01-01
  • 2020-03-15
  • 1970-01-01
相关资源
最近更新 更多