Возможно ли переадресовать-объявить функцию в Python?

StackOverflow https://stackoverflow.com/questions/1590608

  •  22-09-2019
  •  | 
  •  

Вопрос

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

print "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

Я организовал свой код таким образом, чтобы поместить определение cmp_configs метод после вызова.Это приводит к сбою с этой ошибкой:

NameError: name 'cmp_configs' is not defined

Есть ли какой-нибудь способ "объявить" cmp_configs метод до того, как он будет использован?Это сделало бы мой код более чистым?

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

Рассмотрим этот случай, когда в Python было бы необходимо прямое объявление функции:

def spam():
    if end_condition():
        return end_result()
    else:
        return eggs()

def eggs():
    if end_condition():
        return end_result()
    else:
        return spam()

Где end_condition и end_result были ранее определены.

Является ли единственным решением реорганизовать код и всегда помещать определения перед вызовами?

Это было полезно?

Решение

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

Технически вы все еще определяете это первым, но это чисто.

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

def foo():
    bar()

def bar():
    foo()

Функции Python анонимны точно так же, как анонимны значения, однако они могут быть привязаны к имени.

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

Ваша проблема не может быть решена, потому что это все равно что просить получить переменную, которая не была объявлена.

Другие советы

Что вы можете сделать, так это обернуть вызов в собственную функцию.

Так что

foo()

def foo():
    print "Hi!"

сломается, но

def bar():
    foo()

def foo():
    print "Hi!"

bar()

будет работать должным образом.

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

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

Если вы запустите свой скрипт с помощью следующего:

if __name__=="__main__":
   main()

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

Если подумать, я никогда не слышал такого понятия, как "прямое объявление" в python...но опять же, я могу ошибаться ;-)

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

def a():
  b()  # b() hasn't been defined yet, but that's fine because at this point, we're not
       # actually calling it. We're just defining what should happen when a() is called.

a()  # This call fails, because b() hasn't been defined yet, 
     # and thus trying to run a() fails.

def b():
  print "hi"

a()  # This call succeeds because everything has been defined.

В общем, помещение вашего кода внутрь функций (таких как main()) решит вашу проблему;просто вызовите main() в конце файла.

Я приношу извинения за возобновление этой темы, но здесь не обсуждалась стратегия, которая может быть применима.

Используя отражение, можно сделать что-то похожее на прямое объявление.Например, допустим, у вас есть раздел кода, который выглядит следующим образом:

# We want to call a function called 'foo', but it hasn't been defined yet.
function_name = 'foo'
# Calling at this point would produce an error

# Here is the definition
def foo():
    bar()

# Note that at this point the function is defined
    # Time for some reflection...
globals()[function_name]()

Итак, таким образом, мы определили, какую функцию мы хотим вызвать до того, как она будет фактически определена, фактически прямое объявление.В python оператор globals()[function_name]() это то же самое , что foo() если function_name = 'foo' по причинам, рассмотренным выше, поскольку python должен выполнять поиск в каждой функции перед ее вызовом.Если бы кто-то должен был использовать timeit модуль, чтобы увидеть, как сравниваются эти два оператора, они имеют точно такие же вычислительные затраты.

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

Нет, я не верю, что есть какой-либо способ переадресовать-объявить функцию в Python.

Представьте, что вы интерпретатор Python.Когда ты дойдешь до очереди

print "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

либо вы знаете, что такое cmp_configs, либо нет.Чтобы продолжить, вы должны знать cmp_configs.Не имеет значения, есть ли рекурсия.

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

Рассмотрим следующий пример:

def a():
   b() # won't be resolved until a is invoked.

def b(): 
   print "hello"

a() # here b is already defined so this line won't fail.

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

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

Вы можете сделать это без предварительных объявлений:

def main():
  make_omelet()
  eat()

def make_omelet():
  break_eggs()
  whisk()
  fry()

def break_eggs():
  for egg in carton:
    break(egg)

# ...

main()

Вы не можете повторно объявить функцию в Python.Если у вас есть логика, выполняющаяся до того, как вы определили функции, у вас, вероятно, все равно возникла проблема.Поместите свое действие в if __name__ == '__main__' в конце вашего скрипта (выполняя функцию, которую вы называете "main", если она нетривиальна) ваш код станет более модульным, и вы сможете использовать его как модуль, если вам когда-нибудь понадобится.

Кроме того, замените понимание этого списка выражением генератора (т.е., print "\n".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs)))

Кроме того, не используйте cmp, который устарел.Использование key и обеспечивают меньшую, чем функция.

Импортируйте сам файл.Предполагая, что файл вызывается test.py:

import test

if __name__=='__main__':
    test.func()
else:
    def func():
        print('Func worked')
# declare a fake function (prototype) with no body
def foo(): pass

def bar():
    # use the prototype however you see fit
    print(foo(), "world!")

# define the actual function (overwriting the prototype)
def foo():
    return "Hello,"

bar()

Выходной сигнал:

Hello, world!

"просто реорганизуйте мой код, чтобы у меня не было этой проблемы". Исправьте.Легко сделать.Всегда срабатывает.

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

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

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

А теперь подождите минутку.Когда ваш модуль достигнет инструкции print в вашем примере, перед cmp_configs было определено, что именно вы ожидаете от этого?

Если ваша публикация вопроса с использованием print действительно пытается представить что-то вроде этого:

fn = lambda mylist:"\n".join([str(bla)
                         for bla in sorted(mylist, cmp = cmp_configs)])

тогда нет требования определять cmp_configs перед выполнением этого оператора просто определите его позже в коде, и все будет хорошо.

Теперь, если вы пытаетесь ссылаться cmp_configs в качестве значения по умолчанию аргумента для лямбда-выражения, то это совсем другая история:

fn = lambda mylist,cmp_configs=cmp_configs : \
    "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

Теперь вам нужен cmp_configs переменная, определенная до того, как вы дойдете до этой строки.

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

К счастью, Python, будучи таким приспособленным к типу, как есть, не заботится что вы определяете как cmp_configs, так что вы могли бы просто предварить это утверждение:

cmp_configs = None

И компилятор будет доволен.Просто не забудьте заявить о реальном cmp_configs прежде чем вы когда-либо призовете fn.

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

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

Обработчик может принять аргумент nameOfMethodToCall.Затем использует набор операторов if для вызова правильного метода.

Это решило бы вашу проблему.

def foo():
    print("foo")
    #take input
    nextAction=input('What would you like to do next?:')
    return nextAction

def bar():
    print("bar")
    nextAction=input('What would you like to do next?:')
    return nextAction

def handler(action):
    if(action=="foo"):
        nextAction = foo()
    elif(action=="bar"):
        nextAction = bar()
    else:
        print("You entered invalid input, defaulting to bar")
        nextAction = "bar"
    return nextAction

nextAction=input('What would you like to do next?:')

while 1:
    nextAction = handler(nextAction)

Да, мы можем это проверить.

Входные данные

print_lyrics() 
def print_lyrics():

    print("I'm a lumberjack, and I'm okay.")
    print("I sleep all night and I work all day.")

def repeat_lyrics():
    print_lyrics()
    print_lyrics()
repeat_lyrics()

Выходной сигнал

I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.

Как упоминал Би Джей Гомер в приведенных выше комментариях, общее правило в Python заключается не в том, что функция должна быть определена выше в коде (как в Pascal), а в том, что она должна быть определена перед ее использованием.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top