2009-10-27 5 views
3

Полезно ли использовать закрытие вместо __all__, чтобы ограничить имена, открытые модулем Python? Это предотвратило бы случайное использование программистами неправильного имени для модуля (import urllib; urllib.os.getlogin()), а также избежание загрязнения пространства имен «from x import *» как __all__.Является ли закрытие Python хорошей заменой для `__all__`?

def _init_module(): 
    global foo 
    import bar 
    def foo(): 
     return bar.baz.operation() 
    class Quux(bar.baz.Splort): pass 
_init_module(); del _init_module 

против того же модуля с использованием __all__:

__all__ = ['foo'] 
import bar 
def foo(): 
    return bar.baz.operation() 
class Quux(bar.baz.Splort): pass 

функции могли бы просто принять этот стиль, чтобы не загрязнять пространство имен модуля:

def foo(): 
    import bar 
    bar.baz.operation() 

Это может быть полезно для большого пакета который хочет помочь пользователям отличить свой API от использования пакета API его и других модулей во время интерактивной интроспекции. С другой стороны, возможно, IPython должен просто различать имена в __all__ во время завершения табуляции, и больше пользователей должны использовать среду IDE, которая позволяет им переходить между файлами, чтобы увидеть определение каждого имени.

+0

Почему вы не используете 'from bar import foo' в коде с помощью этого модуля? Я чувствую, что мне что-то не хватает ... – jdb

+0

Второй бит кода не использует первый бит кода. он использует '__all__' для определения того же модуля. – joeforker

+0

Я согласен с jdb: какая проблема? Ваш метод _init_module очень странный, и я не понимаю, почему вы беспокоились? Сколько разработчиков случайно используют urllib.os.getlogin, например? Просто напишите свой код и решите реальные проблемы. –

ответ

4

Проблема с from x import * заключается в том, что он может скрыть NameErrors, что затрудняет поиск тривиальных ошибок. «загрязнение пространства имен» означает добавление материала в пространство имен, о котором вы не знаете, откуда оно взялось.

Какой вид вашего закрытия тоже. Кроме того, это может смутить IDE, контуры, пилинт и тому подобное.

Использование «неправильного» имени для модуля также не является реальной проблемой. Объекты модуля одинаковы из того места, где вы их импортируете. Если «неправильное» имя исчезает (после обновления), должно быть понятно, почему и мотивировать программиста сделать это правильно в следующий раз. Но это не вызывает ошибок.

+1

'from x import *' должен использоваться только в REPL. Хрупкий код не является ошибкой? – joeforker

7

Я являюсь поклонником написания кода, который абсолютно такой же простой в мозге, как может быть.

__all__ - это особенность Python, явно добавленная для решения проблемы ограничения того, какие имена становятся видимыми модулем. Когда вы его используете, люди сразу понимают, что вы с ним делаете.

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

EDIT: Теперь, когда я понимаю проблему немного лучше, вот альтернативный ответ.

В Python считается хорошей практикой префикс частных имен с подчеркиванием в модуле. Если вы делаете from the_module_name import *, вы получите все имена, которые не начинаются с подчеркивания. Таким образом, вместо трюка закрытия, я предпочел бы правильно использовать идиому начального-подчеркивания.

Обратите внимание, что если вы используете начальные имена подчёркивания, вам даже не нужно использовать __all__.

+0

Проблема в том, что '__all__' помогает только в том случае, если вы используете' from x import * ', что вы не должны делать в любом случае, в то время как трюк закрытия обеспечивает очень краткий интроспекции. В Plone, где любое определенное имя определяется где-то среди сотен яиц, десятков тысяч файлов python и миллионов строк кода, есть только небольшой шанс, что вы действительно прочитаете мой код и сможете заметить либо '__all__ 'или закрытие. – joeforker

1

Хорошо, я начинаю понимать этот вопрос немного больше. Закрытие действительно позволяет скрывать частные вещи. Вот простой пример.

Без закрытия:

# module named "foo.py" 
def _bar(): 
    return 5 

def foo(): 
    return _bar() - 2 

С закрытием:

# module named "fooclosure.py" 
def _init_module(): 
    global foo 
    def _bar(): 
     return 5 

    def foo(): 
     return _bar() - 2 

_init_module(); del _init_module 

Пример использования:

>>> import foo 
>>> dir(foo) 
['__builtins__', '__doc__', '__file__', '__name__', '__package__', '_bar', 'foo'] 
>>> 
>>> import fooclosure 
>>> dir(fooclosure) 
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'foo'] 
>>> 

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

Напротив, закрывающая версия foo() работает без _bar() существующего пространства имен. Я даже не уверен, что , как работает ... он держит ссылку на объект функции, созданный для _bar(), или он держит ссылку на пространство имен, которое все еще существует, так что оно может искать имя _bar() и найти его?

+0

См. Http://stackoverflow.com/questions/739654/understanding-python-decorators для более обычного использования замыкания. В Python 2.x foo() хранит _bar в foo.func_closure – joeforker

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