#python #function
#python #переадресация-объявление
Вопрос:
Возможно ли переадресовать-объявить функцию в 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
метод до его использования?
Иногда бывает трудно реорганизовать код, чтобы избежать этой проблемы. Например, при реализации некоторых форм рекурсии:
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
были определены ранее.
Это единственное решение для реорганизации кода и всегда помещать определения перед вызовами?
Ответ №1:
Оберните вызов в собственную функцию, чтобы
foo()
def foo():
print "Hi!"
произойдет сбой, но
def bar():
foo()
def foo():
print "Hi!"
bar()
будет работать должным образом.
Общее правило в Python заключается в том, что функция должна быть определена до ее использования, что не обязательно означает, что она должна быть выше в коде.
Комментарии:
1. 1 самый прямой ответ с концепцией ключевого камня: Pascal = определить выше, Python = определить раньше.
2. Это правильный ответ, он также объясняет, почему
if __name__=="__main__":
решение работает.3. Если я правильно понимаю, это означает, что пример spam() и eggs () OP в порядке, как написано. Это правильно?
4. @krubo да, все в порядке, как написано
5. У меня это не работает. Мне нужно
foo{}
быть определенным последним в файле, и я получаю ошибку при вызовеbar()
.
Ответ №2:
Если вы запустите свой скрипт с помощью следующего:
if __name__=="__main__":
main()
тогда вам, вероятно, не придется беспокоиться о таких вещах, как «переадресация объявления». Видите ли, интерпретатор загрузит все ваши функции, а затем запустит вашу функцию main(). Конечно, убедитесь, что у вас все импортированные файлы тоже правильные 😉
Если подумать, я никогда не слышал такого понятия, как «прямое объявление» в python… но опять же, я могу ошибаться 😉
Комментарии:
1. 1 наиболее практичный ответ: если вы поместите этот код в низ самого внешнего исходного файла, тогда вы можете определять в любом порядке.
2. Отличный совет; действительно помогает мне; поскольку я гораздо больше предпочитаю программирование «сверху вниз», а не снизу вверх.
3. В чем смысл строки ‘if name == » main «: ‘? Я могу просто написать myMain() (или любое другое название «основной» функции) в конце файла.
4. Не будет работать, если вы ссылаетесь на класс как на тип. например.
class X: def som(self, y: Y) ... class Y: ...
Ответ №3:
Если вы не хотите определять функцию до ее использования, и определить ее впоследствии невозможно, как насчет определения ее в каком-либо другом модуле?
Технически вы все еще определяете ее сначала, но она чистая.
Вы могли бы создать рекурсию, подобную следующей:
def foo():
bar()
def bar():
foo()
Функции Python анонимны так же, как анонимны значения, но они могут быть привязаны к имени.
В приведенном выше коде foo()
не вызывается функция с именем foo, она вызывает функцию, которая привязана к имени foo
в момент выполнения вызова. Можно переопределить foo
где-нибудь еще, и bar
затем вызвать новую функцию.
Ваша проблема не может быть решена, потому что это похоже на запрос получения переменной, которая не была объявлена.
Комментарии:
1. короче говоря, если у вас есть if __name__ == ‘__main__’: main() в качестве последней строки в вашем скрипте, все будет просто отлично!
2. @FilipePina Я не понял вашего комментария — почему вы не можете поместить последнюю строку кода так просто
main()
?3. @SanjayManohar: чтобы избежать ее выполнения на
import your_module
4. Я хотел бы добавить — иногда можно обойти эти проблемы, используя лямбды, поскольку они оцениваются позже.
5. W.r.t. «анонимный», вы действительно имеете в виду «объекты первого класса».
Ответ №4:
Я прошу прощения за возобновление этого потока, но здесь не обсуждалась стратегия, которая может быть применима.
Используя отражение, можно сделать что-то похожее на пересылку объявления. Например, допустим, у вас есть раздел кода, который выглядит следующим образом:
# 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()
if function_name = 'foo'
по причинам, рассмотренным выше, поскольку python должен искать каждую функцию перед ее вызовом. Если бы кто-то использовал timeit
модуль, чтобы посмотреть, как сравниваются эти два оператора, они имеют точно такие же вычислительные затраты.
Конечно, приведенный здесь пример очень бесполезен, но если у кого-то должна быть сложная структура, которая необходима для выполнения функции, но должна быть объявлена раньше (или структурно не имеет смысла иметь ее впоследствии), можно просто сохранить строку и попытаться вызвать функцию позже.
Ответ №5:
Если вызов 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() в конце файла.
Ответ №6:
В 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.
Вы можете думать, что тело функции — это просто еще один скрипт, который будет интерпретирован после вызова функции.
Ответ №7:
Иногда алгоритм проще всего понять сверху вниз, начиная с общей структуры и углубляясь в детали.
Вы можете сделать это без переадресации объявлений:
def main():
make_omelet()
eat()
def make_omelet():
break_eggs()
whisk()
fry()
def break_eggs():
for egg in carton:
break(egg)
# ...
main()
Ответ №8:
Нет, я не верю, что есть какой-либо способ переадресовать-объявить функцию в Python.
Представьте, что вы интерпретатор Python. Когда вы дойдете до строки
print "n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])
либо вы знаете, что такое cmp_configs, либо нет. Чтобы продолжить, вы должны
знайте cmp_configs. Не имеет значения, есть ли рекурсия.
Комментарии:
1. ну, это если вы выполняете только один проход по коду. Некоторые компиляторы (и я понимаю, что python интерпретируется) выполняют два прохода, чтобы эти вещи можно было вычислить. прямое объявление или, по крайней мере, какое-то обнаружение области действия, было бы действительно хорошим.
Ответ №9:
# 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!
Комментарии:
1. Это действительно работает, но Pylance скажет вам: «Объявление функции «foo» скрыто объявлением с тем же именем».
def foo(): pass
это не объявление или прототип, это определение функции, которая ничего не делает. Это мое понимание.
Ответ №10:
Вы не можете переадресовать-объявить функцию в Python. Если у вас есть логика, выполняемая до того, как вы определили функции, у вас, вероятно, в любом случае проблема. Поместите свое действие в if __name__ == '__main__'
в конце вашего скрипта (выполнив функцию, которую вы называете «main», если она нетривиальна), и ваш код станет более модульным, и вы сможете использовать его как модуль, если вам когда-нибудь понадобится.
Кроме того, замените это понимание списка выражением генератора (т. Е. print "n".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs))
)
Кроме того, не используйте cmp
, который устарел. Используйте key
и предоставляйте функцию меньшего размера.
Комментарии:
1. Как мне предоставить функцию меньшего размера?
2. Вместо cmp_configs вы бы определили функцию, которая принимает два аргумента и возвращает True, если первый меньше второго, и False в противном случае.
3. Для тех из нас, кто работает на C-подобном фоне, нет ничего необоснованного в том, что логика выполняется до определения функций. Подумайте: «многоходовой компилятор». Иногда требуется некоторое время, чтобы адаптироваться к новым языкам 🙂
Ответ №11:
Импортируйте сам файл. Предполагая, что файл вызван test.py:
import test
if __name__=='__main__':
test.func()
else:
def func():
print('Func worked')
Комментарии:
1. но вам нужно инкапсулировать весь ваш код с помощью имени if, чтобы избежать его выполнения два раза
2. Правильно, я в настоящее время часто использую: «‘if __name__ == ‘__main__’: из функции импорта теста func() else: def func(): print (‘Функция сработала’) «‘ Так что это в предпочтениях конечного пользователя.
Ответ №12:
TL; DR: Python не нуждается в пересылке объявлений. Просто поместите свои вызовы функций внутри определенийdef функций, и все будет в порядке.
def foo(count):
print("foo " str(count))
if(count>0):
bar(count-1)
def bar(count):
print("bar " str(count))
if(count>0):
foo(count-1)
foo(3)
print("Finished.")
Определения рекурсивной функции, совершенно успешно дают:
foo 3
bar 2
foo 1
bar 0
Finished.
Однако,
bug(13)
def bug(count):
print("bug never runs " str(count))
print("Does not print this.")
прерывается при вызове функции верхнего уровня, которая еще не была определена, и выдает:
Traceback (most recent call last):
File "./test1.py", line 1, in <module>
bug(13)
NameError: name 'bug' is not defined
Python — это интерпретируемый язык, такой же, как Lisp. В нем нет проверки типа, только вызовы функций во время выполнения, которые выполняются успешно, если имя функции было привязано, и завершаются неудачей, если оно не привязано.
Критически важно, что определение def функции не выполняет какие-либо функциональные вызовы внутри своих строк, оно просто объявляет, из чего будет состоять тело функции. Опять же, он даже не выполняет проверку типа. Итак, мы можем сделать это:
def uncalled():
wild_eyed_undefined_function()
print("I'm not invoked!")
print("Only run this one line.")
и он работает отлично (!), с выводом
Only run this one line.
Ключ — это разница между определениями и вызовами.
Интерпретатор выполняет все, что поступает на верхний уровень, что означает, что он пытается вызвать его. Если это не входит в определение.
В вашем коде возникли проблемы, потому что вы пытались вызвать функцию, в данном случае на верхнем уровне, до того, как она была привязана.
Решение состоит в том, чтобы поместить ваши вызовы функций не верхнего уровня внутри определения функции, а затем вызвать эту функцию намного позже.
Вопрос о «if __ main __» — это идиома, основанная на этом принципе, но вы должны понять почему, вместо того, чтобы просто слепо следовать ему.
Безусловно, есть гораздо более сложные темы, касающиеся лямбда-функций и динамической привязки имен функций, но это не то, о чем просил OP. Кроме того, они могут быть решены с использованием этих же принципов: (1) defs определяют функцию, они не вызывают свои строки; (2) у вас возникают проблемы, когда вы вызываете символ функции, который не связан.
Ответ №13:
Python не поддерживает переадресацию объявлений, но обычным обходным решением для этого является использование следующего условия в конце вашего скрипта / кода:
if __name__ == '__main__': main()
При этом он сначала прочитает весь файл, а затем оценит условие и вызовет функцию main(), которая сможет вызвать любую перенаправленную объявленную функцию, поскольку она уже сначала прочитала весь файл. Это условие использует специальную переменную, __name__
которая возвращает __main__
значение всякий раз, когда мы запускаем код Python из текущего файла (когда код был импортирован как модуль, затем __name__
возвращает имя модуля).
Ответ №14:
«просто реорганизуйте мой код, чтобы у меня не было этой проблемы». Правильно. Легко сделать. Всегда работает.
Вы всегда можете предоставить функцию до ее ссылки.
«Однако бывают случаи, когда это, вероятно, неизбежно, например, при реализации некоторых форм рекурсии»
Не вижу, как это даже отдаленно возможно. Пожалуйста, приведите пример места, где вы не можете определить функцию до ее использования.
Комментарии:
1. У меня такая ситуация. Я пытаюсь передать типы в декораторе функций, и типы определяются далее по модулю. Я не могу переместить нарушающие типы вверх, потому что это нарушило бы цепочку наследования.
2. Я исправил это, передав лямбда моему декоратору вместо фактического типа; но я бы не знал, как это исправить иначе (это не потребовало бы от меня изменения моего наследования)
3. » Легко сделать. Всегда работает «. — Нет. Это НЕ всегда работает. В очень особых случаях это не так.
Ответ №15:
Теперь подождите минуту. Когда ваш модуль достигает инструкции 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
И компилятор будет доволен. Просто не забудьте объявить real cmp_configs
перед вызовом fn
.
Ответ №16:
Python технически поддерживает прямое объявление.
если вы определяете функцию / класс, а затем устанавливаете для тела значение pass, у него будет пустая запись в глобальной таблице.
затем вы можете «переопределить» функцию / класс позже, чтобы реализовать функцию / класс.
однако, в отличие от прямого объявления c / c , это не работает из-за пределов области видимости (т. Е. Другого файла), поскольку у них есть свое собственное «глобальное» пространство имен
пример:
def foo(): pass
foo()
def foo(): print("FOOOOO")
foo()
foo
объявляется оба раза
однако при первом foo
вызове он ничего не делает, поскольку тело просто pass
но вызывается второй раз foo
. он выполняет новое тело print("FOOOOO")
но опять же. обратите внимание, что это не устраняет циклические зависимости. это потому, что файлы имеют свое собственное имя и имеют свои собственные определения функций
пример 2:
class bar: pass
print(bar)
это выводит <class '__main__.bar'>
но если бы это было объявлено в другом файле, это было бы <class 'otherfile.foo'>
я знаю, что этот пост старый, но я подумал, что этот ответ был бы полезен всем, кто продолжает находить этот пост после многих лет, в течение которых он был опубликован
Ответ №17:
Один из способов — создать функцию-обработчик. Определите обработчик на ранней стадии и поместите обработчик под всеми методами, которые вам нужно вызвать.
Затем, когда вы вызываете метод обработчика для вызова своих функций, они всегда будут доступны.
Обработчик может принимать аргумент 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)
Комментарии:
1. это кажется очень непитоническим. Python должен обрабатывать такого рода вещи сам по себе.
2. перечитайте принятый ответ. Python не нуждается в определении функции, пока вы не вызовете ее, а не просто используете ее в определении.