2014-09-26 5 views
7

Szukałem w odpowiedzi na to pytanie: Is it possible to define a class constant inside an Enum?Python: Stała Klasa

Co interesuje mnie najbardziej, to klasa Stała w odpowiedzi Ethan Furman za.

class Constant: 
    def __init__(self, value): 
     self.value = value 
    def __get__(self, *args): 
     return self.value 
    def __repr__(self): 
     return '%s(%r)' % (self.__class__.__name__, self.value) 

Pytanie dotyczyło Pythona 3.4, ale używam 2.7. W odpowiedzi Ethan ustawia stałą grawitacji jako zmiennej instancji o Planet klasy tak:

G = Constant(6.67300E-11) 

Mój testowania tej klasy w 2,7 pokazuje, że wpisując tylko G daje mi to:

Out[49]: Constant(3) 

(Ustawiłem go na 3 dla łatwości użycia podczas testowania.Jest to dla mnie wyjście __repr__, proszę poprawić mnie, jeśli się mylę.)

Wartość jest dostępna przez G. wartość, jednak w odpowiedzi Ethana używa

return self.G * self.mass/(self.radius * self.radius) 

To oczywiście działa tylko wtedy, gdy zwracana jest wartość w stosunku do wyjścia __repr__. Teraz, jeśli zmienię class Constant: na class Constant(int):, a następnie wpisz G nadal otrzymuję dane wyjściowe __repr__, ale jeśli wpiszę G * 4, otrzymam 12, a nie błąd, który otrzymałem. (TypeError: nieobsługiwane typy argumentów dla *: 'instance' i 'int' )

Tak więc coś podobnego do obiektu int może wyprowadzić liczbę po wywołaniu. Czy brakuje mi magicznej metody, która pozwoliłaby mi to zrobić dla klasy Constant? Ponieważ stałe mogą być ciągami, liczbami całkowitymi lub zmiennymi wolałbym mieć 1 klasę, która obsługuje je wszystkie w stosunku do 3 oddzielnych klas, które rozszerzają te obiekty.

Wartość można również ustawić za pomocą G. wartości. Czy mogę to zablokować, aby zachowywać się w klasie stałej, która lubi stałą rzeczywistą? (Podejrzewam, że odpowiedź brzmi "nie").

+0

Dlaczego chcesz, aby był stały? – Veedrac

+0

@Veedrac Dlaczego nie chciałbym stałych? Lub wartości tylko do odczytu w klasie? Z mojego czytania na Pythonie rozumiem, że jest to sprzeczne z filozofią Pythona "Wszyscy jesteśmy zgodnymi dorosłymi". Naprawdę lubię Pythona, ale myślę, że filozofia to niedopatrzenie. Programuję dla siebie, i dla mnie - nikt inny go nie przeczyta i nie użyje. Po kodowaniu w kilku językach przez ostatnie 15 lat nauczyłem się kilku rzeczy. Jedną z tych rzeczy jest to, że mogę zapobiec debugowaniu dni, po prostu projektując klasę, w której pewnych rzeczy po prostu nie da się zmienić poza wewnętrznymi cechami tej klasy. –

+0

Byłem bardziej zastanawiasz się, na jakim poziomie potrzebujesz stałej wartości; moim celem było sprawdzenie, czy dobrze się czujesz z "stałym przez interfejs publiczny", który jest prawidłowym sposobem robienia tego, lub faktycznie chcesz, aby był * całkowicie * stały, co jest niemożliwe. – Veedrac

Odpowiedz

2

Twoja klasa stała powinna dziedziczyć z obiektu, aby stać się nowym stylem klasy Python.

W ten sposób Constant będzie nazywany deskryptorem . Mówiąc prościej, deskryptor jest konstrukcją Pythona, aby dostosować zachowanie pobierania i ustawiania atrybutu klasy. Są przydatne, gdy instancja deskryptora jest ustawiona jako atrybut innej klasy.

W twoim przykładzie Stała jest deskryptorem, a Planeta ma atrybut będący instancją Stałego. Kiedy dostaniesz atrybut G klasy Planet (self.G w twoim przykładzie), to, co naprawdę dostajesz, to, co zwraca metoda __get__ deskryptora, czyli wartość.

Należy zauważyć, że __get__ jest wywoływane tylko wtedy, gdy instancja deskryptora jest dostępna dla innego atrybutu klasy.

więc zdefiniować klasę tak:

class Constant(object): 
    def __init__(self, value): 
     self.value = value 
    def __get__(self, *args): 
     return self.value 
    def __repr__(self): 
     return '%s(%r)' % (self.__class__.__name__, self.value) 

Wtedy ten mały przykład:

c = Constant(3.14) 
print c 

class Test: 
    c = Constant(3.14) 

t = Test() 
print t.c 

wypisze:

Constant(3.14) 
3.14 

Zobacz, że gdy Stała instancja jest drukowany bezpośrednio , metoda __repr__ zostanie wywołana, ale po wydrukowaniu jako inny atrybut klasy użyjemy __get__.

Więcej informacji na temat deskryptorów można znaleźć na stronie this great article.

+0

Z tego, co przetestowałem, działa tylko wtedy, gdy jest zmienną klasy, a nie instancją. Jeśli przeniesię 'c = Constant' na' __init__' przez 'self.c = Constant (...)' wtedy otrzymam wyjście '__repr__'. I nadal jest do zapisu, co jest dobre dla stałej, ponieważ mogę nazwać wszystkimi CAPS. Idealnie, mam nadzieję, że znajdę sposób na utworzenie zmiennej instancji zmiennej z klasy, ale nie poza skryptem '__main__'. Miałem coś w PHP, który zrobił to całkiem dobrze, ale z tego, co przeczytałem do tej pory, nie jest możliwe w Pythonie. Atrybut klasy wydaje się zawsze dostępny do nadpisania. Dzięki –

0

Cóż, value jest członkiem Państwa class Constant; więc można spróbować making it private:

class Constant: 
    def __init__(self, value): 
    # This actually transforms the variable to _Constant__value, 
    # but also hides it from outer scope 
    self.__value = value 
    def __get__(self, *args): 
    # Altough the member is theorically renamed as _Constant__value, 
    # it is completely accesible from inside the class as __value 
    reurn self.__value 
    def __repr__(self): 
    return '%s(%r)' % (self.__class__.__name__, self.__value) 

Innym rozwiązaniem mogłoby być następujące this recipe.

Wypróbuj je i pozwól mi teraz. Mam nadzieję, że ci pomogłem!

+0

Nie próbowałem jeszcze przepisu, ale podany przeze mnie kod nie zadziałał, ponieważ jest to jego własna instancja. Nie mogłem już osiągnąć wartości bezpośrednio. Podejrzewam, że to z powodu tego, co powiedział @baxeico, gdzie musi stać się deskryptorem w klasie. Thx –

+0

Receptura wydaje się działać. Musi jednak znajdować się w pliku i zostać zaimportowany. Wydaje się również być singletonem. Importowanie go w klasie (tuż poniżej definicji klasy) nie ogranicza go do przestrzeni nazw tej klasy. Nie umieszcza również linii importu w '__init__'. Przepis działa całkiem nieźle, jeśli możesz żyć z tymi ograniczeniami. –