Когда вы пишете это:
class B(A):
b = 2
y = b # works fine
x = a # NameError: name 'a' is not defined
x = A.a # works fine
Что Python делает, это создает новую область (хранится в словаре), выполняет все ваши определения, а затем в конце блока класса передает словарь вкласс type
(если вы не установили другой метакласс) для создания нового класса.Это примерно эквивалентно следующему:
B = type('B', (A,), classdict_from_scope)
Прежде чем перейти к этому этапу, класс B
не существует, не говоря уже о наличии базовых классов для наследования атрибутов.Учтите, что у вас может быть несколько базовых классов, и порядок разрешения имен в этих классах является сложным и зависит от их полного набора и всех их баз;этот порядок не будет определен до тех пор, пока ваш дочерний класс B
не будет фактически создан.
Это означает, что когда Python начинает выполнять x = a
, он находит a
вне области действия и не выполняет атрибутпоиск класса или экземпляра, поэтому он не может следовать протоколу разрешения имен для поиска альтернативной привязки.Поэтому все, что он может сделать, это выдать ошибку.
Так вот , почему Python работает именно так.Что вы можете с этим поделать?
У вас есть два основных варианта.
- Вы можете указать класс, в котором вы хотите явно искать атрибуты.
- Выможет искать атрибуты в вашем подклассе после его создания.
Обратите внимание, что (1) не так уж и плохо, если вы используете только одиночное наследование;вы можете просто посмотреть все атрибуты в A
;если они определены только в родительском классе A
, он все равно найдет их, поэтому вам на самом деле не нужно знать, где в иерархии они определены.
Если у вас множественное наследование, тогдавам нужно знать хотя бы, в какой иерархии содержится атрибут, который вы хотите найти.Если это сложно или невозможно, вы можете использовать опцию (2), которая будет выглядеть примерно так:
class B(A):
b = 2
y = b
B.x = B.a
Это выглядит немного уродливо, но создает идентичный класс B
, как если бывы создали x
внутри блока класса, и переходный класс B
без x
никогда не будет виден никаким другим кодом, если вы поместите присваивание B.x
непосредственно после блока класса.(Хотя может не иметь идентичных результатов, если вы используете декораторы классов)