Почему __bases__ недоступен в теле класса?

Объекты класса имеют атрибут __bases____base__):

>>> class Foo(object):
...     pass
... 
>>> Foo.__bases__
(<class 'object'>,)

К сожалению, эти атрибуты недоступны в теле класса, что было бы очень удобно для доступа к атрибутам родительского класса без жесткого кодирования имени:

class Foo:
    cls_attr = 3

class Bar(Foo):
    cls_attr = __base__.cls_attr + 2
    # throws NameError: name '__base__' is not defined

Есть ли причина, по которой __bases__ и __base__ недоступны в теле класса?

(Для ясности, я спрашиваю, является ли это сознательным проектным решением. Я не спрашиваю о реализации; я знаю, что __bases__ является дескриптором в type и что к этому дескриптору нельзя получить доступ, пока объект класса не будет Я хочу знать, почему python не создает __bases__ как локальную переменную в теле класса.)

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
6
0
294
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я не отвечаю, почему было решено реализовать именно так, я отвечаю, почему это не было реализовано как "локальная переменная в теле класса":

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

Я не уверен, что это так... В конце концов, такие вещи, как __module__ и __qualname__, волшебным образом появляются как локальные переменные в теле класса.

Aran-Fey 27.05.2019 09:50

Это потому, что он просто еще не создан.

Рассмотрим следующее:

>>> class baselessmeta(type):
...     def __new__(metaclass, class_name, bases, classdict):
...         return type.__new__(
...             metaclass,
...             class_name,
...             (), # I can just ignore all the base
...             {}
...         )
...
>>> class Baseless(int, metaclass=baselessmeta):
...     # imaginary print(__bases__, __base__)
...     ...
...
>>> Baseless.__bases__
(<class 'object'>,)
>>> Baseless.__base__
<class 'object'>
>>>

К чему должен привести воображаемый отпечаток? Каждый класс Python так или иначе создается с помощью метакласса type. У вас есть аргумент int для аргумента type() in bases, но вы не знаете, каким будет возвращаемое значение. Вы можете использовать это непосредственно в качестве базы в своем метаклассе или можете вернуть другую базу с вашим LOC.

Только что понял вашу часть to be clear, и теперь мой ответ бесполезен, ха-ха. О, привет.

Ну, конечно, класс еще не существует, но python может просто установить __base__ на то, что вы написали в круглых скобках после имени класса ((int,) в вашем примере). Python делает создает __module__ и __qualname__ как локальные переменные, и обе они могут быть изменены или проигнорированы метаклассом, как и __bases__. Во всяком случае, я ожидаю, что воображаемый вывод выведет (int,) int - он действительно не может выводить ничего другого, потому что, как вы сказали, __new__ метакласса еще не был выполнен в этот момент.

Aran-Fey 27.05.2019 09:57

@Aran-Fey __module__ и __qualname__ - это только текстовая информация, предназначенная для помощи в отладке - они не влияют на свойства (в самом общем определении) конечного class объекта. bases OTHO влияет на результирующий класс огромный...

bruno desthuilliers 27.05.2019 10:25
Ответ принят как подходящий

I want to know why python doesn't create __bases__ as a local variable in the class body

Как вы знаете, class в основном является сокращением для type.__new__() - когда среда выполнения сталкивается с операторами class, она выполняет все операторы на верхнем уровне тела class, собирает все полученные привязки в выделенном словаре пространства имен, вызывает type() с конкретным метаклассом. , имя класса, базовые классы и пространство имен dict, и привязывает полученный объект класса к имени класса в охватывающей области (обычно, но не обязательно, в пространстве имен модуля верхнего уровня).

Важным моментом здесь является то, что метакласс несет ответственность за создание объекта класса, а чтобы разрешить настройку создания объекта класса, метакласс должен быть свободен делать со своими аргументами все, что он хочет. Чаще всего пользовательский метакласс будет в основном работать с attrs dict, но он также должен уметь работать с bases аргументом. Теперь, поскольку метакласс вызывается только ПОСЛЕ выполнения операторов тела класса, среда выполнения не может надежно раскрыть bases в области тела класса, поскольку эти базы могут быть впоследствии изменены метаклассом.

Здесь также есть несколько более философских соображений, особенно относительно явного или неявного, и, как упоминает shx2, разработчики Python стараются избегать появления магических переменных на ровном месте. Действительно, есть пара переменных реализации (__module__ и, в py3, __qualname__), которые «автоматически» определяются в пространстве имен тела класса, но это просто имена, в основном предназначенные для дополнительной информации об отладке/проверке для разработчиков) и не имеют абсолютно никакого влияния. ни о создании объекта класса, ни о его свойствах, поведении и многом другом.

Как всегда с Python, вы должны учитывать весь контекст (модель выполнения, объектную модель, то, как различные части работают вместе и т. д.), чтобы действительно понять варианты дизайна. Согласны ли вы со всем дизайном и философией — это еще один спор (и он здесь неуместен), но вы можете быть уверены, что да, эти варианты являются «сознательные дизайнерские решения».

Я не уверен, что следую вашему аргументу «__bases__ имеет огромное влияние на класс». Так что, если это так? Как это причина не делать его доступным в теле класса? Как это делает жесткое кодирование родительского класса лучше, чем обращение к нему по имени __base__?

Aran-Fey 27.05.2019 10:31

Если я правильно понимаю, ваше объяснение в основном сводится к тому, что «метакласс может изменить базовые классы, и тогда люди могут быть сбиты с толку, к чему на самом деле относятся __base__ и __bases__»?

Aran-Fey 27.05.2019 10:45

@Aran-Fey да, в этом действительно суть - особенно если учесть, что если «исходное» значение __bases__ было выставлено таким образом, использование __bases__[0] вместо жестко запрограммированного имени класса не приносит многого с точки зрения удобочитаемости и удобства обслуживания (это сделает код немного СУХИМ, но это единственное, что вы получите - по крайней мере, единственное, что я могу придумать).

bruno desthuilliers 27.05.2019 10:53

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

bruno desthuilliers 27.05.2019 10:57

Справедливо. Я не думаю, что это хороший аргумент (и, что более важно, я действительно не уверен, что кто-то из разработчиков серьезно обдумывал это), но у вас есть мой голос.

Aran-Fey 27.05.2019 10:57

@Aran-Fey, возможно, в последнее время в экосистеме Python многое изменилось с уходом BDFL, но, наблюдая за эволюцией языка с 1.5.2 дней, я могу сказать вам, что основная команда исторически известна тем, что очень серьезно думала над выбор дизайна.

bruno desthuilliers 27.05.2019 11:14

Другие вопросы по теме