Вмешиваться в определение каждого класса в python неявно используя метаклассы? - PullRequest
0 голосов
/ 05 апреля 2020

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

Я могу добавить новый атрибут явно, используя пользовательский метакласс, подобный этому

class NewAttrMeta(type):
    def __new__(cls, name, bases, attrs):
        attrs['new_attr'] = 'new_thing'
        return super().__new__(cls, name, bases, attrs)

class A(metaclass=NewAttrMeta):
   ...

print(A.new_attr)
$ 'new_thing'

Но возможно ли принудительно изменить подобное для каждого класса, который определен, а не только для тех, которые явно унаследовать от вашего пользовательского метакласса?

Я подумал, может быть, поскольку все классы имеют тип type, если я перезаписал сам type своим пользовательским метаклассом, тогда все новые классы могут наследоваться от него. А поскольку метаклассы являются подклассами type, то все классы, определенные таким образом, будут по-прежнему действительны ...

class NewAttrMeta(type):
    def __new__(cls, name, bases, attrs):
        attrs['new_attr'] = 'new_thing'
        return super().__new__(cls, name, bases, attrs)

type = NewMagicMeta

Но это работает только в том случае, если type передается явно снова:

class A(type):
   ...

print(A.new_attr)
$ 'new_thing'

class B():
   ...

print(B.new_attr)
$ AttributeError: type object 'A' has no attribute 'new_attr'

С какой стати я пытаюсь это сделать? Я хотел посмотреть, смогу ли я локально реализовать версию отклоненного PEP 472: «Поддержка индексации с аргументами ключевых слов» путем переопределения метода __getitem__ каждого класса, который определил любую версию __getitem__. Я делаю это только для удовольствия, поэтому мне будут интересны любые идеи или альтернативные способы сделать это (чем хакер тем лучше!).

1 Ответ

1 голос
/ 05 апреля 2020
  1. Python не позволяет модифицировать встроенные объявленные типы. Это означает, что словари, списки, классы, определенные в расширениях, таких как Numpy .ndarrays, «заморожены» из кода Python.

  2. Даже если это возможно, меняйте метакласс для всех классы не будут изменять классы, уже определенные. Итак, list, et c ... не повлияло бы. Вы можете расположить свою программу так, чтобы она могла «установить» хуки для создания классов, прежде чем импортировать любые другие модули с определением класса, - чтобы это могло повлиять на классы, написанные в Python коде. (Классы, созданные в расширениях, определены в коде C и в любом случае не go в процессе создания класса метаклассов)

  3. type упоминается как метакласс для объекта, так что даже если вы измените type во встроенных программах - что возможно, это не будет автоматически использоваться в качестве метакласса ни для кого. Он используется по умолчанию, потому что это то, что возвращается type(object)

Все это говорит о том, что можно создать что-то, что будет искать во всех существующих классах в работающей Python программе, и всякий раз, когда класс определен в Python, для украшения метода __getitem__, если он существует, для принятия ключевых параметров.

Но тогда:

  1. Поддержка индексирования аргументов, предложенная в PEP 472, требует изменения синтаксического анализатора и спецификации языка - простое принятие аргументов ключевых слов в __getitem__ не заставит a[b=1] работать или не будет синтаксисом ошибка. Можно было бы написать a.__getitem__(b=1)

  2. Имя индекса в __getitem__ - это нечто очень специфичное c для определенного типа объектов. Нет никакого смысла для любого класса, разработанного без учета этого. Если a список, что бы значило a[fish='golden']? А что если a является диктатом?

В общем, у вас уже был бы очень классный класс, если бы вы нашли что-то, для чего имеет смысл передавать имя в индекс - и тогда вы могли бы просто иметь какой-либо метод для его извлечения и использовать обычные обозначения в скобках для этого a.get(fish="gold"), или даже, если вы пишете метод __call__: a(fish="gold")

...