Python setter i gettery

Odpowiedz Nowy wątek
2019-07-04 23:37
0

Poniżej mam kod. Czy aby użyć getterow i setterow dla innych zmiennych obiektu muszę na nowo stworzyć dekoratory i funkcje jak to zrobiłem dla marki? Czy jest może inny sposób-szybszy?
Pozdriawiam

class Car:
    def __init__(self, marka, rocznik):
        self.marka = marka
        self.rocznik = rocznik

    @property
    def marka(self):
        return self.__marka

    @marka.setter
    def marka(self, nazwa):
        self.__marka = nazwa

    @marka.deleter
    def marka(self):
        del self.marka

    def __repr__(self):
        return "{}, {}".format(self.marka, self.rocznik)
auto = Car("Audi", 2019)

print(auto.marka)
auto.marka = "BMW"
print(auto.marka)
print(auto)
edytowany 3x, ostatnio: Ktos, 2019-07-05 09:21

Pozostało 580 znaków

2019-07-04 23:49
0

A po co te property jak nic w nich nie robisz. To nie java. A nawet w javie to jest kontrowersyjne (tylko tam niestety nie ma property)


01010100 01110101 01110100 01100001 01101010 00100000 01101110 01101001 01100101 00100000 01101101 01100001 00100000 01101110 01101001 01100011 00100000 01100011 01101001 01100101 01101011 01100001 01110111 01100101 01100111 01101111 00101110 00100000 01001001 01100011 00100000 01110011 01110100 01101111 01101110 01110100 00101110
edytowany 2x, ostatnio: stivens, 2019-07-04 23:51

Pozostało 580 znaków

2019-07-04 23:58
0

Tymczasem "Pythonic" będzie nie używać getterów i setterów, tylko ustawiać bezpośrednio atrybuty (plain attributes).
https://www.python.org/dev/pe[...]08/#designing-for-inheritance


Pozostało 580 znaków

2019-07-05 00:03
0

Poproszę kogoś o poprawę kodu, abym wiedział jak to ma wyglądać.

Pozostało 580 znaków

2019-07-05 00:09
1

Raz Nazywasz atrybut self.marka, a potem self.__marka, to Popraw. A mając obiekt, nie widzę potrzeby usuwania i zmieniania jego atrybutów (bo jak tak to po co mi obiekt? Wystarczy lista albo słownik).


Pozostało 580 znaków

2019-07-05 07:56
2

Robisz 2 błędy.

1) Pierwszy logiczny. Po co samochód miałby zmieniać swoją markę? Takie zachowanie w ogóle mija się z rzeczywistością. Ostatecznie użyłbym settera, by właśnie rzucić wyjątek jeśli ktoś czegoś takiego spróbuje dokonać (chociaż i tak lepiej byłoby użyć tutaj zamrożony dataclass), ale popatrz pierw na drugi punkt.
2) Python mimo, że jest językiem obiektowym i pozwala stosować praktyki obiektowe. Jednak ostatecznie za bardzo mu nie służą konstrukcje, które właściwie nic nie robią poza komplikowaniem kodu. W javie to standard, a w pythonie ludzie pomyślą, że masz problem z głową.

Bardzo rzadko idzie spotkać osobę, która ma wyważone podejście z klasami w pythonie (tzn zna i rozumie ograniczenia jakie wiążą się z różnymi technikami). Ludzie na ogół małpują tzn. często nadużywają dziedziczenia (choćby z powodu django, jak i własnych paranoi), klasy abstrakcyjne, interfejsy, piszą bardzo generyczny kod, a jeszcze niektórzy zapędzają się w metaprogramowanie. Ostatecznie mają z tym więcej problemów niż ludzie płaskomyślący, którzy piszą kod bez abstrakcji z nastawieniem na modyfikacje, lub przepisywaniem fragmentów kodu od nowa.

Pozostało 580 znaków

2019-07-08 14:21

Rozłóżmy zagadnienie na czynniki pierwsze. Najpierw zdefiniujmy najprostszą postać klasy Car:

class Car:

    def __init__(self, brand, year_production):
       self.brand = brand
       self.year_production = year_production

    def __str__(self):
        return "{0} was produced in {1}".format(self.brand, self.year_production)

Przejdźmy do wykorzystania klasy:

my_car = Car("MINI Cooper", 2018)
print(my_car)
# MINI Cooper was produced in 2018
print(my_car.brand)
# MINI Cooper
print(my_car.year_production)
# 2018
my_car.year_production = 2019
print(my_car.year_production)
# 2019

Widzimy, że możemy zmienić bardzo łatwo rok produkcji samochodu, co jest w realnym życiu niemożliwe. Rok produkcji powinien być niezmienny, zresztą podobnie jak marka samochodu (nie zrobisz z Malucha Poloneza i odwrotnie). Chcielibyśmy, aby nie dało się tej wartości zmienić. Możemy więc ukryć te atrybuty (marka, rok produkcji) i stworzyć metodę, która będzie służyć do ich odczytywania:

class Car:

    def __init__(self, brand, year_production):
       self.__brand = brand
       self.__year_production = year_production

    def brand(self):
        return self.__brand

    def year_production(self):
        return self.__year_production

    def __str__(self):
        return "{0} was produced in {1}".format(self.__brand, self.__year_production)

Teraz możemy stworzyć nowy obiekt i odczytać jego wartości:

my_car = Car("BMW", 2010)
print(my_car.brand())
# BMW
print(my_car.year_production())
# 2010

Przy okazji zauważmy, że musimy ukryć atrybuty, dodając prefiks w postaci __ przed nazwą zmiennej. Gdyby tego nie zastosować, to wciąż można by zmienić atrybuty z pomocą zwykłego przypisania, tj. my_car.brand = "Opel". Z powyższego kodu widzimy również, że aby pobrać atrybut, musimy wywołać metodę, a więc dodać na końcu parę (, ), np. my_car.brand(). Takie rozwiązanie niekoniecznie jest eleganckie. Lepiej byłoby nie używać nawiasów. W takim przypadku możemy wykorzystać dekorator @property:

class Car:

    def __init__(self, brand, year_production):
       self.__brand = brand
       self.__year_production = year_production

    @property
    def brand(self):
        return self.__brand

    @property
    def year_production(self):
        return self.__year_production

    def __str__(self):
        return "{0} was produced in {1}".format(self.__brand, self.__year_production)

Teraz możemy pobierać prywatną wartość i nie możemy jej ponownie ustawić, bo program rzuci wyjątkiem:

my_car = Car("Opel", 2007)
print(my_car.year_production)
# 2007
my_car.year_production = 2012
# AttributeError: can't set attribute
print(my_car)
# Opel was produced in 2007

Ostatnia wersja klasy wydaje się więc tym, czego szukałeś. Ustawiasz atrybuty obiektu tylko raz w konstruktorze i to się nie może zmienić. Dodatkowo wystawiasz publiczny interfejs w postaci metod, które zwracają Ci odpowiednie wartości atrybutów. Z pomocą @property pozbywasz się brzydkich nawiasów. Ale to koniec. Załóżmy, że udostępniłeś taką klasę, np. publikując ją na Python Package Index (odradzam, bo to trywialna klasa). Po jakimś czasie pisze do Ciebie znajomy, że przecież to bezsensu, że wyprodukowano samochód w 1642 roku, bo wtedy jeszcze nie było samochodów. Sprawdzasz na Wikipedii, że pierwsza masowa produkcja aut rozpoczęła się w 1903 roku (Ford). Chciałbyś to jakoś uwzględnić w swoim kodzie, który już wykorzystują inni. Na szczęście dekorator @property daje Ci taką możliwość. Z jego pomocą możesz przerobić klasę tak, by ustawić setter:

class Car:

    def __init__(self, brand, year_production):
       self.__brand = brand
       self._year_production = year_production

    @property
    def brand(self):
        return self.__brand

    @property
    def year_production(self):
        return self.__year_production

    @year_production.setter
    def _year_production(self, year):
        first_car_year_production = 1903

        if year < first_car_year_production:
            raise ValueError("Year of production must be >= {}".format(first_car_year_production))
        else:
            self.__year_production = year

    def __str__(self):
        return "{0} was produced in {1}".format(self.__brand, self.__year_production)

Zauważmy, że metodzie __init__ zmieniliśmy ostatnią linię. Wywołuje ona wewnętrzną metodę _year_production(self, year). Mogliśmy zostawić year_production(self, year), bo dzięki @property możemy przeciążać tę metodę (drugą metodą jest getter year_production(self)), ale prefiks _ sugeruje, że ta metoda jest metodą wewnętrzną i nie powinna być używana przez inne obiekty/programy/użytkowników. Dodatkowo jest ona oznaczona za pomocą dekoratora jako setter dla zmiennej year_production.

Teraz już możesz tworzyć obiekty typu Car. Jeśli ustawisz nieprawidłowy rok produkcji, to program zrzuci wyjątek. Atrybuty pobierasz z pomocą metod, ale dekorator @property zmienia je na zwykłe atrybuty. I jeszcze jedno, metoda __repr__ służy do czegoś innego niż __str__. Poczytaj sobie o różnicach. W każdym razie w Twoim przypadku lepiej użyć __str__, co też uczyniłem.

edytowany 2x, ostatnio: Pyxis, 2019-07-08 14:25
i nagle kod w Pythonie staje się brzydki :D - Spine 2019-07-08 14:27
Opisałem tylko mechanizmy, z nich wcale nie trzeba korzystać. Niewątpliwą zaletą dekoratora property jest kompatybilność wsteczna, co pokazałem na przykładzie kolejnych implementacji klasy Car. - Pyxis 2019-07-08 14:32
Ło Panie. Tak wyczerpującej odpowiedzi się nie spodziewałem. Dziękuję. Teraz wszystko jest jasne! - zduninho88 2019-07-08 15:06

Pozostało 580 znaków

2019-07-09 09:43
4

Ja chyba jednak napisałbym to tak...

from dataclasses import dataclass

@dataclass(frozen=True)
class Car:
    brand: str
    year: int

    def __str__(self):
        return f'{self.brand} was produced in {self.year}'

car = Car("Opel", 2007)
print(car)

Edytka:
W komentarzu @Guaz słusznie zauważył, że to śmiga na 3.7+ zatem gdyby ktoś był skazany na Pythona 2.4+ to można to napisać też z użyciem namedtuple. W takim wypadku niemutowalność jest jedyną opcją, a nie wyborem:

from collections import namedtuple

class Car(namedtuple('Car', ['brand', 'year'])):

    def __str__(self):
        return '{0} was produced in {1}'.format(self.brand, self.year)

car = Car('Opel', 2007)
print(car)

Wiedza to potęga
edytowany 1x, ostatnio: Haskell, 2019-07-09 17:38
Pokaż pozostałe 3 komentarze
No właśnie, a jeśli nie jest zamrożona, to możesz zmieniać atrybuty. Mnie się troszkę podane przez Ciebie rozwiązanie kłóci z There should be one-- and preferably only one --obvious way to do it. - Pyxis 2019-07-10 09:27
Mnie z kolei Twoje rozwiązanie kłóci się z Pythonem. - Haskell 2019-07-10 10:10
Podaj zatem powód. - Pyxis 2019-07-10 10:11
Próbujesz programować w Pythonie jak w Javie, a to jest Python, a nie Java. Ten język nie był projektowany z myślą o enkapsulacji pól obiektów i wszelkie próby osiągnięcia tego celu wyglądają jak nieudolna proteza. - Haskell 2019-07-10 10:14
Próbujesz programować w Pythonie - raczej pokazuję tylko mechanizmy, o które autor pytał. Swoją drogą property też można stosować do dataclass, bo to zwykłe klasy, a jak pisaliśmy wyżej samo frozen nie zawsze rozwiązuje problem. - Pyxis 2019-07-10 11:02

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0