Python: предварительное определение функций

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Python: предварительное определение функций

Непрочитанное сообщение Olej » 26 янв 2019, 16:05

Столкнулся с этим при написании GUI приложения на Python с использованием модуля tkinker, использующего графику Tcl/Tk ... код полный здесь: Python - графика.

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

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

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: предварительное определение функций

Непрочитанное сообщение Olej » 26 янв 2019, 16:19

Olej писал(а): Потому как функции могут использовать только ранее определённые переменные, а операторы главного приложения могут вызывать только ранее определённые функции.
Моделирую проблему простейшим кодом (о его осмысленности не говорим ... осмысленный код иллюстрирующий ситуацию - в полном GUI приложении здесь: Python - графика):

Код: Выделить всё

#!/usr/bin/python3 
 
a = 1

def fun1():
    global a
    a += 1
    return a

b = fun1()

def fun2():
    global b
    return b + 1

a = fun2()

print( a, b )

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/python$ ./n0.py 
3 2

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: предварительное определение функций

Непрочитанное сообщение Olej » 26 янв 2019, 16:30

Olej писал(а):
Olej писал(а): Потому как функции могут использовать только ранее определённые переменные, а операторы главного приложения могут вызывать только ранее определённые функции.
Моделирую проблему простейшим кодом (о его осмысленности не говорим ...
Но вот такой номер уже не проходит:

Код: Выделить всё

#!/usr/bin/python3 

if __name__ == '__main__':
    a = 1
    b = fun1()
    a = fun2()
    print( a, b )

def fun1():
    global a
    a += 1
    return a

def fun2():
    global b
    return b + 1

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/python$ ./n0-2.py
Traceback (most recent call last):
  File "./n0-2.py", line 5, in <module>
    b = fun1()
NameError: name 'fun1' is not defined

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: предварительное определение функций

Непрочитанное сообщение Olej » 26 янв 2019, 16:47

Olej писал(а): А проблема в том, что если использовать глобальные переменные (а при использовании GUI пакетов это частая практика), или, например, глобальных флагов для управления потоками (а поток threading нельзя принудительно завершить другим способом извне) - то приходится перемежать код главного приложения и определения используемых функций.
В других, компилируемых языках программирования эта проблема известна и решают её по-разному...
Одни из старых языков - C и C++ - используют предварительное описание функций и требуют обязательного предварительного описания:

Код: Выделить всё

#include <stdio.h>

int a = 1;

int fun1( void ) {
   a += 1;
   return a;
}

int fun2( void ); // предварительное описание fun2

int b;

int main() {
   b = fun1();
   a = fun2();
   printf( "%d %d\n", a, b );
}

int fun2( void ) {
   return b + 1;
}

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/python$ gcc n1.c -o n1_c

olej@ACER:~/2019_WORK/own.WORK/python$ ./n1_c
3 2
На этом построена вся 45-летняя история использования хэдер файлов .h в этих языках.

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: предварительное определение функций

Непрочитанное сообщение Olej » 26 янв 2019, 16:58

Olej писал(а): Одни из старых языков - C и C++ - используют предварительное описание функций и требуют обязательного предварительного описания:
Один из новых языков Go (и ряд других тоже), используя отрицательный опыт предшественников C/C++, допускает прямое использование позже описанных функций, за счёт того, что компилятор просматривает исходный код в несколько проходов (во времена создания C на скорости обработки приходилось сильно экономить, а C++ вынужден был поддерживать синтаксическую совместимость с C):

Код: Выделить всё

package main

var a int = 1

func fun1() int {
   a += 1
   return a
}

var b int 

func main() {
   b = fun1()
   a = fun2()
   print( a, " ", b, "\n" );
}

func fun2() int {
   return b + 1
}
Проверил специально на 2-х разных доступных компиляторах - GCC и GoLang:

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/python$ gccgo n1.go -o n1_go 

olej@ACER:~/2019_WORK/own.WORK/python$ ./n1_go 
3 2

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/python$ go build -o n1_gl n1.go

olej@ACER:~/2019_WORK/own.WORK/python$ ./n1_gl
3 2

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: предварительное определение функций

Непрочитанное сообщение Olej » 26 янв 2019, 17:04

Olej писал(а): Но вот такой номер уже не проходит:
Всё это достаточно понятно ... потому что программа Python, точнее переменные (объекты) программы Python - это словарь, содержащий пары имя:значение, где имя является ключом словаря. Это хорошо видно, если чуть расширить начальное приложение:

Код: Выделить всё

#!/usr/bin/python3 
import sys

def pg():
    own = {}
    for key, val in zip( globals().keys(), globals().values() ):
        if key.find( '_' ) < 0: own[ key ] = val
    del own[ sys._getframe().f_code.co_name ], own[ 'sys' ]
    print( own )
 
a = 1
pg()

def fun1():
    global a
    a += 1
    return a

pg()

b = fun1()

pg()

def fun2():
    global b
    return b + 1

pg()

a = fun2()

print( a, b )

Вот как наполняется словарь по ходу выполнения:

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/python$ ./n1.py 
{'a': 1}
{'a': 1, 'fun1': <function fun1 at 0x7fb2a38a5d08>}
{'a': 2, 'fun1': <function fun1 at 0x7fb2a38a5d08>, 'b': 2}
{'a': 2, 'fun1': <function fun1 at 0x7fb2a38a5d08>, 'b': 2, 'fun2': <function fun2 at 0x7fb2a38a56a8>}
3 2

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: предварительное определение функций

Непрочитанное сообщение Olej » 26 янв 2019, 17:12

Olej писал(а): Но вот такой номер уже не проходит:
Зато вот такой проходит:

Код: Выделить всё

#!/usr/bin/python3 

def fun1():
    global a
    a += 1
    return a

def fun2():
    global b
    return b + 1

a = 1 
b = fun1()
a = fun2()
print( a, b )

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/python$ ./n0-1.py 
3 2
Казалось бы, это нарушает сказанное выше о том, что имя переменной должно быть объявлено раньше его использования (внутри функций). Но это не так - срабатывает квалификатор global (для a и b внутри функций), который требует использования глобального словаря на момент вызова функции.

Но только и этот способ не разрешает все проблемы для более сложных и объёмных случаев кода.

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: предварительное определение функций

Непрочитанное сообщение Olej » 26 янв 2019, 17:27

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

Код: Выделить всё

#!/usr/bin/python3 
 
class my():
    a = 1

    def __init__( self ):
        self.b = self.fun1()
        self.a = self.fun2()
        print( self.a, self.b )

    def fun1( self ):
        self.a += 1
        return self.a

    def fun2( self ):
        return self.b + 1

x = my()

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/python$ ./n2.py 
3 2
Здесь переменные и функции (методы) могут быть в любом порядке.
И почему - это тоже понятно: к моменту создания объекта класса словарь имён уже дополнен полным описанием класса и именами его методов.

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 5 гостей