Почему __mro__ не отображается в dir(MyClass)?
class MyClass(object):
pass
print MyClass.__mro__
print dir(MyClass)
Выход:
(<class '__main__.MyClass'>, <type 'object'>)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
Почему __mro__
не указан с dir()
?
2 ответа
Из документации Python:
Поскольку dir() предоставляется в основном для удобства использования в интерактивном приглашении, он пытается предоставить интересный набор имен больше, чем пытается предоставить строго или согласованно определенный набор имен, и его подробное поведение может изменяться в разных выпусках. Например, атрибуты метакласса отсутствуют в списке результатов, когда аргумент является классом.
__mro__
является атрибутом только для чтения, который используется для определения разрешения метода в случае, если ваш класс наследует от нескольких базовых классов. Если вы хотите настроить это поведение, вы должны использовать метакласс (особый вид объекта, целью которого является создание экземпляров класса), который переопределяет mro()
метод. __mro__
остается неизменным в любом случае.
Мне недавно стало интересно то же самое. Я искал ответ более в соответствии с "Как насчет причин реализации Python mro
/__mro__
не быть перечисленным в dir
? И чему я могу доверять dir
перечислить?", чем просто" Как документация Python оправдывает невключение mro
в dir
"Мне не нравится, когда мои ожидания от поведения языка программирования не соответствуют его фактическому поведению, потому что это означает, что мое понимание языка неверно - если только это не ошибка urllib2.escape
, Так что я немного покопался, пока не разобрался с ответом.
Строка adolfopa, процитированная из документации в комментариях выше, хороша для объяснения поведения dir.
"Если объект является объектом типа или класса, список содержит имена его атрибутов и рекурсивные атрибуты его баз".
Что это значит? dir
рекурсивно собирает атрибуты из класса __dict__
и каждый из его суперклассов __dict__
,
set(dir(object)) == set(dict(object.__dict__).keys() #True
class A(object):
...
class B(object):
...
class C(B):
...
class D(C,A):
...
set(dir(D)) == set(D.__dict__.keys()) + set(C.__dict__.keys()) \
+ set(B.__dict__.keys()) + set(A.__dict__.keys()) \
+ set(object.__dict__.keys()) #True
Причина dir(object)
не перечисляет __mro__
/mro
в том, что они не являются атрибутами объекта. Они являются атрибутами type
, Каждый класс, который не определяет свой собственный __metaclass__
это пример type
, Большинство подклассов метаклассов type
, Экземпляры таких метаклассов также являются экземплярами типа. MyClass.__mro__
такой же как type.__getattribute__(MyClass,'__mro__')
,
То, как Python реализует классы, обязательно создает небольшую аномалию в отношении того, как работает dir.
Как правило, dir(MyClass) == dir(MyClass(*requiredparameters)) #True
,
Тем не мение, dir(type) == dir(type(*requiredparameters)) #False
, но единственный способ, которым это могло бы быть иначе, был, если type.__dict__
а также dir
мы одинаковы. Это, само собой разумеется, не цель dir
,
Но ждать! dir
создается рекурсивной суммой, почему мы не можем просто изменить последний кусок так, чтобы dir(object)
больше не просто object.__dict__.keys()
а скорее становится object.__dict__.keys() + type.__dict__.keys()
, Таким образом, было бы mro
/__mro__
и все другие атрибуты, которые имеет объект класса? Ах, но это будут атрибуты объекта класса, а не класса. Ну какая разница?
Рассматривать
list.__mro__ #(<type 'list'>, <type 'object'>)
В то время как
[].__mro__
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# AttributeError: 'list' object has no attribute '__mro__'
Теперь мы готовы ответить на то, на что мы можем рассчитывать dir
перечислить и на что мы можем рассчитывать dir
не перечислять. Простой ответ - тот, который мы уже рассмотрели. В нем перечислены все ключи в классе __dict__
плюс все ключи в каждом из его суперклассов __dict__
х, рекурсивно. Для экземпляра он также включает все параметры в экземпляре __dict__
, Чтобы заполнить отрицательное пространство, он не перечисляет ничего определенного в __getattr__
или в чем-либо в __getattribute__
если это не также в __dict__
, Также не перечисляются никакие атрибуты типа / метатипа.
Еще одна вещь, на которую, как мне кажется, я должен обратить внимание: ответ Дэна, который является принятым ответом на момент написания этого документа, содержит информацию, которая является неточной или, по крайней мере, вводящей в заблуждение.
Атрибуты встроенных объектов не могут быть установлены, поэтому, в некотором смысле, type.__mro__
"только для чтения", но только так же, как list.append
или type.mro
есть, в этом отношении.
MyClass.__mro__ = "Hello world!"
не вызывает ошибку. Это просто не влияет на порядок разрешения методов, определенный в type
, Таким образом, это может не дать ожидаемого эффекта, если вы пытаетесь изменить это поведение. (Что он делает, это причина MyClass(*requiredparameters).__mro__
быть "Hello World!"
что и следовало ожидать, так как определение атрибутов классов работает в python.) Вы также можете переопределить __mro__
когда вы подкласса типа для создания метакласса. Если вы не переопределяете его, он наследуется, как и все, что вы не переопределяете. (Если вы создаете метакласс, который не является подклассом типа и который не является функцией, которая возвращает экземпляр типа, вероятно, вы уже точно знаете, что делаете, и не беспокоитесь об этом, но __mro__
не будет наследоваться, так как вы не подкласс type
)
Согласно документации (описание поведения супер):
Атрибут __mro__ типа перечисляет порядок поиска разрешения метода, который используется как getattr(), так и super(). Атрибут является динамическим и может изменяться всякий раз, когда обновляется иерархия наследования.
Так что напрямую модифицируя __mro__
должен вести себя так, как вы ожидаете, во многом так же, как изменение mro
было бы. Однако, как правило, проще получить желаемое поведение, переопределив функцию. (Подумайте о том, что вам нужно сделать для правильной обработки подклассов.)