2014-05-02 20 views
7

Jestem stosunkowo nowy w Pythonie, więc mam nadzieję, że nie zostały pominięte coś, ale tu idzie ...„Prywatna” właściwości atrybutów w Pythonie

Próbuję napisać moduł Pythona, a ja d utworzyć klasę z "prywatnym" atrybutem, który może (lub może "powinien") być modyfikowany tylko przez jedną lub więcej funkcji w module. Ma to na celu uczynienie modułu bardziej wytrzymałym, ponieważ ustawienie tego atrybutu poza tymi funkcjami może prowadzić do niepożądanego zachowania. Na przykład może mam:

  1. klasy, która przechowuje wartości X i Y dla wykresu punktowego, Data
  2. funkcji do odczytu wartości X i Y z pliku i przechowywać je w klasie, read()
  3. funkcja je wykreślić, plot()

W tym przypadku wolałbym, jeśli użytkownik nie był w stanie zrobić coś takiego:

data = Data() 
read("file.csv", data) 
data.x = [0, 3, 2, 6, 1] 
plot(data) 

Zdaję sobie sprawę, że dodanie pojedynczego podkreślenia do nazwy wskazuje użytkownikowi, że atrybut nie powinien zostać zmieniony, tj. Zmień nazwę na _x i dodaj dekorator właściwości, aby użytkownik mógł uzyskać dostęp do wartości bez poczucia winy. Jednak co zrobić, jeśli chciałam dodać obiekt seter także:

class Data(object): 
    _x = [] 
    _y = [] 
    @property 
    def x(self): 
     return self._x 
    @x.setter 
    def x(self, value): 
     # Do something with value 
     self._x = value 

jestem teraz w takiej samej sytuacji jak ja wcześniej - użytkownik nie może już bezpośrednio uzyskać dostęp do atrybutu _x, ale nadal można je ustawić go za pomocą:

data.x = [0, 3, 2, 6, 1] 

Idealnie byłoby zmienić nazwę definicji funkcji nieruchomość do _x(), ale prowadzi to do nieporozumień na temat tego, co w rzeczywistości oznacza self._x (w zależności od kolejności, w jakiej zostały zgłoszone, to wydaje się skutkować albo seter nazywa się rekursywnie, lub ustawiający jest ignorowany na rzecz atrybutu).

Kilka rozwiązań mogę myśleć:

  1. Dodaj podwójne wiodącego podkreślenia do atrybutu, __x, tak że nazwa staje się zniekształcone i nie pomylić z funkcją seter. Jak rozumiem, powinno to być zarezerwowane dla atrybutów, których klasa nie chce udostępniać z możliwymi podklasami, więc nie jestem pewien, czy jest to uzasadnione użycie.
  2. Zmień nazwę atrybutu, np. _x_stored. Chociaż rozwiązuje to całkowicie problem, utrudnia odczyt kodu i wprowadza problemy z konwencjami nazewnictwa - które atrybuty zmieniają nazwę? tylko te, które są istotne? tylko te, które mają właściwości? tylko te w tej klasie?

Czy jedno z powyższych rozwiązań ma zastosowanie? A jeśli nie, czy istnieje lepszy sposób na rozwiązanie tego problemu?

Edit

Dzięki za odpowiedzi tak daleko.Kilka punktów rzucone przez komentarze:

  1. chcę zachować dodatkową logikę, że nieruchomość seter daje mi - rozdział w powyższym przykładzie # Do something with value - tak wewnętrznie ustawienie atrybutu poprzez bezpośredni dostęp self._x nie Rozwiąż problem.
  2. Usunięcie właściwości ustawiającego i utworzenie oddzielnej funkcji powoduje, że problem jest rozwiązywany, ale nie jest rozwiązaniem bardzo schludnym, ponieważ umożliwia ustawienie _x na dwa różne sposoby - przez wywołanie tej funkcji lub bezpośredni dostęp do self._x. Musiałbym wtedy śledzić, które atrybuty powinny być ustawione przez ich własną (niezwiązaną z nieruchomością) funkcję ustawiającą i które powinny być modyfikowane przez bezpośredni dostęp. Najprawdopodobniej skorzystam z jednego z rozwiązań, które zasugerowałem powyżej, ponieważ nawet jeśli robią bałagan konwencji nazewnictwa w klasie, to są co najmniej konsekwentne w ich używaniu poza klasą, tj. Wszystkie używają syntaktycznego cukru właściwości . Jeśli nie ma sposobu na zrobienie tego w bardziej uporządkowany sposób, to sądzę, że muszę wybrać ten, który powoduje najmniejsze zakłócenia.
+0

jestem c Zastanawiające się, dlaczego chcesz zdefiniować osobę, która nie chce, aby ludzie używali. – cmd

+0

@cmd - Nadal chcę móc używać settera w innych funkcjach w module. Na przykład, mogę mieć funkcję 'interpolate()', która musi być w stanie ustawić wartości xiy. – sftd

+0

Ale te mogą po prostu ustawić '_x' i' _y'. – kindall

Odpowiedz

3

Jeśli chcesz zniechęcić użytkownikom zmianę właściwości, ale ma to być jasne, że można go odczytać, użyję @property bez podania setter, podobny do tego, co opisano wcześniej:

class Data(object): 
    def __init__(self): 
     self._x = [] 
     self._y = [] 

    @property 
    def x(self): 
     return self._x 

    @property 
    def y(self): 
     return self._x 

Wiem, że wspomniałeś "Co jeśli chciałbym dodać setera do nieruchomości?", Ale myślę, że mógłbym to odeprzeć: Dlaczego warto dodać setera, jeśli nie chcesz, aby twoi klienci mogli ustawić właściwość? ? Wewnętrznie, możesz uzyskać bezpośredni dostęp do self._x.

Jeśli chodzi o klienta uzyskującego bezpośredni dostęp do _x lub _y, każda zmienna z prefiksem "_" jest rozumiana jako "prywatna" w Pythonie, więc powinieneś ufać swoim klientom, aby jej przestrzegać. Jeśli nie posłuchają tego, a w końcu coś zepsują, to z własnej winy. Ten sposób myślenia jest sprzeczny z wieloma innymi językami (C++, Java itp.), Gdzie przechowywanie prywatnych danych jest uważane za bardzo ważne, ale kultura Pythona jest po prostu inna w tym względzie.

Edit

Jedna uwaga, ponieważ twoje prywatne obiekty w tym konkretnym przypadku są listy, które są zmienne (w przeciwieństwie ciągi lub wskazówki, które są niezmienne), klient może skończyć zmieniając je nieco przypadkowo:

>>> d = Data() 
>>> print d.x 
['1', '2'] 
>>> l = d.x 
>>> print l 
['1', '2'] 
>>> l.append("3") 
>>> print d.x 
['1', '2', '3'] # Oops! 

Jeśli chcesz tego uniknąć, należałoby aĹ aby powrócić kopię listy:

@property 
def x(self): 
    return list(self._x) 
+0

Rozumiem, że łatwo jest wewnętrznie ustawić wartość przy użyciu bezpośredniego dostępu 'self._x', ale to usuwa tę zaletę, jaką daje mi ustawiający, tj. Bit' # Zrób coś z wartością'. – sftd

+0

Ach, masz na myśli to, że seter robi coś więcej niż proste 'self._x = blah'?W tym przypadku musisz dodać @property również do własności prywatnej (co, jak myślę, wspomniałeś, że ci się nie podobało), lub po prostu pamiętaj, aby wywołać funkcję wewnętrznego pomocnika, gdy chcesz przypisać do prywatnego własność. – dano

+0

Czy istnieje na to bardziej elegancki sposób, który nie wprowadza niespójnej konwencji nazewnictwa? Zobacz oryginalną edycję postu. (@cmd, @kindall) – sftd

Powiązane problemy