Как мне передавать переменные между функциями в этом классе?

Что я пытаюсь сделать: выполняя скрипт, мне нужно будет ввести два числа, и он их сравнит. Я хочу, чтобы меня спросили в общей сложности 3 раза. В первый раз я наберу 10 и 5, во второй раз 5 и 10 и в третий раз я наберу 10 и 10, чтобы получить все три возможных ответа.

Моя проблема с первым кодом: getnumbers() вызывается внутри Checknumbers(). Я хочу создавать функции и цикл и строго ТОЛЬКО выполнять функции внутри выделенного цикла, а не внутри другой функции.

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

Я решил это с помощью класса, но я не совсем уверен, убиваю ли я язык или это обычная практика. Также я должен ссылаться на класс внутри функции checknumbers().

Первое решение:

def getnumbers():
    x = input("Enter the X number: ")
    y = input("Enter the Y number: ")
    return x, y

def checknumbers():
    x, y=getnumbers()
    if   x > y:
        print(f'x is larger then y: x is {x} and y is {y}')
    elif y > x:
        print(f"y is larger then x: x is {x} and y is {y}")
    elif y == x:
        print(f"x is equal to y: x is {x} and y is {y}")     
    else:
        print("Dont know mate")


n = 0
while(n < 3):
    checknumbers()
    n += 1

Это вариант с классом:

class ui:
    x = input("Enter the X number: ")
    y = input("Enter the Y number: ")


def checknumbers():
    if   ui.x > ui.y:    
        print(f'x is larger then y: x is {ui.x} and y is {ui.y}')
    elif ui.y > ui.x:
        print(f"y is larger then x: x is {ui.x} and y is {ui.y}")
    elif ui.y == ui.x:
        print(f"x is equal to y: x is {ui.x} and y is {ui.y}")     
    else:
        print("Dont know mate")

n = 0
while(n < 3):
    checknumbers()
    n += 1

Идеальное решение, так что обе функции getnumbers() и checknumbers начисто независимы друг от друга, и они вызываются внутри цикла while, проблема в том, что x и y из функции getnumbers() неизвестны checknumbers.

Требование: у меня не может быть ссылки на какую-либо другую функцию внутри моих функций, как мне передать x и y без ссылки на них?:

def getnumbers():
    x = input("Enter the X number: ")
    y = input("Enter the Y number: ")
    return x, y

def checknumbers():
    if   x > y:
        print(f'x is larger then y: x is {x} and y is {y}')
    elif y > x:
        print(f"y is larger then x: x is {x} and y is {y}")
    elif y == x:
        print(f"x is equal to y: x is {x} and y is {y}")     
    else:
        print("Dont know mate")


n = 0
while(n < 3):
    getnumbers()
    checknumbers()
    n += 1

person Barry    schedule 29.11.2018    source источник
comment
Добро пожаловать в Python и SO! Ваш вопрос в основном касается того, почему мы обычно используем атрибуты экземпляра (а не атрибуты класса) и как разложить класс на методы и атрибуты экземпляра, чтобы выполнить работу. См., например. Члены класса Python и другие подобные вопросы.   -  person smci    schedule 29.11.2018


Ответы (3)


  • You're getting confused between classes and instances, and between class attributes and instance attributes. (Read e.g. this)
    • The OO way to store state variables (like x,y) so you don't have to pass them around between function(/method) calls is to make them instance attributes. (Not class attributes, as you were doing. Don't worry, I did that too when I first learned Python).
    • Итак, мы объявляем класс UI; мы будем обращаться к его атрибутам экземпляра как self.x, self.y внутри его методов.
    • Не пытайтесь напрямую делать что-то в пользовательском интерфейсе класса. Сначала вы должны создать его экземпляр: ui = UI(). Вы должны следовать соглашению Python о том, что имена классов в верхнем регистре/верблюжьем регистре: UI, имена экземпляров в нижнем регистре, например. ui, ui1, ui2...
    • Вы пытались поместить код непосредственно в определение класса пользовательского интерфейса, а не определять методы и помещать туда код, а в вашем классе пользовательского интерфейса даже не было __init__()
    • Методы — это функции внутри класса, они всегда имеют первый аргумент self. Если бы они этого не сделали, метод не смог бы получить доступ к остальной части класса (!)
  • Now that we cleared that up, there are a couple of ways to decompose the methods to do what you want to do:
    1. Have an empty __init__() (you could just make its body do pass). Have get_numbers() and check_numbers() be separate methods, which you manually call in-order. This is what I show below and is closest to what you said you want ("I want no reference to any function inside another function"), but is bad decomposition - what if the client called check_numbers() before get_numbers()? It would blow up on TypeError since __init__() initializes x,y with None.
    2. Лучше было бы __init__() вызывать метод get_numbers() под капотом, чтобы гарантировать правильную инициализацию экземпляра. (Мы всегда можем снова позвонить get_numbers() позже, если захотим ввести новые числа). Это легко изменить, я оставляю это вам.
    3. В подходе 1 мы должны были инициализировать элементы экземпляра что-то (иначе попытка доступа к ним в check_numbers() приведет к краху). Итак, мы инициализируем значение None, которое намеренно выдает исключение при сравнении. На самом деле это не имеет значения, это просто плохая декомпозиция, поскольку __init__() не может правильно инициализировать экземпляр (и вызывать любые методы, необходимые для этого). Вот почему подход 2. лучше. Как правило, у вас всегда должен быть __init__(), который инициализирует класс в известном состоянии, чтобы можно было безопасно вызывать любой другой метод.

Код:

class UI:
    def __init__(self, x=None, y=None):
        self.x = x
        self.y = y
    def get_numbers(self):
        self.x = input("Enter the X number: ")
        self.y = input("Enter the Y number: ")
    def check_numbers(self):
        """This is bad decomposition because if the client calls check_numbers() before get_numbers(), the NoneType will throw a TypeError"""
        if   self.x > self.y:    
            print(f'x is larger then y: x is {self.x} and y is {self.y}')
        elif self.y > self.x:
            print(f'y is larger then x: x is {self.x} and y is {self.y}')
        elif self.y == self.x:
            print(f'x is equal to y: x is {self.x} and y is {self.y}')     
        else:
            print("Don't know mate")

# Declare an instance and reuse it three times    
ui = UI()
for n in range(3):
    ui.get_numbers()
    ui.check_numbers()

Также небольшие стилистические моменты:

  • вам не нужен цикл while для простого счетчика: n = 0, while(n < 3) ... n += 1 . Цикл for является однострочным: for n in range(3):
  • хороший стиль Python (см. PEP-8) состоит в том, чтобы называть методы lower_case_with_underscores, таким образом, get_numbers(), check_numbers()
  • Отличный способ проектирования класса сверху вниз — это сначала написать сигнатуры его методов, а затем подумать о том, какие методы и атрибуты вам понадобятся и как они будут работать вместе. Пример: «get_numbers() получит пользовательский ввод, поэтому нам понадобятся атрибуты self.x,y для хранения чисел, чтобы check_numbers() мог получить к ним доступ». И таким образом вы должны решить любые проблемы с дизайном классов, прежде чем вы напишете стену кода.
person smci    schedule 29.11.2018

Если вы не хотите вызывать getnumbers() внутри checknumbers(), единственной разумной альтернативой является передача чисел в качестве параметров в checknumbers().

def getnumbers():
    x = int(input("Enter the X number: "))
    y = int(input("Enter the Y number: "))
    return x,y

def checknumbers(x, y):
    if x > y:
        # etc.

...

for _ in range(3):
    x,y = getnumbers()
    checknumbers(x,y)

Это, по крайней мере, лучше разделяет интересы.

person khelwood    schedule 29.11.2018
comment
Передача его в качестве параметров не работает: Traceback (последний последний вызов): File /Users/bla/Documents/Python/test4.py, строка 23, в ‹module› checknumbers() TypeError: checknumbers() отсутствует 2 обязательных позиционных аргументы: «х» и «у» - person Barry; 29.11.2018
comment
@Barry Передача аргументов действительно работает, если вы делаете это правильно. В сообщении об ошибке говорится, что вы все еще пытаетесь вызвать функцию как checknumbers(). Должно быть checknumbers(x,y). - person khelwood; 29.11.2018
comment
действительно, я забыл (x, y) внутри контрольных чисел (x, y) внутри цикла. Но теперь я получаю: Traceback (последний последний вызов): File /Users/bla/Documents/Python/test4.py, строка 23, в ‹module› checknumbers(x,y) NameError: имя 'x' не определено - person Barry; 29.11.2018
comment
А теперь вы пропустили предыдущую строку, то есть x,y = getnumbers(). Смотрите код в моем ответе. - person khelwood; 29.11.2018

  • Я не вижу ничего плохого в первом решении (за исключением того факта, что getumbers возвращает строки в Python 3). Классы — не решение всех проблем

  • #P2#
    #P3#

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

person DeepSpace    schedule 29.11.2018
comment
Я хочу, чтобы они были абсолютно отдельными. Моим идеальным способом было бы иметь все связанные функции в одном файле и иметь цикл в другом. Довольно раздражает ссылка на другие функции внутри функции. Я не чувствую, что это правильно. - person Barry; 29.11.2018
comment
@Barry Ссылка на другие функции внутри функции довольно раздражает. Вам придется отпустить это. Совершенно невозможно написать функционирующий, понятный код, в котором ни одна функция не вызывает другую функцию. К вашему сведению, print - это функция (по крайней мере, в Python 3). Вы не можете использовать его, если не вызовете его из другой функции. - person DeepSpace; 29.11.2018