外部类如何从其内部类访问数据?

时间:2018-02-20 21:59:30

标签: python-3.x class oop subclass

我正在尝试使用基于this question/answer的非常简单的测试用例来更好地理解如何使用子类。

class Outer():

    def __init__(self, x):
        self.x = super(Inner, self).__init__
        # self.x = Inner(**kwargs)

    def add_two(self):
        """ """
        return self.x + 2

    class Inner():

        def __init__(self, x=2):
            self.x = x

res = Outer(x=3).add_two()
# res = Outer({'x' : 3}).add_two()
print(res)
>> NameError: name 'Inner' is not defined

如果我运行相同的代码但使Inner()成为自己独立的类(而不是Outer()的子类,则会收到以下错误。

TypeError: super(type, obj): obj must be an instance or subtype of type

此错误的原因是什么?如何解决此问题?

1 个答案:

答案 0 :(得分:1)

用Python(或其他语言)嵌套类很少有意义。在这种情况下,它根本没用。

如果在"外面"你希望有一个关联的" Inner"的实例,应该在外部的__init__方法上创建为实例属性 - 就像这样:

class Outer():

    def __init__(self, x):
        self.x = Inner(x)
        # self.x = Inner(**kwargs)

    def add_two(self):
        """ """
        return self.x + 2


class Inner():

    def __init__(self, x=2):
        self.x = x

现在,一步一步地查看原始代码,并尝试更好地理解为什么它不起作用: 在Python中,在类的主体中声明的所有内容都成为该类的一个属性 - 它的单个副本(通常)将由该类的所有实例共享。声明一个嵌套的整个类在语法上是合法的,但你什么也得不到:内部类不是"隐藏"在任何意义上来自外部世界的语言:不是语言,也不是Python开发人员通常遵循的惯例。

如果您希望用户(即其他程序员或您自己使用此文件的代码)创建" Outer"并且不要创建" Inner"的实例,只需在其名称前添加_。这是Python代码中的一种约定,开发人员通常会知道他们不应该信任以单个_开头的任何类,函数或其他名称,以便在第三方代码中安全使用 - 这是最接近的Python进入"私有"或者"受保护的"成员。

现在,走到界限:

    ...
    self.x = super(Inner, self).__init__

这再次毫无意义。 super或显式引用超类意味着调用超类 - 即您继承的类。您在代码中没有创建继承关系,而是在组合中创建了一个继承关系。这就是您收到错误消息的原因 - 如果您使用的是super的显式版本,则您传递的对象必须是您正在调用super的类的子类。而事实并非如此。 (另外,以这种形式进行,它不会调用方法 - 只是引用它 - 所有函数或方法调用都是通过使用()来调用的)

你也可以从内部进行外部继承 - 在这种情况下,外部将会"成为"一个内部,不需要在一个属性中保持对它的引用 - self将意味着一个外层和一个内部类。 在这种情况下,我们需要引用" Inner"在解析" Outer"的声明时,需要先定义它:

class _Inner():

    def __init__(self, x=2):
        self.x = x

class Outer(_Inner):

    def __init__(self, x):
        super().__init__(x)

    def add_two(self):
        """ """
        return self.x + 2

请注意使用无参数super - Python 3的主要更改之一。如果您需要编写仍与Python 2兼容的代码,则super的参数可以是显式的,并且调用将是{{ 1}}。

(再次,调用它super(Outer, self).__init__()将意味着您的类的用户不应该继承或实例化_Inner并且应该使用_Inner - 这是编码风格的惯例而不是语言语法)