[Перевод] Как написать класс без использования класса

задняя часть Python Программа перевода самородков редкоземельный

Как написать класс без класса

предисловие

Объектная модель Python невероятно мощная; на самом деле, вы можете переписать все (объекты) или раздать странный объект кому угодно, и заставить его принять его, как обычный объект.

Объектная ориентация Python является потомком объектной ориентации smalltalk. В Python все является объектом, даже наборы объектов и типы объектов; в частности, функции также являются объектами. Мне стало любопытно: можно ли создать класс без использования класса?

код

Ключевой код для этой идеи показан ниже. Это очень простая реализация, но она поддерживает__call__Такие крайние случаи (но другие магические методы не поддерживаются, так как им нужно подгружать зависимости). Это будет объяснено позже.

Что-то пошло не так?

Это очень продвинутые колеса Python, которые используют некоторые объекты не так, как они были разработаны. Мы объясним код по частям.

первый помощник

def _suspend_self(namespace, suspended):  

Это несколько пугающее название функции. Пауза? Это нехорошо, но мы можем это исправить._suspend_selfфункцияfunctools.partialПростое приложение , которое работает путем захвата из области видимости внешней функции.namespaceи наведите курсор на внутреннюю функцию.

    def suspender(*args, **kwargs):
        return suspended(namespace, *args, **kwargs)

Затем эта внутренняя функция вызывает приостановленную функцию, переданную с первым пространством имен параметров, которое фактически упаковывает метод, чтобы его можно было применить к обычному классу Python._suspend_selfОстальное просто устанавливает некоторые свойства, которые могут быть использованы при отражении в какой-то момент (я могу что-то упустить).

зверь

Следующая функцияmake_class. Что мы можем узнать из его подписи?

def make_class(locals: dict):  
    """
    在被调用者的本地创建一个类。
    参数 locals:建立类的本地。
    """

Это не хорошо, если другие методы запросы или получают ваши локальные переменные напрямую. Обычно это для поиска чего-либо в предыдущем стеке или просто взломать вашу машину. Наш текущий экземпляр имеет первый тип, в поисках локальных функций и добавления их в класс.

    # 试着找到一个 `__call__` 来执行 call 函数
    # 它将作为一个函数,这样命名空间和被调用者可以引用彼此
    def call_maker():
        if '__call__' in locals and callable(locals['__call__']):
            return _suspend_self(namespace, locals['__call__'])

        def _not_callable(*args, **kwargs):
            raise TypeError('This is not callable')

        return _not_callable

Эта функция довольно проста, это функция, которая принимает функцию в качестве возвращаемого значения! На самом деле он делает следующее:

  • В классе функций проверьте, определили ли вы__call__
  • Если это так, как описано выше, используйте_suspend_selfФункция «монтирует» пространство имен для использования__call__Создайте метод.
  • Если нет, используйте по умолчанию__call__Мол, он инициирует возврат заглушки ошибки (функции-заглушки).

пространство имен

Пространство имен является ключевой частью, но я еще не объяснил его. Каждый (или большинство) методов в классе будетselfВ качестве первого параметра этоselfЭто экземпляр класса, когда функция запускается.

Экземпляр класса на самом деле является.Словарь, доступ к содержимому которого осуществляется с использованием символьных, а не числовых индексов. Итак, нам нужен объект, который можно передать в функцию, которая, как мы ожидаем, будет имитировать этот словарь. Поэтому мы говорим, что этот экземплярnamespace,мы вnamespaceУстановить переменные и т. д. Позже упоминаетсяnamespace, и относимся к нему как к нашему экземпляру. Вы можете получить экземпляр класса, вызвав сам объект класса:obb = SomeClass().

Стандартный способ создания точечного словаря — attrdict:

attrdict = type("attrdict", (dict,), {"__getattr__": dict.__getitem__, "__setattr__": dict.__setitem__})  

Но так как он создает класс, это немного обманчиво. Другие методы включаютtyping.SimpleNamespaceили создайте класс без часового. Но оба этих метода обманчиво создают классы, которые мы не можем использовать.

решение

Решение для пространства имен — это другая функция. Функции ведут себя как вызываемые точечные словари доступа, поэтому мы просто создаемnamespaceфункция, предполагая, что это self.

    # 这个就充当了 self 对象
    # 所有的属性都建立在此之上
    def namespace():
        return called()

нужно обратить внимание на звонокcalled()использование - это для обычных фиктивных экземпляров на__call__поведение.

Создайте__init__

Все классы в Python имеют__init__(исключая классы, которые по умолчанию предоставляют пустую инициализацию), поэтому нам нужно эмулировать это и убедиться, что вызывается определенная пользователем инициализация.

    # 创建一个 init 的替代方法
    def new_class(*args, **kwargs):
        init = locals.get("__init__")
        if init is not None:
            init(namespace, *args, **kwargs)

        return namespace

Этот код предназначен просто для получения определяемого пользователем из локального__init__, если он найден, он вызывается. Затем он возвращает пространство имен (то есть поддельный экземпляр), эффективно имитируя цикл:(metaclass.)__call__ -> __new__ -> __init__.

очистить

Следующее, что нужно сделать, это создать методы поверх класса, что можно сделать с помощью очень простого сканирования цикла:

    # 更新 namespace
    for name, item in locals.items():
        if callable(item):
            fn = _suspend_self(namespace, item)
            setattr(namespace, name, fn)

Подобно тому, что было упомянуто выше, все вызываемые функции_suspend_selfОберните его, чтобы включить функцию в метод класса и завершить настройку в пространстве имен.

получить класс

Последнее, что нужно сделать, это простоreturn new_class. Последний раунд цикла для получения экземпляра класса:

  • Код пользователя определяет функцию класса
  • Когда вызывается функция класса, функция вызываетmake_classустановить пространство имен (добавить@makeмодификатор, этот шаг можно сделать автоматически)
  • make_classФункция устанавливает экземпляр, делая его готовым к последующей инициализации.
  • make_classФункция возвращает другую функцию, и вызов этой функции получает экземпляр и завершает его инициализацию.

Теперь он у нас есть, класс совершенно бесполезных классов. Держу пари, вы действительно примените это.


Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.