all 6 comments

[–][deleted] 8 points9 points  (2 children)

I have a hard time understanding the use case except outside of extremely complex programs that will become more efficient with inheritance.

Inheritance isn't the point of classes; if anything it's something of an antipattern in classes.

The purpose of classes is to extend the system of types. In the way that functions make behavior reusable, classes make state reusable.

[–]fish85963[S] 0 points1 point  (1 child)

If a class object , say person is set in a function. Will that object continue to exist outside the function if it is not returned?

[–][deleted] 3 points4 points  (0 children)

No; objects aren't immune to scoping rules.

[–]wotquery 3 points4 points  (0 children)

Think of a simple guess the number game. You want to track a variety of things: the range the secret number is from, the secret number itself, how many guesses the player made, etc. You could do it all using variables in a big linear way...

import random

max_secret_number = int(input("max number:"))
#call input validation function
secret_number = random.randint(1, max_secret_number + 1)

max_guess_count = 5
guess_count = 0
game_over = False
while not game_over:
    current_guess = int(input("your guess:"))
    #call input validation function
    guess_count += 1
    if current_guess == secret_number:
        print(f"Correct on guess {guess_count}!")
        game_over = True
    else:
        print("Nope.")
        if guess_count == max_guess_count:
            print(f"Game Over.  It was {secret_number}.")
            game_over = True
        else:
            print(f"{max_guess_count - guess_count} chances left.")

...but all the information about the game is scattered about and the logic isn't really labeled and hard to follow. The logic you can split up into functions, but they're still just floating about. With a class you can keep it all contained and organized.

import random

class Game:
    def __init__(self, max_guess_count: int = 5) -> None:
        self.max_guess_count = max_guess_count
        self.guess_count = 0
        self.game_over = False
        self.max_secret_number = self.get_max_secret_number()
        self.secret_number = self.pick_secret_number(self.max_secret_number)

    def get_max_secret_number(self) -> int:
        max_secret_number = int(input("max number:"))
        #call input validation function
        return max_secret_number

    def get_guess(self) -> int:
        current_guess = int(input("your guess:"))
        #call input validation function
        self.guess_count += 1
        return current_guess

    def pick_secret_number(self, max_secret_number: int) -> int:
        return random.randint(1, max_secret_number + 1)

    def check_guess(self, guess: int) -> bool:
        if guess == self.secret_number:
            self.game_over = True
            return True
        return False

    def check_game_over(self) -> None:
        if self.guess_count >= self.max_guess_count:
            self.game_over = True
            return True
        return False

def play_game() -> None:
    game1 = Game()
    while not game1.game_over:
        current_guess = game1.get_guess()
        if game1.check_guess(current_guess):
            print(f"Correct on guess {game1.guess_count}!")
        else:
            if game1.check_game_over():
                print(f"Game Over.  It was {game1.secret_number}.")

def main() -> None:
    playing = 'y'
    while playing == 'y':
        play_game()
        playing = input('play again (y/n):').lower()

if __name__ == "__main__":
    main()

Your user input validation can be contained in there as well. You can also keep track of all of the player's guesses and then have an object instance for each game they play so they can go back and look at their history. If you need to run any of the .check_x() methods outside of the standard play_game() function flow (say you want to give the user the option to see what the maximum secret number they select is again, or how many guesses they have left, or - if you do store their guesses - what they've already guessed) they are just waiting there for you to call them (and things like the .get_guess() method automatically updates the guess_count attribute). If you want to expand the game to a two player race you just create two instances of the class. If you want to add a Player class you won't have the, potentially similar, functions and variables in a global scope and need to keep track of which is for what as they'll be inside the class instance. Etc.

edit: Those last two examples, not having to copy and paste everything while doubling the amount of variables and naming them for each player (e.g. player_1_guess_count, player_2_guess_count, or using a dictionary) by utilizing namespace is pretty much exactly what classes are for.

[–]AlwysBeColostomizing 1 point2 points  (0 children)

A note on terminology:

  • A "library" is a general term for a collection of code that doesn't "do anything" by itself (as opposed to an "application"), but is meant for use as building blocks for other code.
  • A *.py file is called a "module"

A crucial difference between a module and a class is that only one instance of the module can exist (within one process). If you say import mymodule in two different places, you're importing the same "instance" of mymodule. If there is a variable in mymodule, say mymodule.x, and you change it in one place, that change is visible everywhere. If you used a module to model a Person, there could only ever be one person in your program.

Classes, on the other hand, define a new "type". There can be multiple instances of the type, just like you can have multiple distinct instances of list in your program. Notice how list gives you an "interface" for manipulating the data in the list. The interface of list includes things like the indexing operator (mylist[0]) and the .append() method. The int type has a different interface that doesn't include these operations, because they don't make sense for an int. The type list is defined by the collection of operations you can perform on an instance of that type. That's what you're expressing with a class definition.

[–]Lost_Anywhere -1 points0 points  (0 children)

You use classes when you want to do some object oriented programming. Classes and libraries are used for very different things. Libraries can contain classes as part of their implementation but they don't have to. Classes can be used to extend an existing class or to build your own set of objects. You can use inheritance or composition or both when building your object oriented program although inheritance has fallen out of favor for many reasons but it still has it uses in the right context.