Является ли закрытие Python хорошей заменой `__all__`? - PullRequest
3 голосов
/ 27 октября 2009

Является ли хорошей идеей использовать закрытие вместо __all__ для ограничения имен, предоставляемых модулем Python? Это предотвратит случайное использование программистами неправильного имени для модуля (import urllib; urllib.os.getlogin()), а также исключит "from x import *" загрязнение пространства имен, например __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, которая позволяет им переходить между файлами, чтобы увидеть определение каждого имени.

Ответы [ 3 ]

7 голосов
/ 27 октября 2009

Я фанат написания кода, который настолько прост, насколько это возможно.

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

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

РЕДАКТИРОВАТЬ: Теперь, когда я немного лучше понимаю проблему, вот альтернативный ответ.

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

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

4 голосов
/ 27 октября 2009

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

Что и делает ваше закрытие. Кроме того, это может привести к путанице в IDE, набросках, пилинте и т. П.

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

1 голос
/ 27 октября 2009

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

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

# 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() и найти его?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...