Примечание 1
Просто быть чистым:
Foo
не наследует от FooMeta
.
FooMeta
не надкласс Foo
super
не получится.
Заметка 2
Теперь это примечание (1) не по пути, если вы хотите получить доступ к методу метакласса изнутри метода экземпляра метакласса, вы можете сделать это следующим образом:
class FooMeta(type):
_foo = 5
def get_foo(cls):
print("`get_foo` from `FooMeta` was called!")
class Foo(metaclass=FooMeta):
@classmethod
def bar(Foo):
FooMeta = type(Foo)
FooMeta_dot_getfoo = FooMeta.get_foo
FooMeta_dot_getfoo(Foo)
def baz(self):
Foo = type(self)
FooMeta = type(Foo)
FooMeta_dot_getfoo = FooMeta.get_foo
FooMeta_dot_getfoo(Foo)
Foo.bar()
foo = Foo()
foo.baz()
Результат:
`get_foo` from `FooMeta` was called!
`get_foo` from `FooMeta` was called!
Заметка 3
Если у вас есть метод класса с тем же именем, что и у метода в метаклассе, почему метод метакласса НЕ вызывается? Рассмотрим следующий код:
class FooMeta(type):
def get_foo(cls):
print("META!")
class Foo(metaclass=FooMeta):
@classmethod
def get_foo(cls):
print("NOT META!")
Foo.get_foo()
Результатом является NOT META!
. В следующем обсуждении предположим, что:
foo
является экземпляром Foo
Foo
является экземпляром FooMeta
Впервые в этом посте у меня будет псевдокод, а не питон. Не пытайтесь запустить следующее. __getattribute__
выглядит следующим образом:
class FooMeta(type):
def get_foo(Foo):
print("META!")
class Foo(metaclass=FooMeta):
@classmethod
def get_foo(Foo):
print("NOT META!")
def __getattribute__(foo, the string "get_foo"):
try:
attribute = "get_foo" from instance foo
except AttributeError:
attribute = "get_foo" from class Foo
# Begin code for handling "descriptors"
if hasattr(attribute, '__get__'):
attr = attribute.__get__(None, Foo)
# End code for handling "descriptors"
return attribute
foo = Foo()
foo.get_foo() # prints "NOT META!"
get_foo = Foo.__getattribute__(foo, "get_foo")
get_foo.__call__()
На самом деле вы можете игнорировать то, что говорит «code for handling "descriptors"
». Я включил это только для полноты картины.
Обратите внимание, что __getattribute__
нигде не говорит: «получить get_foo
из метакласса».
- Во-первых, мы пытаемся получить
get_foo
из экземпляра. Возможно, get_foo
является переменной-членом. Возможно, у одного экземпляра get_foo = 1
, а у другого экземпляра get_foo = 5
Компьютер не знает. Компьютер тупой.
- Компьютер понимает, что у экземпляра нет переменной-члена с именем
get_foo
. Затем он говорит: «Ах-ха! Держу пари, что get_foo
принадлежит к КЛАССУ». Итак, он выглядит там, и о чудо, вот оно: Foo
имеет атрибут с именем get_foo
. FooMeta
также имеет атрибут get_foo
, но кого это волнует.
На что следует обратить внимание, так это на то, что:
Foo
имеет атрибут с именем get_foo
MetaFoo
имеет атрибут с именем get_foo
Они оба имеют атрибуты с именами get_foo
, но Foo
и MetaFoo
— это разные объекты. Это не так, как если бы два get_foo
были общими. Я могу иметь obj1.x = 1
и obj2.x = 99
. Без проблем.
FooMeta
имеет собственный метод __getattribute__
. Раньше я говорил о Foo.__getattribute__
, а теперь давайте поговорим о MeTa __getattribute__
class FooMeta(type):
def get_foo(Foo):
print("META!")
def __getattribute__(Foo, the string "get_foo"):
try: # LINE 1
attribute = "get_foo" from class Foo # LINE 2
except AttributeError: # LINE 3
attribute = "get_foo" from class FooMeta # LINE 4
# LINE 5
# Begin code for handling "descriptors"
if hasattr(attribute, '__get__'):
attr = attribute.__get__(None, Foo)
# End code for handling "descriptors"
return attribute
class Foo(metaclass=FooMeta):
@classmethod
def get_foo(Foo):
print("NOT META!")
Foo.get_foo()
get_foo = FooMeta.__getattribute__(Foo, "get_foo")
get_foo.__call__()
Порядок событий:
- Строки 1 и 2 случаются
- Строки 3, 4 и 5 не выполняются
- Вы можете игнорировать информацию об дескрипторах, потому что ни один из разных
get_foo
в этой задаче не имеет метода __get__
.
Хорошо сейчас! Почему только 1 и 2 строки? Потому что ты сделал @classmethod
глупым! Мы проверяем Foo
, есть ли у него get_foo
, и он есть! Зачем проверять атрибуты класса, если мы сначала находим атрибут экземпляра? Мы всегда проверяем, принадлежит ли атрибут экземпляру (Foo
) first-and-first, прежде чем проверять, может быть, существует только одна копия статической переменной-члена. принадлежащий классу (FooMeta
) и общий для всех экземпляров.
Обратите внимание, что если у Foo нет get_foo
, то FooMeta.__getattribute__(Foo, "get_foo")
вернет get_foo
из метакласса, потому что первая попытка (получение его из экземпляра) не удалась. Вы как бы заблокировали эту опцию, дав экземпляру что-то с тем же именем, что и статическая переменная-член класса.
class K:
im_supposed_to_be_shared = 1
def __init__(self, x):
# NOPE!
self.im_supposed_to_be_shared = x
# maybe try type(self)
obj1 = K(14)
obj2 = K(29)
print(obj1.im_supposed_to_be_shared)
print(obj2.im_supposed_to_be_shared)
print(K.im_supposed_to_be_shared)
Отпечатки:
14
29
1
НЕ печатает:
29
29
29
Обратите внимание, что если вы хотите установить статическую переменную-член класса, instance.mem_var = 5
— это очень ⱽᵉᴿʸ плохая идея. Вы дадите instance
новую переменную-член, а статическая (общая) переменная-член класса будет затенена. Вы можете исправить это примерно так:
def __setattr__(self, attr_name, attr_val):
if hasattr(type(self), attr_name):
setattr(type(self), attr_name, attr_val)
else:
super_class = inspect.getmro(type(self))[1]
super_class.__setattr__(self, attr_name, attr_val)
Тогда ваша компиляция напечатает:
29
29
29
Примечание 4
class Foo:
@classmethod
def funky(cls):
pass
НЕ MetaClass.funky = funky
. Вместо этого это:
def funky(cls)
pass
Funky = classmethod (funky)
... что почти то же самое, что:
def funky(cls):
pass
funky = lambda self, *args, **kwargs: funky(type(self), *args, **kwargs)
Мораль истории note 4 заключается в том, что classmethod
funky
является атрибутом Foo
, а не атрибутом FooMeta
.
person
Samuel Muldoon
schedule
05.11.2019
return FooMeta.get_foo(cls) + 1
, но возникает вопрос - метаклассы предназначены для настройки создания класса. Вы этого не делаете. Итак, почему вы используете метакласс?super
больше касается обхода MRO (родительских или родственных классов при использовании наследования), поэтому я не совсем уверен, почему вы пытаетесь использовать его, чтобы получить метод метакласса в первую очередь, это совершенно не связанные проблемы. - person wim   schedule 05.11.2019super(FooMeta, Foo)
будет проксировать существующий методget_foo
? Потому что, в отсутствие чего-либо еще в цепочке наследования, следующим методом здесь будет просто попытка разрешения метода наtype
. Если вы хотите получить доступ к методу метаклассаget_foo
, вы просто используетеFooMeta.get_foo
напрямую. - person wim   schedule 05.11.2019get_foo
не появляется в возвращаемом значенииdir(Foo)
. (На самом деле это просто еще одно указание на то, чтоsuper().get_foo
потерпит неудачу, а не объяснение того, почему она потерпит неудачу). - person chepner   schedule 05.11.2019dir
предназначен для интерактивного удобства, а не для согласованности. Цитируя документы: удобство использования в интерактивной подсказке, он пытается предоставить интересный набор имен больше, чем он пытается предоставить строго или последовательно определенный набор имен, и его подробное поведение может меняться в разных выпусках. Например, атрибуты метакласса отсутствуют в списке результатов, если аргументом является класс. - person user2357112 supports Monica   schedule 05.11.2019