2012-01-26 14 views
8

Wiem, że atrybuty klasy, które są deklarowane przez prefiks podwójnego podkreślenia __, mogą lub nie mogą być widoczne poza definicją klasy. Ponieważ nadal możemy uzyskać dostęp do tych atrybutów przez object._className__attrName.Ukrywanie danych w języku Python Klasa

class A: 
    def __init__(self): 
     self.a = 1 
     self.b = 2 
     ---- 
     ---- 
     self.z = 26 
     self.catch = 100 

teraz, aby zabezpieczyć wszystkie atrybuty oprócz atrybutu catch, muszę zadeklarować je z podwójnym podkreśleniem co jest dość niechlujny. Czy jest jakiś sposób, aby powiedzieć w mojej definicji klasy, że tylko self.catch może być dostępny poza klasą?

Przeprosiny, jeśli odpowiedź zostanie udzielona w innym miejscu lub omówiona wcześniej.

+1

Nie. Jednakże, jeśli nie jesteś naprawdę paranoikiem, fakt, że ktoś * może * uzyskać dostęp do obiektu za pomocą 'object._className__attrName' nie jest wielkim problemem. Naprawdę po prostu chcesz zapobiec przypadkowemu dostępowi do zmiennych, których nie powinni używać podczas używania twojego kodu. Obfuskacja osiąga ten cel. – Chris

+2

możliwy duplikat [Czy python ma zmienne "prywatne" w klasach?] (Http://stackoverflow.com/questions/1641219/does-python-have-private-variables-in-classes) –

+0

@ BjörnPollex, this isn ' o co on prosi. On chce potrzeby jawnego "publicznego" operatora ", a nie odwrotnie. –

Odpowiedz

16

Tak, to jest możliwe, aby ukryć prywatne dane w zamknięciu - przynajmniej, jeśli istnieje sposób, aby uzyskać dostęp private z zewnątrz make_A, ja nie znalazłem go:

def make_A(): 
    private = { 
     'a' : 1, 
     'b' : 2, 
     'z' : 26, 
     } 
    class A: 
     def __init__(self): 
      self.catch = 100 
      private['a'] = 2 # you can modify the private data 
     def foo(self): 
      print(private['a']) # you can access the private data 
    return A 

A = make_A() 

a=A() 

a.foo() 
# 2 

Uwaga, private nie jest w dir(a)

print('private' in dir(a)) 
# False 

Chociaż jest to możliwe, nie sądzę, to jest godny polecenia sposób programować w Pythonie.


Powyżej private jest wspólna dla wszystkich wystąpień A. Aby korzystać z danych prywatnych na podstawie na przykład dodać self do klucza dict:

def make_A(): 
    private = {} 
    class A: 
     def __init__(self): 
      self.catch = 100 
      private[self,'a'] = 1 # you can modify the private data 
      private[self,'b'] = 2  
      private[self,'z'] = 26  
     def foo(self): 
      print(private[self,'a']) # you can access the private data 
    return A 
+2

+1 za ukrywanie danych w stylu zamknięcia. – 9000

+0

Kciuki za *** zamknięć **. Może to być interesujące: [Secret Variables i Python] (http://www.jotflow.com/jot/Secret-Variables-In-Python/5). Porównuje dane funkcjonalne ukryte w Pythonie ze zmiennymi prywatnymi w Javie. –

1

Nie, ponieważ jest to część nazwy, nie można tego zrobić.

Cóż, w rzeczywistości możesz hakować za pomocą modułów pobierających/ustawiających, ale generalnie nie powinieneś.

1

Dlaczego warto chronić atrybuty?

Jeśli chcesz zabezpieczyć atrybuty przed przypadkowym nadpisaniem, użyj podwójnych podkreśleń i, prawdopodobnie, właściwości gotowych do użycia, aby ułatwić sobie dostęp. Ale przez większość czasu, you aint't gonna need it. Po prostu zrób sobie przysługę i pozostaw otwarte atrybuty, co znacznie ułatwia debugowanie.

Jeśli chcesz chronić obiekt przed manipulacją przez osoby trzecie, można grać z __getattr__ i __setattr__, ale prawdopodobnie nie powinien przechodzić do niezaufany klient obiektów z danymi chronionymi w ogóle używać facade pattern zamiast i ukryć swoje kosztowności bardziej nieprzeciętnie. Możesz także przemyśleć swoją architekturę.

+1

* Dlaczego chcesz chronić atrybuty? * Mogę tylko wskazać ci wpis wiki na temat enkapsulacji, ale dam ci praktyczny przykład. Załóżmy, że masz dużą i złożoną aplikację python, w której masz kilka klas z atrybutem "nazwa". Znaleziono błąd, w wyniku którego nazwa administratora jest niepoprawnie nadpisana. Jeśli pozostawiłeś atrybut nazwy otwarty zgodnie z sugestią, w jaki sposób znajdziesz naruszającą linię kodu? Jeśli ukryłeś atrybut i użytych akcesorów, możesz umieścić punkt przerwania na pobierającym i szybko znaleźć winnego. – dar512

+0

@ dar512: jeśli twoja klasa jest częścią publicznego interfejsu, a klienci mogą manipulować atrybutami, pilnuj ich przez akcesorów! Liczba klas publicznych jest raczej ograniczona w typowej aplikacji. Ponadto używanie niezmiennych danych ma wiele zalet; Często używam podklas "namedtuple" do reprezentowania danych w sposób odporny na kule. – 9000

+0

Jako programista, jeśli zdecyduję się wystrzelić moją nogę za pomocą strzelby, niech tak będzie. Ograniczasz moją zdolność do naprawiania/modyfikowania twojego kodu przez ukrywanie rzeczy. "Chronione" i "prywatne" słowa kluczowe są po prostu anty-wzorcami. Proszę porzucić pogląd, że wyświadczasz komuś przysługę poprzez "ukrywanie" rzeczy przed innymi programistami. Jeśli chcesz zrobić coś takiego, użyj '_variable' i podaj właściwość getter' variable' i no setter. Nadal mogę ręcznie modyfikować '_zmienną' w ten sposób, jeśli sobie tego życzę. Ale nie używaj hacków językowych do zaciemniania twoich wewnętrznych elementów od innych programistów. – Rebs

3

Choć przyjął odpowiedź przez unutbu wygląda jak dobry pomysł, aby ukryć dane, słownika private nadal dostępne przy użyciu __closure__ (atrybut ten nie może być usunięte):

def make_A(): 
    private = {} 
    class A: 
     def __init__(self): 
      self.catch = 100 
      private[self,'a'] = 1 # you can modify the private data 
      private[self,'b'] = 2  
      private[self,'z'] = 26  
     def foo(self): 
      print(private[self,'a']) # you can access the private data 
    return A 


A = make_A() 
a = A() 
a.foo() # 1 
a.foo.__closure__[0].cell_contents[(a, 'a')] = 42 
a.foo() # 42 

lub po linku podanego przez Sumukh Barve w komentarzy:

def createBankAccount(): 
    private = {'balance': 0.0} 
    def incr(delta): 
     private['balance'] += delta; 
    account = { 
     'deposit': lambda amount: incr(amount), 
     'withdraw': lambda amount: incr(-amount), 
     'transferTo': lambda otherAccount, amount: (
      account['withdraw'](amount), 
      otherAccount['deposit'](amount) 
     ), 
     'transferFrom': lambda otherAccount, amount: (
      otherAccount['transferTo'](account, amount) 
     ), 
     'getBalance': lambda : private['balance'] 
    } 
    return account; 


account = createBankAccount() 

print(account['getBalance']()) 
account['getBalance'].__closure__[0].cell_contents['balance'] = 1000**1000 
print(account['getBalance']()) # I can buy a couple of nations 

I bevile jedyny sposób tworzenia private atrybutów jest napisać jakiś k rozszerzenie ind. CPython.

+0

dzięki za to –