Добра практика ли е да се извикват функции в пакет чрез ::

Пиша някои R функции, които използват някои полезни функции в други пакети като stringr и base64enc. Добре ли е да не извиквам library(...) или require(...), за да заредя първо тези пакети, а да използвам ::, за да препратя директно към функцията, от която се нуждая, като stringr::str_match(...)?

Добра практика ли е в общия случай? Или какъв проблем може да предизвика?


person Kun Ren    schedule 23.04.2014    source източник
comment
require обикновено се използва в рамките на функция в пакет и тази SO публикация прави добра разлика между него и library. Ако сте сигурни, че ще имате нужда само от една (или две) функции от пакет, :: е добре, но аз гравитирам към него само когато има сблъсъци в пространството на имената. И не забравяйте за оператора :::.   -  person hrbrmstr    schedule 23.04.2014


Отговори (1)


Всичко зависи от контекста.

:: е основно необходимо, ако има сблъсъци в пространството на имена, функции от различни пакети с едно и също име. Когато зареждам пакета dplyr, той предоставя функция filter, която се сблъсква с (и маскира) функцията filter, заредена по подразбиране в пакета stats. Така че, ако искам да използвам stats версията на функцията, ще трябва да я извикам с stats::filter. Това също дава мотивация да не се зареждат много пакети. Ако наистина искате само една функция от пакет, може да е по-добре да използвате ::, отколкото да заредите целия пакет, особено ако знаете, че пакетът ще маскира други функции, които искате да използвате.

Не в код, а в текст, намирам :: за много полезен. Много по-сбито е да въведете stats::filter от функцията filter в пакета stats.

От гледна точка на производителността има (много) ниска цена за използване на ::. Дългогодишният член на екипа за разработка на R-Core Martin Maechler написа (на r-devel пощенски списък (септември 2017 г.))

Много хора изглежда забравят, че всяко използване на :: е извикване на R функция и използването му е неефективно в сравнение само с използването на вече импортираното име.

Намаляването на производителността е много малко от порядъка на няколко микросекунди, така че е проблем само когато имате нужда от силно оптимизиран код. Изпълнението на ред код, който използва :: един милион пъти, ще отнеме секунда или две повече от код, който не използва ::.

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

Настрана: може да се направи подобен аргумент за предпочитане на library() пред require(). Библиотеката ще предизвика грешка и ще спре, ако пакетът не е там, докато require ще предупреди, но ще продължи. Ако вашият код има план за извънредни ситуации, в случай че пакетът не е там, тогава непременно използвайте if (require(package)) ..., но ако вашият код ще се провали без пакет, трябва да използвате library(package) в горната част, така че да се провали рано и ясно.

В рамките на вашия собствен пакет

Общото решение е да направите свой собствен пакет, който imports другите пакети трябва да използвате във файла DESCRIPTION. Тези пакети ще бъдат инсталирани автоматично, когато вашият пакет е инсталиран, така че можете да използвате pkg::fun вътрешно. Или, като ги импортирате също във NAMESPACE файла, можете да import цял пакет или избирателно importFrom специфични функции и да не се нуждаете от ::. Мненията по този въпрос се различават. Martin Maechler (същият източник на r-devel като по-горе) казва:

Лично аз имам впечатлението, че :: се използва много повече в днешно време, особено в пакети, където силно бих препоръчал използването на importFrom() в NAMESPACE, така че всичко това се случва по време на зареждане на пакета и след това не използвайки :: в самите източници на пакета.

От друга страна, главният учен на RStudio Хадли Уикъм казва в своята книга R Packages:

Обичайно е пакетите да бъдат изброени в Imports в DESCRIPTION, но не и в NAMESPACE. Всъщност това е, което препоръчвам: избройте пакета в DESCRIPTION, така че да бъде инсталиран, след което винаги го препращайте изрично с pkg::fun(). Освен ако няма сериозна причина да не го правите, по-добре е да бъдете изрични.

С двама уважавани R експерти, които дават противоположни препоръки, мисля, че е честно да се каже, че трябва да изберете стила, който ви подхожда най-добре и отговаря на вашите нужди за яснота, ефективност и поддръжка.


Ако често установявате, че използвате само една функция от друг пакет, можете да копирате кода и да го добавите към вашия собствен пакет. Например, имам пакет за лична употреба, който заимства %nin% от пакета Hmisc, защото смятам, че е страхотна функция, но не използвам често нищо друго от Hmisc. С roxygen2 е лесно да добавите @author и @references за правилно приписване на кода за заета функция. Също така се уверете, че пакетните лицензи са съвместими, когато правите това.

person Gregor Thomas    schedule 23.04.2014
comment
Друго предимство на използването на library(...) в горната част на вашия скрипт е, че ако някой се опита да source вашия файл без инсталиран пакет, командата source ще се провали рано (а не след потенциално дълги зареждания на данни или манипулации). - person Hugh; 23.04.2014
comment
require е написано, за да върне логическа стойност, което го прави лесен за използване в конструкцията: if( !require(pkg) ){ cat("informative error message")} - person IRTFM; 23.04.2014
comment
@BondedDust В случай като този би било по-добре да използвате stop или warning вместо cat. - person Gregor Thomas; 23.04.2014
comment
Ако пишете пакет, мисля, че е по-добре да използвате :: вместо imports namespace или importsFrom (освен ако не използвате много функции от другия пакет) - person hadley; 23.04.2014
comment
@hadley Ще отложа това на теб, но бих искал малко пояснение. Ако основната функционалност на вашия пакет се нуждае от pkg::foo--дори ако foo е единствената необходима функция от pkg---тогава не трябва ли pkg да бъде в Imports или Depends, за да се гарантира, че е инсталирана? Ако бъде преместен на Suggests или Enhances, тогава няма гаранция, че pkg е там. - person Gregor Thomas; 23.04.2014
comment
@shujaa Да, трябва да е в DESCRIPTION imports, но не трябва да е в NAMESPACE import(pkg) или importFrom(pkg,foo) (мисля, че първоначалният ми коментар беше малко объркващ) - person hadley; 23.04.2014
comment
Може да се добави кратък коментар относно четливостта на кода. Наличието на всяко извикване на функция с префикс име на пакет може да направи кода объркан. Особено когато някое извикване на функция е аргумент на друго извикване на функция и т.н... (често срещано в tidyr) - person Dead Vil; 29.07.2020