Использование __slots__?

Макросы делают преобразования кода

Макрос преобразует исходный код. Леневой оценки нет. Представьте, что теперь вы можете писать функции, которые преобразуют произвольный код в произвольный код.

Очень простые преобразования кода

Создание простых языковых конструкций также является очень простым примером. Рассмотрим ваш пример открытия файла:

(with-open-file (stream file :direction :input)
  (do-something stream))

vs.

(call-with-stream (function do-something)
                  file
                  :direction :input)

Что дает мне макрос, это немного другой синтаксис и структура кода.

Встроенный язык: расширенные конструкции итераций

Далее рассмотрим несколько другой пример:

(loop for i from 10 below 20 collect (sqr i))

vs.

(collect-for 10 20 (function sqr))

Мы можем определить функцию COLLECT-FOR, который делает то же самое для простого цикла и имеет переменные для начала, конца и функции шага.

Но LOOP предоставляет новый язык. Макрос LOOP является компилятором для этого языка. Этот компилятор может выполнить LOOP конкретные оптимизации и также может проверить синтаксис во время компиляции для этого нового языка. Еще более мощный макрос цикла ITERATE. Эти мощные инструменты на уровне языка теперь могут быть написаны как библиотеки без какой-либо специальной поддержки компилятора.

Прогулка по дереву кода в макросе и внесение изменений

Следующий еще один простой пример:

(with-slots (age name) some-person
  (print name)
  (princ " "
  (princ age))

vs. что-то похожее:

(flet ((age (person) (slot-value person 'age))
       (name (person) (slot-value person 'name)))
   (print (name))
   (princ " ")
   (princ (age)))

Макрос WITH-SLOTS вызывает полное перемещение вложенного исходного дерева и заменяет имя переменной вызовом на (SLOT-VALUE SOME-PERSON 'name):

(progn
  (print (slot-value some-person 'name))
  (princ " "
  (princ (slot-value some-person 'age)))

В этом случае макрос может переписать отдельные части кода. Он понимает структуру языка Lisp и знает, что имя и возраст являются переменными. Он также понимает, что в некоторых ситуациях name и age могут не быть переменными и не должны быть переписаны. Это приложение так называемого кода Walker , инструмент, который может перемещаться по кодам деревьев и вносить изменения в дерево кода.

Макросы могут изменять среду компиляции

Еще один простой пример: содержимое небольшого файла:

(defmacro oneplus (x)
  (print (list 'expanding 'oneplus 'with x))
  `(1+ ,x))

(defun example (a b)
   (+ (oneplus a) (oneplus (* a b))))

В этом примере нас не интересует макрос ONEPLUS, а сам макрос DEFMACRO.

Что интересно? В Lisp вы можете иметь файл с указанным выше содержимым и использовать компилятор file для компиляции этого файла.

;;; Compiling file /private/tmp/test.lisp ...
;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 1
;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
;;; Source level debugging is on
;;; Source file recording is  on
;;; Cross referencing is on
; (TOP-LEVEL-FORM 0)
; ONEPLUS

(EXPANDING ONEPLUS SOURCE A) 
(EXPANDING ONEPLUS SOURCE (* A B)) 
; EXAMPLE
;; Processing Cross Reference Information

Итак, мы видим, что компилятор file g2] расширяет использование макроса ONEPLUS.

Что особенного в этом? В файле есть определение макроса, и в следующем виде мы уже используем этот новый макрос ONEPLUS. Мы никогда не загружали определение макроса в Lisp. Как-то компилятор знает и регистрирует определенный макрос ONEPLUS и затем может его использовать.

Таким образом, макрос DEFMACRO регистрирует вновь определенный макрос ONEPLUS во время компиляции, так что компилятор знает об этом макросе - без загрузки кода. Затем макрос можно выполнить во время компиляции во время расширения макроса.

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

Обратите внимание, что макрос ONEPLUS также запускается дважды, поскольку он используется дважды в файл. Побочным эффектом является то, что он что-то печатает. Но ONEPLUS может иметь и другие произвольные побочные эффекты. Например, он может проверить закрытый источник на базе правил и предупредить вас, если, например, закрытый код нарушает некоторые правила (подумайте о проверке стиля).

Это означает, что макрос здесь DEFMACRO , может изменять язык и среду при компиляции файла. В других языках компилятор может предоставить специальные директивы компилятора, которые будут распознаны во время компиляции. Существует множество примеров таких макросов, которые влияют на компилятор: DEFUN, DEFCLASS, DEFMETHOD, ...

Макросы могут сделать код пользователя короче

Например, макрос DEFSTRUCT для определения структур данных записи .

(defstruct person name age salary)

Выше defstruct макрос создает код для

  • новый тип структуры person с тремя слотами
  • слот-аксессуар для чтения и записи значений
  • предикат для проверки того, имеет ли какой-либо объект класс person
  • a make-person функция для создания структурных объектов
  • печатное представление

Кроме того, он может:

  • записать исходный код
  • записывает исходный код исходного кода (файл, буфер редактора, REPL, ...)
  • перекрестная ссылка на исходный код

Исходный код для определения структуры - это короткая строка. Расширенный код намного длиннее.

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

643
задан kmario23 2 May 2019 в 11:23
поделиться

4 ответа

Заключение в кавычки Jacob Hallen :

надлежащее использование __slots__ должно оставить свободное место в объектах. Вместо того, чтобы иметь динамический dict, который позволяет добавлять атрибуты к объектам в любое время, существует статическая структура, которая не позволяет дополнения после создания. [Это использование __slots__ устраняет издержки одного dict для каждого объекта.], В то время как это иногда - полезная оптимизация, было бы абсолютно ненужным, если бы интерпретатор Python был достаточно динамичным так, чтобы это только потребовало бы dict, когда на самом деле были дополнения к объекту.

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

Создание CPython, достаточно умный для обработки оставляющего свободного места без __slots__, является основным обязательством, которое является, вероятно, почему это (еще) не находится в списке изменений для P3k.

261
ответ дан Rob Bednark 2 May 2019 в 11:23
поделиться

Каждый объект Python имеет __dict__ атрибут, который является словарем, содержащим все другие атрибуты. например, когда Вы Python типа self.attr на самом деле делаете self.__dict__['attr']. Поскольку можно предположить использовать словарь для хранения атрибута, занимает некоторое дополнительное место & время для доступа к нему.

Однако, когда Вы будете использовать __slots__, любой объект, созданный для того класса, не будет иметь __dict__ атрибут. Вместо этого весь доступ атрибута сделан непосредственно через указатели.

Поэтому, если хотят структуру стиля C, а не абсолютный класс, можно использовать __slots__ для уплотнения размера объектов & сокращение времени доступа атрибута. Хорошим примером является класс Точки, содержащий атрибуты x & y. Если Вы собираетесь иметь много точек, можно попытаться использовать __slots__ для сохранения некоторой памяти.

58
ответ дан muhuk 2 May 2019 в 11:23
поделиться

Вы хотели бы использовать __slots__, если Вы собираетесь инстанцировать много (сотни, тысячи) объектов того же класса. __slots__ только существует как инструмент оптимизации памяти.

Этому высоко препятствуют для использования __slots__ для ограничения создания атрибута, и в целом Вы хотите избежать его, потому что это повреждает рассол, наряду с некоторыми другими функциями самоанализа Python.

120
ответ дан Ryan 2 May 2019 в 11:23
поделиться

У Вас есть — по существу — бывший бесполезный для __slots__.

В течение времени, когда Вы думаете, что Вам, возможно, понадобилось бы __slots__, Вы на самом деле хотите использовать Легкий вес или Боксер наилегчайшего веса шаблоны разработки. Это случаи, когда Вы больше не хотите использовать просто объекты Python. Вместо этого Вы хотите Python подобная объекту обертка вокруг массива, структуры или массива numpy.

class Flyweight(object):

    def get(self, theData, index):
        return theData[index]

    def set(self, theData, index, value):
        theData[index]= value

подобная классу обертка не имеет никаких атрибутов —, она просто предоставляет методы, которые действуют на базовые данные. Методы могут быть уменьшены до методов класса. Действительно, это могло быть уменьшено до просто функций, воздействующих на основной массив данных.

1
ответ дан Noctis Skytower 2 May 2019 в 11:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: