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

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

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

8 ответов


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

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

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

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__

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

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

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


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

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)

игнорируйте все mumbo jumbo в функции " 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)

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

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

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


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

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

В Python Декораторы

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


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

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

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