Объекты класса имеют атрибут __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 не любит, когда имена волшебным образом появляются из ниоткуда.
Это потому, что он просто еще не создан.
Рассмотрим следующее:
>>> 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 __module__
и __qualname__
- это только текстовая информация, предназначенная для помощи в отладке - они не влияют на свойства (в самом общем определении) конечного class
объекта. bases
OTHO влияет на результирующий класс огромный...
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__
?
Если я правильно понимаю, ваше объяснение в основном сводится к тому, что «метакласс может изменить базовые классы, и тогда люди могут быть сбиты с толку, к чему на самом деле относятся __base__
и __bases__
»?
@Aran-Fey да, в этом действительно суть - особенно если учесть, что если «исходное» значение __bases__
было выставлено таким образом, использование __bases__[0]
вместо жестко запрограммированного имени класса не приносит многого с точки зрения удобочитаемости и удобства обслуживания (это сделает код немного СУХИМ, но это единственное, что вы получите - по крайней мере, единственное, что я могу придумать).
NB: чтобы внести ясность, я не говорю, что это наилучший возможный дизайн или что вы должны согласиться с этим выбором — просто он имеет смысл в контексте модели исполнения Python и согласуется с философией Python.
Справедливо. Я не думаю, что это хороший аргумент (и, что более важно, я действительно не уверен, что кто-то из разработчиков серьезно обдумывал это), но у вас есть мой голос.
@Aran-Fey, возможно, в последнее время в экосистеме Python многое изменилось с уходом BDFL, но, наблюдая за эволюцией языка с 1.5.2 дней, я могу сказать вам, что основная команда исторически известна тем, что очень серьезно думала над выбор дизайна.
Я не уверен, что это так... В конце концов, такие вещи, как
__module__
и__qualname__
, волшебным образом появляются как локальные переменные в теле класса.