Область вложенных классов?

Я пытаюсь понять область применения вложенных классов в Python. Вот мой пример кода:

class OuterClass:
    outer_var = 1
    class InnerClass:
        inner_var = outer_var

Создание класса не завершено, и я получаю ошибку:

<type 'exceptions.NameError'>: name 'outer_var' is not defined

Попытка inner_var = Outerclass.outer_var не работает. Я получаю:

<type 'exceptions.NameError'>: name 'OuterClass' is not defined

Я пытаюсь получить доступ к статическому outer_var из InnerClass.

Есть ли способ сделать это?

Ответы

Ответ 1

class Outer(object):
    outer_var = 1

    class Inner(object):
        @property
        def inner_var(self):
            return Outer.outer_var

Это не совсем то же самое, что аналогичные вещи работают на других языках и использует глобальный поиск вместо того, чтобы просматривать доступ к outer_var. (Если вы измените объект, к которому привязано имя Outer, тогда этот код будет использовать этот объект при следующем его выполнении.)

Если вы хотите, чтобы все объекты Inner имели ссылку на Outer, потому что outer_var действительно является атрибутом экземпляра:

class Outer(object):
    def __init__(self):
        self.outer_var = 1

    def get_inner(self):
        return self.Inner(self)
        # "self.Inner" is because Inner is a class attribute of this class
        # "Outer.Inner" would also work, or move Inner to global scope
        # and then just use "Inner"

    class Inner(object):
        def __init__(self, outer):
            self.outer = outer

        @property
        def inner_var(self):
            return self.outer.outer_var

Обратите внимание, что классы вложенности несколько необычны в Python и не подразумевают автоматически какие-либо особые отношения между классами. Тебе лучше не гнездиться. (Вы все равно можете установить атрибут класса на Outer до Inner, если хотите.)

Ответ 2

Я думаю, вы можете просто сделать:

class OuterClass:
    outer_var = 1

    class InnerClass:
        pass
    InnerClass.inner_var = outer_var

Проблема, с которой вы столкнулись, связана с этим:

Блок представляет собой часть текста программы Python, которая выполняется как единое целое. Ниже приведены блоки: модуль, тело функции и класс определение.
(...)
Область определяет видимость имени внутри блок.
(...)
Объем имен, определенных в блоке класса, ограничивается блоком класса; он не распространяется на кодовые блоки методы - это включает выражения генератора, так как они реализован с использованием области функций. Это означает, что не в состоянии:

   class A:  

       a = 42  

       b = list(a + i for i in range(10))

http://docs.python.org/reference/executionmodel.html#naming-and-binding

Вышеуказанное означает:
тело функции является блоком кода, а метод является функцией, тогда имена, определенные из тела функции, присутствующие в определении класса, не распространяются на тело функции.

Перефразируя это для вашего случая:
определение класса является кодовым блоком, тогда имена, определенные из определения внутреннего класса, присутствующие во внешнем определении класса, не распространяются на определение внутреннего класса.

Ответ 3

Вам может быть лучше, если вы просто не используете вложенные классы. Если вы должны вложить, попробуйте следующее:

x = 1
class OuterClass:
    outer_var = x
    class InnerClass:
        inner_var = x

Или объявите оба класса перед их вложением:

class OuterClass:
    outer_var = 1

class InnerClass:
    inner_var = OuterClass.outer_var

OuterClass.InnerClass = InnerClass

(После этого вы можете del InnerClass, если вам нужно.)

Ответ 4

Самое простое решение:

class OuterClass:
    outer_var = 1
    class InnerClass:
        def __init__(self):
            self.inner_var = OuterClass.outer_var

Это требует от вас быть явным, но не требует больших усилий.

Ответ 5

В Python изменяемые объекты передаются как ссылка, поэтому вы можете передать ссылку внешнего класса во внутренний класс.

class OuterClass:
    def __init__(self):
        self.outer_var = 1
        self.inner_class = OuterClass.InnerClass(self)
        print('Inner variable in OuterClass = %d' % self.inner_class.inner_var)

    class InnerClass:
        def __init__(self, outer_class):
            self.outer_class = outer_class
            self.inner_var = 2
            print('Outer variable in InnerClass = %d' % self.outer_class.outer_var)

Ответ 6

Все объяснения можно найти в документации Python. Учебное пособие по Python

Для вашей первой ошибки <type 'exceptions.NameError'>: name 'outer_var' is not defined. Объяснение:

Нет никаких сокращений для привязки атрибутов данных (или других методов!) из методов. Я нахожу, что это на самом деле увеличивает читаемость методов: нет возможности запутать локальные переменные и переменные экземпляра при просмотре метода.

приведенный в The Python Tutorial 9.4

Для вашей второй ошибки <type 'exceptions.NameError'>: name 'OuterClass' is not defined

Когда определение класса остается в норме (через конец), создается объект класса.

приведенный в учебнике по Python 9.3.1

Итак, когда вы пытаетесь inner_var = Outerclass.outer_var, Quterclass еще не создан, поэтому name 'OuterClass' is not defined

Более подробное, но утомительное объяснение вашей первой ошибки:

Хотя классы имеют доступ к охватывающим областям функций, хотя они не действуют как охватывающие области для кода, вложенные в класс: Python ищет закрывающие функции для ссылочных имен, но никогда никаких закрывающих классов. То есть, класс является локальной областью и имеет доступ к закрывающим локальным областям, но он не служит закрывающей локальной областью для дальнейшего вложенного кода.

цитируется в Learning.Python(5th).Mark.Lutz