Реальные примеры вложенных функций

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

Большое спасибо

11
задан Community 23 May 2017 в 12:30
поделиться

8 ответов

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

Одна очень простая причина использовать вложенную функцию - это просто то, что определяемая вами функция не обязательно должна быть глобальной, потому что только вложенная функция ее использует. Типичный пример из стандартного библиотечного модуля Python's quopri.py:

def encode(input, output, quotetabs, header = 0):
    ...
    def write(s, output=output, lineEnd='\n'):
        # RFC 1521 requires that the line ending in a space or tab must have
        # that trailing character encoded.
        if s and s[-1:] in ' \t':
            output.write(s[:-1] + quote(s[-1]) + lineEnd)
        elif s == '.':
            output.write(quote(s) + lineEnd)
        else:
            output.write(s + lineEnd)

    ...  # 35 more lines of code that call write in several places

Здесь был общий код внутри функции encode, так что автор просто учёл его в функции write.


Другим общим использованием для вложенных функций является re.sub. Вот код из стандартного библиотечного модуля json/encode.py:

def encode_basestring(s):
    """Return a JSON representation of a Python string

    """
    def replace(match):
        return ESCAPE_DCT[match.group(0)]
    return '"' + ESCAPE.sub(replace, s) + '"'

Здесь ESCAPE является регулярным выражением, а ESCAPE.sub(replace, s) находит все совпадения ESCAPE в s и заменяет каждое из них на replace(match).


На самом деле, любой API, например re.sub, который принимает функцию в качестве параметра, может привести к ситуациям, когда вложенные функции удобны. Например, в turtle.py есть какой-то глупый демонстрационный код, который делает это:

    def baba(xdummy, ydummy):
        clearscreen()
        bye()

    ...
    tri.write("  Click me!", font = ("Courier", 12, "bold") )
    tri.onclick(baba, 1)

onclick ожидает, что вы передадите функцию-обработчик событий, поэтому мы определяем ее и передаем в нее.

10
ответ дан 3 December 2019 в 02:52
поделиться

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

def create_adder(x):
   def _adder(y):
       return x + y
   return _adder

add2 = create_adder(2)
add100 = create_adder(100)

>>> add2(50)
52
>>> add100(50)
150
2
ответ дан 3 December 2019 в 02:52
поделиться

Декораторы Python

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

1
ответ дан 3 December 2019 в 02:52
поделиться

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

def entry_exit(f):
    def new_f(*args, **kwargs):
        print "Entering", f.__name__
        f(*args, **kwargs)
        print "Exited", f.__name__
    return new_f

@entry_exit
def func1():
    print "inside func1()"

@entry_exit
def func2():
    print "inside func2()"

func1()
func2()
print func1.__name__
8
ответ дан 3 December 2019 в 02:52
поделиться

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

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

>>> def fib(n):
        def rec():
            return fib(n-1) + fib(n-2)

        if n == 0:
            return 0
        elif n == 1:
            return 1
        else:
            return rec()

>>> map(fib, range(10))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

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

4
ответ дан 3 December 2019 в 02:52
поделиться

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

from functools import wraps
from types import InstanceType



def printCall(func):
   def getArgKwargStrings(*args, **kwargs):
      argsString = "".join(["%s, " % (arg) for arg in args])
      kwargsString = "".join(["%s=%s, " % (key, value) for key, value in kwargs.items()])
      if not len(kwargs):
         if len(argsString):
            argsString = argsString[:-2]
      else:
         kwargsString = kwargsString[:-2]
      return argsString, kwargsString

   @wraps(func)
   def wrapper(*args, **kwargs):
      ret = None
      if args and isinstance(args[0], InstanceType) and getattr(args[0], func.__name__, None):
         instance, args = args[0], args[1:]
         argsString, kwargsString = getArgKwargStrings(*args, **kwargs)
         ret = func(instance, *args, **kwargs)
         print "Called %s.%s(%s%s)" % (instance.__class__.__name__, func.__name__, argsString, kwargsString)
         print "Returned %s" % str(ret)
      else:
         argsString, kwargsString = getArgKwargStrings(*args, **kwargs)
         ret = func(*args, **kwargs)
         print "Called %s(%s%s)" % (func.__name__, argsString, kwargsString)
         print "Returned %s" % str(ret)
      return ret
   return wrapper


def sayHello(name):
   print "Hello, my name is %s" % (name)

if __name__ == "__main__":
   sayHelloAndPrintDebug = printCall(sayHello)
   name = "Nimbuz"
   sayHelloAndPrintDebug(name)

Игнорируйте все мумбо-джумбо в функции "printCall" на данный момент и сфокусируйтесь только на функции "sayHello" и ниже. Здесь мы хотим распечатать, как функция "sayHello" вызывалась каждый раз, когда она вызывалась, не зная и не изменяя того, что делает функция "sayHello". Итак, мы переопределим функцию "sayHello", передав её на "printCall", которая возвращает НОВУЮ функцию, которая делает то, что делает функция "sayHello" И выводит на печать, как была вызвана функция "sayHello". Таково понятие декораторов.

Помещение "@ printCall" над определением "sayHello" приводит к тому же результату:

@printCall
def sayHello(name):
   print "Hello, my name is %s" % (name)

if __name__ == "__main__":
   name = "Nimbuz"
   sayHello(name)
2
ответ дан 3 December 2019 в 02:52
поделиться

Они полезны при использовании функций, которые принимают на вход другие функции. Предположим, вы находитесь в функции и хотите отсортировать список элементов на основе значений элементов в dict:

def f(items):
    vals = {}
    for i in items: vals[i] = random.randint(0,100)
    def key(i): return vals[i] 
    items.sort(key=key)

Вы можете просто определить ключ прямо здесь и использовать vals , локальная переменная.

Другой вариант использования - обратные вызовы.

2
ответ дан 3 December 2019 в 02:52
поделиться

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

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

-121--2904449-

Добавление опции -L (например, gcc -L -ahl ) может предоставить несколько более понятные списки.

Эквивалентный параметр MSVC - /FACs (и это немного лучше, потому что он пересекает исходный, машинный язык и двоичный, и включает некоторые полезные комментарии).


Примерно одна треть моей работы состоит в том, чтобы делать именно то, что вы делаете: жонглировать C-кодом вокруг и затем смотреть на выходные данные сборки, чтобы убедиться, что она была правильно оптимизирована (что предпочтительно, чтобы просто записывать встроенную сборку по всему месту).

Блоги и статьи для разработки игр могут быть хорошим ресурсом для темы, так как игры фактически являются приложениями реального времени в постоянной памяти - У меня есть некоторые заметки к ней , как и Майк Эктон , и другие. Обычно мне нравится держать ссылку на набор команд Intel в окне во время просмотра объявлений.

Самое полезное - сначала получить хорошее базовое представление о программировании сборки - не потому, что вы хотите написать код сборки, а потому, что это делает разборку чтения намного проще. Мне пришлось нелегко найти хороший современный учебник.

-121--2904445-

OK, кроме декораторов: Скажем, у вас было приложение, где вам нужно было отсортировать список последовательностей по подстрокам, которые время от времени менялись. Теперь функции sorted принимают аргумент key = , который является функцией одного аргумента: Предметы (последовательности в данном случае) для сортировки. Как определить функцию, по которой нужно сортировать подстроки? Замыкание или вложенная функция идеально подходит для этого:

def sort_key_factory(start, stop):
    def sort_key(string):
        return string[start: stop]
    return sort_key

Просто? Вы можете расширить его, заключив начало и конец в кортеж или объект среза, а затем передав последовательность или итепригодность этих объектов в sort_key_factory.

1
ответ дан 3 December 2019 в 02:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: