Реальные примеры вложенных функций - PullRequest
11 голосов
/ 07 января 2010

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

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

Ответы [ 8 ]

10 голосов
/ 07 января 2010

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

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

8 голосов
/ 07 января 2010

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

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__
4 голосов
/ 07 января 2010

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

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

>>> 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]

РЕДАКТИРОВАТЬ: На практике генераторы были бы лучшим решением для этого, но пример показывает, как использовать преимущества вложенных функций.

2 голосов
/ 07 января 2010

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

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 голосов
/ 07 января 2010

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

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

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)
1 голос
/ 07 января 2010

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

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

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

1 голос
/ 07 января 2010

Python Decorators

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

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