Как вы выразились, и обнаружил на практике A.method
является не на цепочке поиска Б - отношение классов и метаклассов не один наследование - это один из «экземпляров» как класс является экземпляром метакласса.
Python - прекрасный язык, на котором он ведет себя в ожидании, с очень небольшим количеством сюрпризов, - и в этих обстоятельствах это одно и то же: если бы мы имели дело с «обычными» объектами, ваша ситуация была бы такой же, как и примерB
со значением A
. И B.method
будет присутствовать в B.__dict__
- вызов «супер», помещенный в метод, определенный для такого экземпляра, никогда не сможет добраться до A.method
- это фактически приведет к ошибке. Поскольку B
- объект класса, super
внутри метода в B's __dict__
имеет смысл - но он будет искать цепочку классов B __mro__
(в данном случае (object,)
) - и это то, что вы нажмете.
Такое положение не должно происходить часто, и я даже не думаю, что это должно произойти вообще; семантически очень сложно найти какой-либо смысл в методе, который имел бы смысл как метаклассовый метод, так и как метод самого класса. Более того, если method
не переопределено в B
, обратите внимание, что оно не будет даже видимым (и не вызываемым) из экземпляров B.
Возможно, ваш дизайн должен:
a. есть BaseClass Base
, используя свой метакласса A
, который определяет method
вместо того, чтобы он определил в A
- а затем определить class B(Base):
вместо
б.Или метаклассом A
достаточно впрыснуть method
в каждом class
он создает, с кодом для этого в его __init__
или __new__
метод - вместе:
def method(cls):
m = "this is an injected method of '%s'"
print(m % cls.__name__)
class A(type):
def __init__(cls, name, bases, dct):
dct['method'] = classmethod(method)
Это будет мой предпочтительный подход - но это не позволяет один переопределить этот method
в классе, который использует этот метакласс - без какой-либо дополнительной логики там, вышеприведенный подход скорее переопределит любой такой method
явный в теле. Чем проще вещь, чтобы иметь базовый класс Base
, как указано выше, или придать метод с другим именем, как base_method
на выпускном классе, и жёстко призывает к нему в любой наиважнейшей method
:
class B(metaclass=A):
@classmethod
def method(cls):
cls.base_method()
...
(Использование дополнительный if
на метаклассом-х __init__
так, что по умолчанию method
является псевдонимом base_method
)
что вы буквально просили здесь начинается
Теперь, если у вас действительно есть прецедент для методов в метаклассе, который должен быть вызван из класса, «один и очевидный» способ состоит в простом кодировании вызова, как это было сделано до существования super
Вы можете сделать что-либо:
class B(metaclass=A):
@classmethod
def method(cls):
A.method(cls)
...
Который менее динамичен, но меньше магии и более удобным для чтения - или:
class B(metaclass=A):
@classmethod
def method(cls):
cls.__class__.method(cls)
...
Что является более динамичным (атрибут __class__
работает cls
только л икэ она будет работать в случае B
был просто какой-то экземпляр A
как мой пример во втором абзаце: B.__class__ is A
)
В обоих случаях, вы можете защитить себя от вызова в метакласса несуществующий method
с простым if hasattr(cls.__class__, "method"): ...
Это было скорее интеллектуальное преследование, чем практическое. По сути, у меня есть вариант использования, когда я вызываю метод 'setup_class' в' __init__' метакласса. Существует базовая функциональность для 'setup_class', поэтому было бы совершенно неинтуитивно, чтобы это было определено в метаклассе, но после того, как я столкнулся с этой проблемой, я понял, что ее можно так же легко определить в базовом классе. Я скоро отправлю свою попытку решения. Спасибо за ясный ответ – rmorshea
Если у вас есть класс setup_class, который имеет смысл в метаклассе и что может дополнять это, что имеет смысл в классе, вы можете документировать стандарт именования или декоратор, чтобы иметь методы, называемые самим классом путем выполнения самого setup_class. (однако есть трюк - метод класса, который вызывается перед возвратом метакласса '__init__', не может использовать' super' - см. http://stackoverflow.com/questions/13126727/how-is-super-in-python- 3-реализован/28605694 # 28605694 – jsbueno