Могу ли я определить область в любом месте Python?

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

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

Мне бы хотелось узнать, возможно ли это как-то

Ответ 1

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

Обычная техника в 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__ обсуждается в docs для инструкции импорта.

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

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

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

  • настройка неудобна (нетипичное и таинственное использование функций)
  • вам нужно объявить глобальными любые назначения, которые вы хотите сделать, которые не являются временными.
  • код не будет работать, пока вы не вызовете функцию
 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

Ответ 2

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

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:
    ...

Ответ 3

Просто выполните функцию?

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()

Ответ 4

Короткий ответ: "Нет".

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 - это способ заявить, что вы намерены изменить переменную, которая не входит в объем функции.

Ответ 5

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

Ответ 6

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

  • Во-первых, метод определения псевдонима для длинного префикса, как описано Раймондом Хеттингером, - это ОДИН, чтобы использовать.

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

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

Итак, можно создать script имени "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

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

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

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

Ответ 7

В общем, типизация не является трудной частью написания программного обеспечения, но если вы настаиваете:

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.

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