- Оригинальный адрес:Classes Without Classes
- Оригинальный автор:Fuyukai
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:EmilyQiRabbit
- Корректор:allenlongbaobao,sunhaokk
Как написать класс без класса
предисловие
Объектная модель 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,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.