繁体   English   中英

在 Python 中的另一个类中定义一个类是否有好处?

[英]Is there a benefit to defining a class inside another class in Python?

我在这里谈论的是嵌套类。 本质上,我有两个正在建模的类。 一个 DownloadManager 类和一个 DownloadThread 类。 这里明显的 OOP 概念是组合。 然而,组合并不一定意味着嵌套,对吧?

我有看起来像这样的代码:

class DownloadThread:
    def foo(self):
        pass

class DownloadManager():
    def __init__(self):
        dwld_threads = []
    def create_new_thread():
        dwld_threads.append(DownloadThread())

但现在我想知道是否存在嵌套会更好的情况。 就像是:

class DownloadManager():
    class DownloadThread:
        def foo(self):
            pass
    def __init__(self):
        dwld_threads = []
    def create_new_thread():
        dwld_threads.append(DownloadManager.DownloadThread())

当“内部”类是一次性的时,您可能想要这样做,它永远不会在外部类的定义之外使用。 例如使用元类,有时这样做很方便

class Foo(object):
    class __metaclass__(type):
        .... 

如果您只使用一次,而不是单独定义元类。

我唯一一次使用这样的嵌套类时,我仅将外部类用作命名空间,将一堆密切相关的类组合在一起:

class Group(object):
    class cls1(object):
       ...

    class cls2(object):
       ...

然后从另一个模块中,您可以导入 Group 并将它们称为 Group.cls1、Group.cls2 等。然而,有人可能会争辩说,您可以通过使用一个模块来完成完全相同的工作(也许以一种不那么令人困惑的方式)。

我不懂 Python,但你的问题似乎很笼统。 如果它特定于 Python,请忽略我。

类嵌套是关于范围的。 如果您认为一个类仅在另一个类的上下文中才有意义,那么前者可能是成为嵌套类的好选择。

这是一种常见的模式,使辅助类成为私有的嵌套类。

嵌套类还有另一种用法,当人们想要构造其增强功能封装在特定嵌套类中的继承类时。

看这个例子:

class foo:

  class bar:
    ...  # functionalities of a specific sub-feature of foo

  def __init__(self):
    self.a = self.bar()
    ...

  ...  # other features of foo


class foo2(foo):

  class bar(foo.bar):
    ... # enhanced functionalities for this specific feature

  def __init__(self):
    foo.__init__(self)

注意,在的构造foo ,行self.a = self.bar()将构造一个foo.bar被构造的物体时实际上是一个foo对象和foo2.bar当要构造的对象对象实际上是一foo2对象。

如果类bar是在类foo之外定义的,以及它的继承版本(例如,将称为bar2 ),那么定义新类foo2会痛苦得多,因为foo2的构造foo2需要有它的第一行替换为self.a = bar2() ,这意味着重写整个构造函数。

您可以使用类作为类生成器。 喜欢(在一些袖口代码中:)

class gen(object):
    class base_1(object): pass
    ...
    class base_n(object): pass

    def __init__(self, ...):
        ...
    def mk_cls(self, ..., type):
        '''makes a class based on the type passed in, the current state of
           the class, and the other inputs to the method'''

我觉得当你需要这个功能时,你会很清楚。 如果您不需要做类似的事情,那么它可能不是一个好的用例。

这样做真的没有任何好处,除非您正在处理元类。

班级:套房真的不是你想象的那样。 这是一个奇怪的范围,它会做一些奇怪的事情。 真的连课都没有! 它只是收集一些变量的一种方式——类的名称、基类、属性的小字典和元类。

名称、字典和基数都传递给作为元类的函数,然后将其分配给 class:suite 所在作用域中的变量“name”。

你可以通过弄乱元类获得什么,实际上是通过在你的标准类中嵌套类,更难阅读代码,更难理解代码,以及在不非常熟悉为什么“类”的情况下很难理解的奇怪错误作用域与任何其他 Python 作用域完全不同。

不,构图并不意味着嵌套。 如果要在外部类的命名空间中更多地隐藏它,那么有一个嵌套类是有意义的。

无论如何,我认为在你的情况下没有任何实际的嵌套用法。 这将使代码更难以阅读(理解),并且它还会增加缩进,这会使行更短并且更容易分裂。

无论哪种方式,在类内部或外部定义,都可以。 这是一个员工工资计划程序,其中帮助类 EmpInit 嵌入在类 Employee 中:

class   Employee:

    def level(self, j):
        return j * 5E3

    def __init__(self, name, deg, yrs):
        self.name = name
        self.deg = deg
        self.yrs = yrs
        self.empInit = Employee.EmpInit(self.deg, self.level)
        self.base = Employee.EmpInit(self.deg, self.level).pay

    def pay(self):
        if self.deg in self.base:
            return self.base[self.deg]() + self.level(self.yrs)
        print(f"Degree {self.deg} is not in the database {self.base.keys()}")
        return 0

    class   EmpInit:

        def __init__(self, deg, level):
            self.level = level
            self.j = deg
            self.pay = {1: self.t1, 2: self.t2, 3: self.t3}

        def t1(self):   return self.level(1*self.j)
        def t2(self):   return self.level(2*self.j)
        def t3(self):   return self.level(3*self.j)

if  __name__ == '__main__':
    for loop in range(10):
        lst = [item for item in input(f"Enter name, degree and years : ").split(' ')]
        e1 = Employee(lst[0], int(lst[1]), int(lst[2]))
        print(f'Employee {e1.name} with degree {e1.deg} and years {e1.yrs} is making {e1.pay()} dollars')
        print("EmpInit deg {0}\nlevel {1}\npay[deg]: {2}".format(e1.empInit.j, e1.empInit.level, e1.base[e1.empInit.j]))

要在外部定义它,只需取消缩进 EmpInit 并将 Employee.EmpInit() 更改为简单的 EmpInit() 作为常规的“has-a”组合。 但是,由于 Employee 是 EmpInit 的控制器并且用户不直接实例化或与之交互,因此在内部定义它是有意义的,因为它不是一个独立的类。 另请注意,实例方法 level() 旨在在此处的两个类中调用。 因此,它也可以方便地定义为 Employee 中的静态方法,这样我们就不需要将它传递给 EmpInit,而只需使用 Employee.level() 调用它。

此功能的一个很好的用例是错误/异常处理,例如:

class DownloadManager(object):
    class DowndloadException(Exception):
        pass

    def download(self):
        ...

现在阅读代码的人知道与这个类相关的所有可能的异常。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM