2017-01-27 4 views
4

Правильный модуль Python отобразит все свои общественные символы в a list called __all__. Управление этим списком может быть утомительным, так как вам придется перечислить каждый символ дважды. Разумеется, есть лучшие способы, возможно, using decorators, поэтому можно было бы просто аннотировать экспортированные символы как @export.Экспорт декоратора, который управляет __all__

Как вы могли бы написать такой декоратор? Я уверен, что есть разные способы, поэтому я хотел бы получить несколько ответов с достаточной информацией о том, что пользователи могут сравнивать подходы друг к другу.

ответ

3

Вы могли бы просто объявить декоратора на уровне модуля, как это:

__all__ = [] 

def export(obj): 
    __all__.append(obj.__name__) 
    return obj 

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

1

Можно определить следующие в какой-то полезной библиотеки:

def exporter(): 
    all = [] 
    def decorator(obj): 
     all.append(obj.__name__) 
     return obj 
    return decorator, all 

export, __all__ = exporter() 
export(exporter) 

# possibly some other utilities, decorated with @export as well 

Тогда внутри вашей публичной библиотеке вы могли бы сделать что-то вроде этого:

from . import utility 

export, __all__ = utility.exporter() 

# start using @export 

Использование библиотеки занимает две строки кода здесь , Он сочетает в себе определение __all__ и декоратора. Таким образом, люди, которые ищут одного из них, найдут другого, что поможет читателям быстро понять ваш код. Вышеизложенное также будет работать в экзотических средах, где модуль может быть недоступен из кэша sys.modules или где было повреждено свойство __module__ или некоторые из них.

2

В Is it a good practice to add names to __all__ using a decorator?, Ed L предлагает следующее, которые должны быть включены в какой-то полезной библиотеке:

import sys 

def export(f): 
    """Use a decorator to avoid retyping function/class names. 

    * Based on an idea by Duncan Booth: 
     http://groups.google.com/group/comp.lang.python/msg/11cbb03e09611b8a 
    * Improved via a suggestion by Dave Angel: 
     http://groups.google.com/group/comp.lang.python/msg/3d400fb22d8a42e1 
    """ 
    mod = sys.modules[fn.__module__] 
    if hasattr(mod, '__all__'): 
     name, all_ = f.__name__, mod.__all__ 
     if name not in __all__: 
      all_.append(name) 
    else: 
     mod.__all__ = [fn.__name__] 
    return f 

Мы адаптированное имя в соответствии с другими примерами. При этом в местной библиотеке утилиты, вы бы просто написать

from .utility import export 

, а затем начать использовать @export. Только одна строка идиоматического Python, вы не можете получить намного проще, чем это. С другой стороны, модуль требует доступа к модулю, используя свойство __module__ и кеш sys.modules, оба из которых могут быть проблематичными в некоторых более эзотерических установках (таких как специализированные механизмы импорта или функции обертывания от другого модуля до создавать функции в этом модуле).

The python partatpublicpackage от Barry Warsaw делает что-то похожее на это. Он также предлагает синтаксис на основе ключевых слов, но вариант декоратора основан на тех же шаблонах, которые были использованы выше.

This great answer от Aaron Hall предлагает что-то очень похожее, с еще двумя строками кода, поскольку он не использует __dict__.setdefault. Может быть предпочтительнее, если по какой-то причине проблема с манипулированием модулем __dict__.

+0

Критика: '__dict__' семантически не является частью публичного API. Я бы предпочел избегать прямых изменений нижнего уровня в пользу абстракций более высокого уровня - даже на таком коде. Я считаю это немного более безопасным, хотя мне будет трудно думать о сценарии, в котором ваш код здесь не работает. Также спасибо за ссылку, ура! –

+1

Поскольку это вики сообщества, я объединил логику, чтобы избежать использования '__dict__' напрямую. Если вы согласны, вы можете отметить указанные выше комментарии для удаления. Другие вещи, которые я улучшил, перемещают кредиты от docstring до конца вашего ответа и улучшают docstring, чтобы проиллюстрировать использование, чтобы, возможно, пройти доктрины. Я не заинтересован в написании собственного ответа на этот конкретный вопрос. –

1

https://github.com/russianidiot/public.py имеет еще одну реализацию такого декоратора.Its core file в настоящее время 160 строк длиной! Ключевыми моментами являются тот факт, что он использует inspect module для получения соответствующего модуля на основе текущего стека вызовов.

Смежные вопросы