Могу ли я определить область где-нибудь в Python? - PullRequest
11 голосов
/ 08 ноября 2011

Иногда я нахожу, что мне нужно использовать функции с длинными именами, такими как os.path.abspath и os.path.dirname a lot в нескольких строках кода.Я не думаю, что стоит засорять глобальное пространство имен такими функциями, но было бы невероятно полезно иметь возможность определять область вокруг строк, где мне нужны эти функции.Например, это было бы идеально:

import os, sys

closure:
    abspath = os.path.abspath
    dirname = os.path.dirname

    # 15 lines of heavy usage of those functions

# Can't access abspath or dirname here

Я бы хотел знать, выполнимо ли это как-то

Ответы [ 7 ]

19 голосов
/ 08 ноября 2011

В Python нет временного инструмента пространства имен, такого как let в Лиспе или Схеме.

Обычная техника в Python - помещать имена в текущее пространство имен и затем удалять их, когда вы закончите с ними. Эта техника интенсивно используется в стандартной библиотеке:

abspath = os.path.abspath
dirname = os.path.dirname
# 15 lines of heavy usage of those functions
a = abspath(somepath)
d = dirname(somepath)
...
del abspath, dirname

Альтернативный способ уменьшить усилие при наборе текста - сократить повторяющийся префикс:

>>> import math as m
>>> m.sin(x / 2.0) + m.sin(x * m.pi)

>>> p = os.path
...
>>> a = p.abspath(somepath)
>>> d = p.dirname(somepath)

Другая техника, обычно используемая в стандартной библиотеке, состоит в том, чтобы просто не беспокоиться о загрязнении пространства имен модуля и просто полагаться на __ all __ , чтобы получить список имен, которые вы намереваетесь опубликовать. Влияние __ all __ обсуждается в документах для оператора импорта .

Конечно, вы также можете создать свое собственное пространство имен, сохраняя имена в словаре (хотя это решение не распространено):

d = dict(abspath = os.path.abspath,
         dirname = os.path.dirname)
...
a = d['abspath'](somepath)
d = d['dirname'](somepath)

Наконец, вы можете поместить весь код в функцию (которая имеет собственное локальное пространство имен), но это имеет ряд недостатков:

  • установка неудобна (нетипичное и загадочное использование функций)
  • вам нужно объявить как global любые назначения, которые вы хотите сделать, которые не являются временными.
  • код не будет запущен, пока вы не вызовете функцию
 def temp():                        # disadvantage 1: awkward setup
    global a, d                     # disadvantage 2: global declarations
    abspath = os.path.abspath
    dirname = os.path.dirname
    # 15 lines of heavy usage of those functions
    a = abspath(somepath)
    d = dirname(somepath)
 temp()                             # disadvantage 3: invoking the code
5 голосов
/ 08 ноября 2011

Этот тип делает то, что вы хотите, но вы должны повторить имена

try:
    abspath = os.path.abspath
    dirname = os.path.dirname
    # fifteen lines of code
finally:
    del abspath
    del dirname

Это позволяет избежать загрязнения пространства имен, если есть исключение в ситуации, как показано ниже

try:
    ...
    try:
        abspath = os.path.abspath
        dirname = os.path.dirname
        # fifteen lines of code
    finally:
        del abspath
        del dirname

    ... # don't want abspath or dirname in scope here even if there was
    ... # an exception in the above block

except:
    ...
2 голосов
/ 08 ноября 2011

Краткий ответ: «Нет».

Python имеет три области действия. У него есть область действия функции, глобальная область видимости (также известный как модуль) и встроенная область видимости. Вы не можете объявить другие области.

Объявление class выглядит как область видимости, но это не так. Это в основном сокращение для назначения группы полей на объекте. Функции в этом классе не могут получить доступ к этим полям, не пройдя объект, для которого они определены.

Это звучит немного более ограниченно, чем есть. В Python вы также можете вкладывать определения функций. Определение вложенной функции получает доступ только для чтения к внешней области. Это «динамично». Имя не должно быть упомянуто до определения функции. Вот пример:

def joe(x):
    def bar():
        return y
    def baz(z):
        y = x + 20
        return x
    y = x+5
    return bar, baz

>>> a, b = joe(5)
>>> b(20)
5
>>> a()
10

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

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

По-видимому, в Python3 есть способ «импортировать» переменную из охватывающей области с помощью ключевого слова nonlocal (аналог ключевого слова global), чтобы вы могли использовать его в контексте чтения / записи:

def joe(x):
    def bar():
        return y
    def baz(z):
        nonlocal y
        y = x + 20
        return x
    y = x+5
    return bar, baz

>>> a, b = joe(5)
>>> b(20)
5
>>> a()
25

В противном случае, когда Python видит переменную слева от знака =, он предполагает, что вы создаете новую локальную переменную. Ключевые слова global и nonlocal указывают на то, что вы намереваетесь изменить переменную, которая не входит в область действия функции.

1 голос
/ 08 ноября 2011

Просто сделать функцию?

def do_things_with_those_functions():
    abspath = os.path.abspath
    dirname = os.path.dirname
    # etc.

Вы можете сделать это в любой области:

def outer_function():
    a = 5
    def inner_function():
        print(a)
    inner_function()
0 голосов
/ 08 ноября 2011

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

  • ДляВо-первых, метод определения псевдонима для длинного префикса, описанный Рэймондом Хеттингером, является ЕДИНСТВЕННЫМ для применения.

  • Во-вторых, я удивлен, что никто не прибегал кимпорт модуля, в котором находятся инструкции и строки, которые вы считаете тяжелыми и мусорными.

Кстати, если вы получаете доступ к функциям с помощью os.path.abspath и os.path.dirnames, неверно говорить, что функции (я полагаю, вы имеете в виду их имена) засоряют пространство имен.Поскольку они принадлежат модулю os или os.path (зависит от того, какой из них был импортирован), существует только имя модуля 'os' или 'os.path' в пространстве имен и объект модуля в памяти, но не имена функций непосредственно в пространстве имен.

Таким образом, можно создать скрипт имени"heavy_code.py":

def doing(x):
    from os.path import abspath as a,dirname as d
    ## Execute all the desired processes and creations
    def fufu(s,t):
        return s+t
    dedex = d[x]
    #.........
    #...........
    #........
    #............
    #..........

    ## Return to the calling scope all that is needed there
    return (dedex,fufu)

И в главном модуле с учетом ответа gnibbler:

one_path = 'I:/all/magala/zeru/kiol.py'
try:
    from pp.bududu import doing
    w,ff = doing(one_path)
finally:
    del doing

.

Чтобы увидеть, как это работает:

one_path = 'I:/all/magala/zeru/kiol.py'
try:
    from pp.bududu.heavy_code import doing
    print "in try : "
    print dir()
    print "executing doing()"
    w,ff = doing(one_path)
    print dir()
finally:
    del doing

print "\nafter finally : "
print dir()
print '\nw ==',w
print 'ff(10,12) ==',ff(10,12)

в результате выдает:

in try :
['__builtins__', '__doc__', '__name__', '__package__', 'doing', 'one_path']
executing doing()
['__builtins__', '__doc__', '__name__', '__package__', 'doing', 'ff', 'one_path', 'w']

after finally :
['__builtins__', '__doc__', '__name__', '__package__', 'ff', 'one_path', 'w']

w == I:/all/magala/zeru
ff(10,12) == 22

После выполнения фрагмента функция do () больше не существует в основном модуле,но объекты, созданные в результате выполнения working () , теперь лежат в нем без путаницы имен в пространстве имен основного модуля.Кроме того, все идентификаторы, необходимые внутри функции working () , являются локальными для нее.

Создание всех требуемых и необходимых объектов может быть делегировано модулю heavy_code сколько бы их ни было, при импорте и выполнении функции working () занимает только две строки в главном модуле, а функция working () в heavy_code плюс его вызывающая линия может быть легко изменена.

Разве не для этого предназначен модуль?

0 голосов
/ 08 ноября 2011

Как правило, набор текста не является сложной частью написания программного обеспечения, но если вы настаиваете:

class LocalNamespace(object):
    def __enter__(self, *args, **kwargs):
        pass
    def __exit__(self, *args, **kwargs):
        pass
 with LocalNamespace() as foo:
     abspath = os.path.abspath
     dirname = os.path.dirname
     # etc.

Надеюсь, это поможет.

0 голосов
/ 08 ноября 2011

Вы можете определять функции где угодно, вызывать их, а затем удалять их. Но нет способа иметь анонимную функцию, которая может использовать операторы.

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