Próba przekszałcenie kodu proceduralnego w typowo obiektowy

Próba przekszałcenie kodu proceduralnego w typowo obiektowy
P7
  • Rejestracja:ponad 3 lata
  • Ostatnio:ponad 2 lata
  • Postów:75
0

Witam ponownie . Zacząłem się bawić w obiektówkę . Przykładowo mam kod proceduralny z liczeniem wieku :

Kopiuj
import datetime

def pobierz_imie():
    return input('podaj imie: ')

def pobierz_rok_urodzenia():
    return int(input('podaj rok urodzenia: '))

def policz_ile_lat(rok_urodzenia, aktualny_rok):
    return aktualny_rok - rok_urodzenia

def odpowiedz_uzytkownikowi(imie, wiek):
    print(f'Czesc {imie}! Masz {wiek} lat.')

while True:
    imie = pobierz_imie()
    rok_urodzenia = pobierz_rok_urodzenia()
    aktualny_rok = datetime.datetime.now().year
    wiek = policz_ile_lat(rok_urodzenia, aktualny_rok)
    odpowiedz_uzytkownikowi(imie, wiek)

Kod w obiektówce:

Kopiuj
import datetime

class Dane:
    def __init__(self):
        self.imie = self.Pobierz_imie   #konstruktor/  - powinno być self.imie = imie /jak takzrobię mam bład
        self.rok_urodzenia = self.pobierz_rok_urodzenia()
        self.aktualny_rok = datetime.datetime.now().year
        self.wiek = self.policz_ile_lat(self.rok_urodzenia, self.aktualny_rok)
        self.odpowiedz_uzytkownikowi(self.imie, self.wiek)

    def Pobierz_imie(self):
        return input('Podaj imie: ')

    def pobierz_rok_urodzenia(self):
        return int(input('podaj rok urodzenia: '))
        

    def policz_ile_lat(self,rok_urodzenia, aktualny_rok):
        return self.aktualny_rok - self.rok_urodzenia

    def odpowiedz_uzytkownikowi(self,imie,wiek):
        print(f'Czesc {self.imie}! Masz {self.wiek} lat.')

    def wyswietl(self):
      #print(self.Pobierz_imie,self.pobierz_rok_urodzenia,self.odpowiedz_uzytkownikowi,self.policz_ile_lat)
      print(self.imie,self.aktualny_rok,self.rok_urodzenia,self.wiek)
      

dane1 = Dane()
dane1.wyswietl()

I ten program próbuję przekształcić w typową obiektówkę... Niby wynik obliczenia wieku mi wychodzi (z błedami) , ale nie mam pytania o imie, żebym podał.... Tylko od razu pytanie o rok urodzenia... Czy w konstruktorze klasy dobrze dodałem te wszystkie "dane"? Powinno być self.imie = imie, self.rok_urodzenia = rok_urodzenia itp..., a zrobiłem trochę inaczej ! Czy jest możliwość całkowitego przerobienia tego programu w typową "obiektówkę" Ogólnie czy dobrze kombinuję, jeśli chodzi o przerobienie tego kodu na obiektówkę? Chętnie bym zobaczył prawidlowy kod obiektowy według was, zebym sobie porównał lub wskazówki co robię zle. Pozdrawiam

OR
OR
  • Rejestracja:ponad 3 lata
  • Ostatnio:prawie 3 lata
  • Postów:6
2

W konstruktorze powinno być self.imie = self.Pobierz_imie(), dlatego od razu pyta o rok urodzenia.
Generalnie to rozdziela się pobieranie danych od obiektów na których pracujesz, a dane umieszczasz w obiekcie przekazując je przez konstruktor np.

Kopiuj
import datetime


class Dane:
    def __init__(self, imie, rok_urodzenia):
        self.imie = imie
        self.rok_urodzenia = rok_urodzenia
        self.aktualny_rok = datetime.datetime.now().year
        self.wiek = self.policz_ile_lat()
        self.odpowiedz_uzytkownikowi(self.imie, self.wiek)

    def policz_ile_lat(self):
        return self.aktualny_rok - self.rok_urodzenia

    def odpowiedz_uzytkownikowi(self):
        print(f'Czesc {self.imie}! Masz {self.wiek} lat.')

    def wyswietl(self):
        print(self.imie, self.aktualny_rok, self.rok_urodzenia, self.wiek)


def Pobierz_imie():
    return input('Podaj imie: ')


def pobierz_rok_urodzenia():
    return int(input('podaj rok urodzenia: '))


pobrane_imie = Pobierz_imie()
pobrany_rok = pobierz_rok_urodzenia()

dane1 = Dane(pobrane_imie, pobrany_rok) # można też dać Dane(rok_urodzenia=pobrany_rok, imie=pobrane_imie) wtedy kolejność parametrów nie ma znaczenia
dane1.wyswietl()

Nie musisz też przekazywać zmiennych to funkcji wewnątrz klasy jak np. w twoim policz_ile_lat. Ustawiasz wartości dla rok_urodzenia i aktualny_rok w konstruktorze, a potem możesz się odwołać do nich przez self.rok_urodzenia i będzie tam dokładnie to co ustawiłeś.

ledi12
  • Rejestracja:prawie 6 lat
  • Ostatnio:około 2 miesiące
  • Lokalizacja:Wrocław
1

Ja bym te inputy wyrzucil poza klase i przekazywal jako atrybut do obiektu, cos jak kolega up ;)


Robię http response status cody w martwych ciągach
edytowany 1x, ostatnio: ledi12
vpiotr
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
3

Ja bym nie dodawał pola aktualny_rok - to powinno sie liczyc na bieżąco.

ledi12
Aż się prosi zrobić z tego @staticmethod :D
LukeJL
  • Rejestracja:około 11 lat
  • Ostatnio:minuta
  • Postów:8423
0

Niespójność: napisałeś metodę Pobierz_imie(self) nie wiadomo dlaczego z dużej litery (później autocomplete rozumiem, że umocniło ten błąd. Czy raczej nie tyle błąd, bo to jest technicznie poprawne, tylko niespójność stylu).


edytowany 1x, ostatnio: LukeJL
Arthan
  • Rejestracja:około 18 lat
  • Ostatnio:ponad rok
1

Ja bym sobie kod podzielił na klasę "ludka" i klasę odpowiadającą za komunikację z użytkownikiem. Klasy w osobnych plikach. Aktualny rok dałbym jako metodę prywatną, albo property nie widoczne za weznątrz. Wiek wystawione jako property readonly. Wyświetlanie obiektu poprzez __str__. Do obiektu osoby dopisałbym testy liczenia wieku. Komunikację rozszerzył o logi. Brakuje też zabezpieczeń przed niewłaściwymi danymi.

osoby.py:

Kopiuj
from datetime import datetime

class Osoba:
    def __init__(self, imie=None, rok_urodzenia=None):
        self.imie = imie
        self.rok_urodzenia = rok_urodzenia

    @property
    def __aktualny_rok(self):
        return datetime.now().year

    @property
    def wiek(self):
        if not self.rok_urodzenia: return None
        return self.__aktualny_rok - self.rok_urodzenia

    def __str__(self):
        return f'{self.imie} {self.__aktualny_rok} {self.rok_urodzenia} {self.wiek}'

komunikacja.py:

Kopiuj
class Komunikacja:
    def pobierz_imie(self):
        return input('Podaj imie: ')

    def pobierz_rok_urodzenia(self):
        return int(input('podaj rok urodzenia: '))

    def odpowiedz_uzytkownikowi(self, osoba):
        print(f'Cześć {osoba.imie}! Masz {osoba.wiek} lat.')

main.py:

Kopiuj
from osoba import Osoba
from komunikacja import Komunikacja

def main():
    komunikacja = Komunikacja()
    osoba = Osoba()
    osoba.imie = komunikacja.pobierz_imie()
    osoba.rok_urodzenia = komunikacja.pobierz_rok_urodzenia()
    print(osoba)
    komunikacja.odpowiedz_uzytkownikowi(osoba)
    
if __name__ == '__main__':
    main()
P7
  • Rejestracja:ponad 3 lata
  • Ostatnio:ponad 2 lata
  • Postów:75
0

Dzięki Wam wszystkim za pomocne informacje....

@Arthan Jeśi chodzi o zabezpiecznie danych przed nieprawidłową odpowiedzią , czy mogę tu w klasach zastosować tzw. wyjątki tzn:

Kopiuj
def pobierz_rok_urodzenia(self):
        while True: 
            try:
                return int(input('podaj rok urodzenia: '))
            except ValueError:
                print('Wpisałeś złą wartosć, wpisz liczbę jeszcze raz')

Z wyjatkiem kod działa, tylko czy mogę to w klasach stosować?. Chodzi mi o poprawność kodu w obiektówce. Tzw while , for to tu się w obiektówce stosuje podobnie jak w proceduralnym kodzie? , co do testów, logów , może pokombinuję (choć nie wiem do konca o co chodzi tu ), co do property to wiedzę sobie uzupełnię na ten temat

OR
OR
  • Rejestracja:ponad 3 lata
  • Ostatnio:prawie 3 lata
  • Postów:6
2

Generalnie założyłbym, że możesz robić w klasach wszystko, co do tej pory robiłeś. Obiekty służą tylko po to, żeby opakować dane w wygodne pudełka i zdefiniować operacje, które można na nich wykonywać.

Arthan
  • Rejestracja:około 18 lat
  • Ostatnio:ponad rok
2

Tak jak napisał @ObywatelRP. Kiedy pudełka są wygodne? Wtedy kiedy poszczególne pudełko odpowiada za pojedynczą funkcjonalność, a nie że obiekt Pralka zmywa naczynia (a niekiedy nawet służy do gotowania). Pudełko takie powinno też udostępniać w miarę wygodny interfejs i bronić dostępu do niektórych części wewnętrznych, do których nie powinno być takiego dostępu z zewnątrz (hermetyzacja).

Wracając jeszcze do pobierania roku, zamiast przechwytywać wyjątek możesz tak napisać program by do niego tu nie dochodziło. Czy użytkownik podał poprawną liczbę możesz sprawdzić używając .isdigit(), do tego przydałoby się sprawdzanie czy liczba jest w jakimś sensownym zakresie, np. większa od 1900 i mniejsza niż obecny rok. Ja tam nie lubię używać pętli gdy nie muszę, dlatego tu zamiast while dałbym rekurencję przy niepowodzeniu.

Tu masz przykład prostych testów:

Kopiuj
import unittest
from osoba import Osoba

class TestOsoba(unittest.TestCase):
    def setUp(self):
        self.osoba = Osoba()

    def test_rok_urodzenia_niepodany(self):
        self.assertIsNone(self.osoba.wiek)
        
    def test_rok_urodzenia_2000(self):
        self.osoba.rok_urodzenia = 2000
        self.assertEqual(self.osoba.wiek, 21)

if __name__ == '__main__':
    unittest.main()

Odpalasz ten plik jako osobny program, te testy są trochę słabe bo za miesiąc przestaną działać, ale masz jakiś przykład ;)

ledi12
Testy!? Ja ufam dla swojego kodu! :DD
Arthan
Ja też testów prawie nigdy nie piszę, chyba że zespół mnie zmusi :P Potem czasami bez nich boli :) Testy są nieocenione przy wprowadzaniu zmian w dużych programach. Poza tym testy są czasami dobrymi przykładami zastosowania, nie mówiąc o tym, że istnieje coś takiego jak TDD :)
ledi12
Nie no śmieszkuje sobie :P
Arthan
Ja wiem, ale jeszcze ktoś w to uwierzy i będziesz miał czyjeś zdrowie psychiczne na sumieniu :D
ledi12
I potem będzie: "A bo taki jeden typ na 4p mi powiedział!" :D
P7
  • Rejestracja:ponad 3 lata
  • Ostatnio:ponad 2 lata
  • Postów:75
0

Dzięki za info, wskazówki.... W wolnej chwili posprawdzam co i jak.... Tak się ostatnio zastanawiałem nad bootcampem python ( dostaję od nich zaproszenie na szkolenia online/stacjonarne) , trochę to kosztuje.... Ale jednak wolę iść samemu w naukę, online, ksiązki , fora... Po pierwsze szkoda mi kasy na to ( jak sam mogę wiedzę zdobyć) ale za to więcej wysiłku i czasu mi to zajmnie, bo nie mam nauczyciela który by poprawiał moje błedy w programowaniu. Czasowo dłużej nauka mi zajmnie jak się domyślam. Druga sprawa to czas, którego za bardzo nie mam i bym się musiał dostosować do rytmu szkolenia takiego. A u mnie z tym byłoby krucho. Na razie kupiłem cienką książkę z przykładami ćwiczeń , zadaniami, i wytłumaczeniami jak co działa. Po trochu coś tam zdziałam dla siebie. A po trzecie wolę wydać kasę na jakieś ebooki, filmiki instruktażowe itp...., niż ponad 10 K za szkolenia z którymi jestem uwiązany czasowo. Opinie na temat bootcampów są zróżnicowane w sieci. . Pozdrawiam

Arthan
a idź Pan :P Niedoceniasz Nas... My Cię tu szkolimy, tłumaczymy teorię z przykładami, wskazujemy błędy, wyjaśniamy co i jak, pokazujemy dobre praktyki i ani złotówki nie widzimy za nasze wysiłki. Chcesz 10K dać obcym ludziom, którzy obiecują że znajdziesz pracę po ich kursie do zawodu, w którym na juniora potrzebne jest 3 lata doświadczenia, a kolegom z 4P nie dasz ani złotówki? :P Idę stąd ... bootcampa se stworzę.
P7
:) Naprawdę doceniam Was ) Po prostu reklamami bootcampu jestem "atakowany" he he he... Ale jak poczytałem opinie na temat bootcampów to dałem sobie spokój, no i tak duża kaassaaa na to potrzeba... A tak poważnie, dziękuję Wam za wszelką pomoc, a także za krytykę, swoje zdanie o moich postępach :)
P7
  • Rejestracja:ponad 3 lata
  • Ostatnio:ponad 2 lata
  • Postów:75
0

@ObywatelRP: Własnie analizuję kod, uruchamia się z błedem.
W klasie Dane na końcu powinno być bez argumentów: self.imie , self wiek tzn:

Kopiuj
 self.odpowiedz_uzytkownikowi()

choć jak tej funkcji w ogóle nie podam , to kod też się wywołuje poprawnie.
Grunt , ze kod się uruchamia :) A tak z ciekawośći, dlaczego dwie ostatnie funkcje w kodzie są akapitami różne? Tak sobie porównuję do innych kodów.

Kopiuj
  
      def wyswietl(self):
        print(self.imie, self.aktualny_rok, self.rok_urodzenia, self.wiek)

def Pobierz_imie():
        return input('Podaj imie: ')

def pobierz_rok_urodzenia():
        return int(input('podaj rok urodzenia: '))
edytowany 1x, ostatnio: pythonowiec74
OR
OR
  • Rejestracja:ponad 3 lata
  • Ostatnio:prawie 3 lata
  • Postów:6
1

Generalnie, to może zacznijmy od tego, że python to taki śmieszny język, gdzie wcięcia służą do definiowania bloków kodu

Kopiuj
def test_indent():
    if False:
        print("To się nie wyświetli")
        print("To też się nie wyświetli")
    print("A to już, tak bo jest inne wcięcie")

test_indent()

Tak samo jest z klasami

Kopiuj
class Clazz:
    def method_in_class(self):
        print("method")

    def method2_in_class(self):
        print("method 2")

def method3_not_in_class(self):
    print("method 3")


clazz = Clazz()
clazz.method_in_class()
clazz.method2_in_class()
clazz.method3_not_in_class() # error

Nie chciałem, żeby metody pobierające dane były w klasie, która na nich operuje, więc nie są na tym samym poziomie wcięcia . Popatrz na kod, który dał ci @Arthan na poprzedniej stronie, Klasa osoba ma tylko metody służące do operowania na danych, które dostaje, a cała reszta, czyli pobieranie danych jest w klasie Komunikacja. U mnie też można by dać je w osobnej klasie, ale jestem leniwy i nie chciało mi się tego pisać, więc dałem tylko inne wcięcie żeby zaznaczyć, że te metody nie powinny tam być.

P7
  • Rejestracja:ponad 3 lata
  • Ostatnio:ponad 2 lata
  • Postów:75
0

@ObywatelRP: Dzięki za info. Dla testów dałem to w osobnej klasie (inny plik) , działa bez problemu :)

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.