Неплохая практика доступа к слотам S4-объектов напрямую с помощью @? - программирование
Подтвердить что ты не робот

Неплохая практика доступа к слотам S4-объектов напрямую с помощью @?

Это почти философский вопрос: плохо ли получить доступ и/или установить слоты объектов S4 напрямую, используя @?

Мне всегда говорили, что это плохая практика, и что пользователи должны использовать методы "accessor" S4, и разработчикам следует предоставить их пользователям. Но я хотел бы знать, знает ли кто-нибудь реальную сделку за этим?

Вот пример использования пакета sp (но может быть обобщен для любого класса S4):

> library(sp)
> foo <- data.frame(x = runif(5), y = runif(5), bar = runif(5))
> coordinates(foo) <- ~x+y
> class(foo)
[1] "SpatialPointsDataFrame"
attr(,"package")
[1] "sp"

> str(foo)
Formal class 'SpatialPointsDataFrame' [package "sp"] with 5 slots
  [email protected] data       :'data.frame': 5 obs. of  1 variable:
  .. ..$ bar: num [1:5] 0.621 0.273 0.446 0.174 0.278
  [email protected] coords.nrs : int [1:2] 1 2
  [email protected] coords     : num [1:5, 1:2] 0.885 0.763 0.591 0.709 0.925 ...
  .. ..- attr(*, "dimnames")=List of 2
  .. .. ..$ : NULL
  .. .. ..$ : chr [1:2] "x" "y"
  [email protected] bbox       : num [1:2, 1:2] 0.591 0.155 0.925 0.803
  .. ..- attr(*, "dimnames")=List of 2
  .. .. ..$ : chr [1:2] "x" "y"
  .. .. ..$ : chr [1:2] "min" "max"
  [email protected] proj4string:Formal class 'CRS' [package "sp"] with 1 slots
  .. .. [email protected] projargs: chr NA

> [email protected]
        bar
1 0.6213783
2 0.2725903
3 0.4458229
4 0.1743419
5 0.2779656
> [email protected] <- data.frame(bar = letters[1:5], baz = runif(5))
> [email protected]
  bar        baz
1   a 0.22877446
2   b 0.93206667
3   c 0.28169866
4   d 0.08616213
5   e 0.36713750
4b9b3361

Ответ 1

В общем, хорошая практика программирования заключается в том, чтобы отделить содержимое объекта от интерфейса, см. эту статью в википедии. Идея состоит в том, чтобы интерфейс был отделен от реализации, таким образом реализация может значительно измениться, не затрагивая ни один из кода, который взаимодействует с этим кодом, например. ваш script. Поэтому использование @ создает менее надежный код, который с меньшей вероятностью будет работать через несколько лет. Например, в sp -пакете, упомянутом в @mdsummer, реализация способов сохранения многоугольников может измениться из-за скорости или прогресса знаний. Используя @, ваш код разбивается, используя интерфейс, ваш код работает все еще. Кроме того, если интерфейс также меняется. Но изменения в реализации гораздо более вероятны, чем изменения интерфейса.

Ответ 2

В этот вопрос stackoverflow-er спрашивает, почему они не могут найти слот end в Bioconductor Объект IRanges; в конце концов есть start(), width() и end() аксессоры и start и width слоты. Ответ заключается в том, что способ взаимодействия пользователей с классом отличается от того, как он реализован. В этом случае реализация управляется простым наблюдением, что для сохранения трех значений (начало, конец, ширина) не достаточно пространства, когда достаточно двух (что два? До разработчика!). Подобные, но более глубокие примеры расхождения между интерфейсом и реализацией присутствуют в других объектах S4 и в общих экземплярах S3, таких как те, которые были возвращены lm, где данные, хранящиеся в классе, подходят для последующих вычислений, а не предназначены для представления величин, которые конкретный пользователь может быть больше всего заинтересован. Ничего хорошего не получится, если вы должны были обратиться к этому экземпляру lm и изменить значение, например, элемент coefficients. Такое разделение интерфейса от реализации дает разработчику большую свободу для обеспечения разумного и постоянного пользовательского опыта, возможно, разделяемого с другими подобными классами, но для реализации классов (и для изменения реализации) способами, которые делают смысл программирования.

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

Ответ 3

Вкратце, разработчик должен предоставить методы для каждого варианта использования, но на практике это довольно сложно, и сложно охватить все возможные варианты использования. Технически и, насколько мне известно, если вам нужно больше, чем предоставляет разработчик, и вы должны использовать "@", чтобы получить неэкспонированные функции, тогда вы являетесь разработчиком (различие в этом случае размыто в программном обеспечении GNU).

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

Метод coordinates() для полигонов и строк возвращает только центроид для каждого объекта, хотя для точек он возвращает каждую "координату" из объекта, но это потому, что "точки" являются "взаимно однозначными". Один объект, одна координата, true также для SpatialPoints и SpatialPointsDataFrame. Это не относится к линиям и многоугольнику, а также к линиям и многоугольникам, или к пространственным линиям и пространственным полигонам, или к пространственным шаблонам DataFrame и SpatialPolygonsDataFrame. Они состоят из > двух координатных дорожек или трехкоординатных поли-колец. Как получить координату каждой вершины в каждом многоугольнике из каждого многоразветвленного SpatialPolygon? Вы не можете, если не вникаете в структуру разработчика с помощью "@".

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

Ответ 4

Как разработчик класса S4, мое мнение таково:

Если вы читаете слоты с @, вы делаете это на свой страх и риск (например, почти все, что вы делаете в R - см. ниже для некоторых известных примеров). При этом слоты класса S4 на самом деле являются частью документированного интерфейса.

Основные преимущества доступа через @ Я вижу скорость:

> microbenchmark (accessor = wl (chondro), direct = [email protected])
Unit: nanoseconds
      expr    min       lq   median       uq    max
1 accessor 333431 341289.5 346784.5 366737.5 654219
2   direct    165    212.5    395.0    520.0   1440

(функция доступа выполняет проверку valitidy в дополнение к возврату слота @wavelength, который вызывает разницу). Я ожидал бы, что каждая достойная общедоступная функция доступа для обеспечения достоверности)

Я даже рекомендую использовать доступ для чтения к слотам моего класса в критичных по времени ситуациях (например, при доступе к множеству подмножеств одного и того же объекта, возможно, стоит пропустить проверку действительности неизмененного объекта каждый раз), а в коде моего пакета я преимущественно читаю слоты напрямую, обеспечивая достоверность в начале функций и в конце функций, где объект мог стать недействительным. Можно утверждать, что конструктивное решение (R), которое @<- не проверяет достоверность, действительно наносит огромные накладные расходы на практике, потому что методы, работающие над объектами S4, не могут полагаться на действительный объект и, следовательно, должны быть выполнены даже методы с чисто доступом на чтение проверка достоверности.

Если вы думаете о доступе к записи в слот, вы должны действительно знать, что делаете. @<- выполняет не проверку достоверности, это должно сделать официальный администратор записи. И, возможно, доступ к записи может значительно увеличить, чем просто обновить один слот, чтобы поддерживать согласованность состояния объекта.

Итак, если вы пишете в слот, ожидайте найти себя в аду и не жалуйтесь.; -)

Думая немного дальше по философской линии этого: мой пакет является публичным в GPL. Я не только позволяю вам адаптировать код к вашим потребностям, но я хочу призвать вас разработать/адаптировать код для ваших нужд. На самом деле это очень просто в R - все уже есть в обычной интерактивной сессии R, включая доступ к слотам. Это вполне соответствует дизайнерским решениям, которые делают R очень мощным, но позволяют такие вещи, как

> T <- FALSE
> `+` <- `-`
> pi <- 3
> pi + 2
[1] 1