【问题标题】:Issue with inner class scopes内部类范围的问题
【发布时间】:2020-04-26 01:42:07
【问题描述】:

所以我有一个类结构,其中包含多个级别的内部类嵌套,用于命名空间。这与将所有东西都放平没有什么不同,但这种方式更加简洁直观。基本上,我们使用内部类来更好地组织代码。

我遇到了一些问题:

我只是发布一些示例代码而不是试图解释它们:

1)

class Namespace1:
    class Class1:
        pass  # base methods go here

        class Subclass1(Namespace1.Class1):
            pass  # extended methods go here

        class Subclass2(Namespace1.Class1):
            pass  # extended methods go here

        class Subclass3(Namespace1.Class1):
            pass  # extended methods go here

    class Class2:
        pass  # base methods go here

        class Subclass1(Namespace1.Class2):
            pass  # extended methods go here

        # etc.

这里的子类化问题是当你尝试声明Namespace1.Class1.Subclass1Namespace1.Class1 还不存在,所以它不起作用。我假设可能没有办法解决这个问题,但我提到这一点以防万一我不知道有什么方法可以实现。

所以我接下来尝试的是:

class Namespace1:
    class Class1:
        pass  # base methods go here

    class Class1Subclass1(Class1):
        pass  # extended methods go here

    class Class1Subclass2(Class1):
        pass  # extended methods go here

    class Class1Subclass3(Class1):
        pass  # extended methods go here

    class Class2:
        pass  # base methods go here

    class Class2Subclass1(Class2):
        pass  # extended methods go here

        # etc.

这很有效,但我真的不喜欢它有几个原因:

  • 我们现在得到子类之间的命名空间冲突,所以我们不能再调用它们(Subclass1Subclass2,...),现在必须调用它们(Class1Subclass1Class1Subclass2, ...)
  • 我们现在已经失去了使层次结构在视觉上明确的额外缩进级别,一旦嵌套 4 层深度并且类名开始变得像 6 个单词一样长,这会很糟糕

所以我想到了这样做:

2)

class Namespace1:
    class Class1:
        pass  # base methods go here

    class Class1_:
        class Subclass1(Class1):
            pass  # extended methods go here

        class Subclass2(Class1):
            pass  # extended methods go here

        class Subclass3(Class1):
            pass  # extended methods go here

    class Class2:
        pass  # base methods go here

    class Class2_:
        class Subclass1(Class2):
            pass  # extended methods go here

        # etc.

这不是 100% 理想的(最好的方法是上面的示例 1),但理论上应该可以做到。当解释器开始创建Namespace1.Class1_.Subclass1 时,Class1 应该已经存在。我遇到的问题是我不知道如何引用它,因为我不能只将它引用为Namespace1.Class1(因为Namespace1 尚不存在),而且我超出了范围将其简单地引用为Class1

有人知道我会怎么做吗?

在有人告诉我只使用模块层次结构来实现这一点之前:我知道这是一个选项,但我更愿意将它全部放在一个文件中,以便对其进行单一概述,因为这些类中的每一个只有大约 3-5 行长(仅通过单个方法覆盖不同)。对于这个特定的用例,最好在一个地方看到所有内容,并且我得到的实际代码有 3-4 级缩进,其中一些类有 12 个以上的子类。浏览超过 100 个文件(每个文件可能有 10 行长)将是一场噩梦。

【问题讨论】:

  • 只是一个想法:每个类需要是自己的全局名称吗?您可以在容器中存储对其他匿名类的引用。
  • 模块本身已经是一个命名空间。并且嵌套类定义(通常)被认为是非常不符合 Python 的(Python 之禅:“扁平比嵌套更好”)。注意:我不建议每个类使用一个模块(这确实是一个 PITA,并且至少与嵌套类一样 unpythonic),只是不要以这种方式嵌套你的子类。
  • 另外,你有这么多子类“只有一个方法覆盖不同”的事实对我来说有点设计味道。当然,如果不了解更多关于真实上下文和用例的信息,就不可能说出来,但也许像策略模式这样的东西可能更合适?

标签: python python-3.x class oop namespaces


【解决方案1】:

嵌套类存储为外部类的属性,因此您可以使用类装饰器,而不是直接在其声明中指定每个嵌套类的基类(由于已经描述的原因,您不能这样做)在外部类上通过调用 type 函数将这些属性转换为外部类的子类,并将外部类作为基类添加到每个内部类:

def nested_subclasses(cls):
    for name, obj in vars(cls).items():
        if isinstance(obj, type):
            setattr(cls, name, type(name, (cls,), dict(vars(obj))))
    return cls

这样:

class Namespace1:

    @nested_subclasses
    class Class1:
        def foo(self):
            print('bar')

        class Subclass1:
            pass

Namespace1.Class1.Subclass1().foo()

输出:

bar

【讨论】:

  • 嗨,谢谢你的回答!这是一个很酷的方法。我从来没有真正使用过类装饰器,而且它们看起来非常有用。我对这个解决方案的主要问题是,像 Jedi 这样的代码自省引擎,以及 linter 和 IDE 在装饰器/元类/动态方面存在限制,并且无法理解这一点。因此,这些类的所有使用都会在整个代码中为您提供未解决的引用错误。您是否还有其他不涉及元编程的想法?
猜你喜欢
  • 2012-06-20
  • 1970-01-01
  • 2016-06-23
  • 1970-01-01
  • 2015-01-30
  • 1970-01-01
  • 2018-11-10
  • 2011-04-18
  • 2023-03-30
相关资源
最近更新 更多