【问题标题】:In nested classes, how to access outer class's elements from nested class in Python?在嵌套类中,如何从 Python 的嵌套类中访问外部类的元素?
【发布时间】:2012-12-10 09:23:00
【问题描述】:

我有这种情况,我需要让一个嵌套类将项目附加到外部类的列表中。这是类似于我试图做的伪代码。我该如何让它发挥作用?

class Outer(object):
  outerlist = []
  class Inner(object):
    def __call__(self, arg1):
      outerlist.append(arg1)

if __name__ == "__main__":
  f = Outer()
  f.Inner("apple")
  f.Inner("orange")

  print f.outerlist()

这是我希望看到的——apple, orange

详情:
OS X、Python 2.7

【问题讨论】:

  • 为什么要使用嵌套类
  • 所以我试图让 Outer 类包含将接受参数的装饰器,最好的方法是将装饰器写成类stackoverflow.com/questions/10610824/…
  • 是的,使用类作为装饰器是个好主意,但这并不意味着您需要使用嵌套类。你想解决什么问题?
  • @sri:是的,装饰器应该是一个类,我同意。但是你装饰的类应该是一个完全独立的类。没有嵌套。
  • 一些附带问题:我假设您想要print f.outerlist,而不是f.outerlist(),因为您不能调用list。此外,虽然您可以通过实例访问类变量,但通过类访问它们通常不会令人困惑,例如print Outer.outerlist。 (你通过f 访问它的事实意味着我不得不问:你真的想要一个类变量吗?)

标签: python class nested


【解决方案1】:

为什么不将外部类实例作为参数传递给内部类方法呢?您可以通过这种方式访问​​外部类实例的成员。

  class Outer(object):
    outerlist = []
    class Inner(object):
      def __init__(self, outer_self):
        self.outer = outer_self
      def __call__(self, arg1):
        self.outer.outerlist.append(arg1)

 if __name__ == "__main__":
   f = Outer()
   i = f.Inner(f)
   i("apple")
   i("orange")

   print f.outerlist

【讨论】:

    【解决方案2】:

    这将产生预期的结果。

    注意__new__ 的使用(A 行)。这意味着 Inner 不会正确地做任何构造工作,而是附加列表。此外,要访问包含类的类属性,只需使用外部类的名称(B 行)。

    最后见 C 行。列表不是函数。您的原始代码中有一些额外的括号。

    class Outer(object):
        outerlist = []
        class Inner(object):
            def __new__(self, arg1):                #A
                Outer.outerlist.append(arg1)        #B
    
    f = Outer()
    f.Inner("apple")
    f.Inner("orange")
    print f.outerlist                               #C
    

    【讨论】:

    • 所以每次调用__new__ 时都会调用__init____call__ 吗?这很整洁。
    • @sri:不是每次都执行__call__——这是一个实例方法,可以多次调用。当类需要创建一个实例时,它会被调用一次——通常是在 __init__ 之前。
    • @Sheena:它们是“类变量”。 “静态”一词指的是既不与类也不与实例相关联的事物,而“属性”是一种特殊的类变量,其描述符让它们像实例变量一样工作。但除此之外,一切都是为了钱。
    【解决方案3】:

    您只能使用完整的全局名称访问外部类:

    class Outer(object):
        outerlist = []
        class Inner(object):
            def __call__(self, arg1):
                Outer.outerlist.append(arg1)
    

    在python中,一般来说无论如何都不需要嵌套类。在某些用例中,在函数中定义类是有意义的(使用作用域变量),但很少需要嵌套类。

    【讨论】:

    • 这基本上是对的,但是使用__call__ 意味着你必须写Outer.Inner()("apple") 而不仅仅是Outer.Inner("apple")。错误出在 OP 的代码中,而不是您的,但仍需要更正。
    • @abarnert:OP 几乎不知道需要实现什么,所以我将推迟讨论如何调用它,直到明确真正的目标是什么..
    • @MartijnPieters 对不起。我手里有一只不安分的宠物——因此延误了,这涉及到家庭作业——因此含糊不清。我确实在问题下回复了您的评论。请检查。我很乐意进一步解释。谢谢。
    • @sri:如果这涉及到家庭作业,你能告诉我们这是作业的哪一部分,哪一部分是你自己想出来的?在我看来,有一种更简单、更 Python 的方式来构建你的老师想要的东西。 (这并不能保证——毕竟有些老师是疯子……但值得一试。)
    【解决方案4】:

    现在我理解了你的设计,但你做的事情都错了。

    首先,Outer 是一个类;它没有得到__call__ed。您的第 17 行只是要构造一个空的 Outer 对象,并且什么也不做。如果你想“调用”Outer 对象,你可以定义一个__init__ 方法——或者,正如Sheena 所建议的,定义一个__new__ 并拦截初始化,因为你实际上并不需要初始化的对象。

    老实说,我不认为不了解__call__ 工作原理的人应该尝试构建像这样棘手的东西。但如果你坚持,请继续阅读。

    在一个类而不是一个实例中收集这种东西是一个非常奇怪,而且可能很糟糕的设计。请记住,类变量实际上是全局变量,包含所有这些。如果您尝试使用Outer 可重入,或者从多个线程/事件处理程序/greenlets/whatever 中使用,那么这些使用最终会相互踩踏。即使您认为现在这可能不会成为问题,但它可能会在未来的某个时候成为问题。

    您可以创建一个Outer 实例,并将其成员用作装饰器。例如:

    from outer_library import Outer
    
    outer = Outer()
    
    @outer.get("/")
    …
    

    但我不确定这是否更好。这里的整个设计似乎涉及在模块级别执行操作,即使看起来您只是定义普通函数并在最后调用一个函数。您设法让自己感到困惑的事实应该证明这是多么令人困惑的设计。

    但是如果您确实想要这样做,您可能想要做的是在Outer.__init__ 方法中定义类,并将它们分配给实例成员。类是一等值,可以像任何其他值一样分配给变量。然后,使用这些类的__init__(或__new__)方法来完成您想要的工作,让这些类模拟函数。

    这可能看起来令人困惑或具有误导性。但是请记住,您尝试做的全部重点是以一种看起来像方法的方式使用类,因此这种混淆是问题固有的。但是,如果您愿意,您可以将装饰器编写为函数(在这种情况下,作为Outer 的普通实例方法);它只是让问题的不同部分变得更难,而不是这部分。

    设计这样的东西的更正常的方法是使Outer 成为一个完全正常的类,人们可以对其进行子类化或创建实例,并提供一种显式的非花哨方式来将处理程序方法或函数附加到 URL。然后,一旦它起作用,设计一种方法来简化使用装饰器注册处理程序。

    【讨论】:

    • 点了。 (__main__ 部分中的 pastebin 代码是错误的 - 我的错,)我不应该在 atm 上谈论我的项目,但我只想说,它应该是一个单例并且应该是可调用的,它已正确实例化在我的代码中。很抱歉不能把事情说得更清楚,下次我会努力的。但至少就我的问题而言,它已经得到了回答。谢谢。 :)
    【解决方案5】:

    我认为 picmate 指出的选项(将外部类作为内部类的输入传递)对于这个特定问题是一个不错的选择。请注意,它也可以在不必“嵌套”类的情况下工作(在我看来,这里要避免的事情,因为它是不必要的)。请注意,我还在外部类中嵌入了内部类的初始化(此步骤并非严格需要,但出于说明目的,我将其包含在此处):

    class Outer(object):
      outerlist = []
      def __init__(self):
        self.Inner=Inner(Outer=self)
      def get_basket(self):
        print "Outer basket: %s" %(self.outerlist)
    
    class Inner(object):
      def __init__(self,Outer):
        self.Outer=Outer
      def __call__(self, arg1):
        self.Outer.outerlist.append(arg1)
      def get_basket(self):
        print "Inner basket: %s" %(self.Outer.outerlist)
    
    if __name__ == "__main__":
      f = Outer()
      print "Initial state"
      f.get_basket()
      f.Inner.get_basket()
      f.Inner("apple")
      f.Inner("orange")
      print "Final state"
      f.get_basket()
      f.Inner.get_basket()
    

    【讨论】:

      猜你喜欢
      • 2017-01-25
      • 1970-01-01
      • 2014-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-30
      • 2012-01-17
      • 1970-01-01
      相关资源
      最近更新 更多