Dziedziczenie metod prywatnych i chronionych w Pythonie

Wiem, że w Pythonie nie ma 'prawdziwych' prywatnych/chronionych metod. Takie podejście nie ma na celu niczego ukrywać; chcę tylko zrozumieć, co robi Python.

class Parent(object):
    def _protected(self):
        pass

    def __private(self):
        pass

class Child(Parent):
    def foo(self):
        self._protected()   # This works

    def bar(self):
        self.__private()    # This doesn't work, I get a AttributeError:
                            # 'Child' object has no attribute '_Child__private'

Czy to zachowanie oznacza, że metody "chronione" będą dziedziczone, ale "prywatne" w ogóle nie będą?
Czy coś mnie ominęło?

Author: ndmeiri, 2013-11-28

6 answers

Python nie ma modelu prywatności, nie ma modyfikatorów dostępu, jak w C++, C# czy Javie. Nie ma prawdziwie "chronionych" lub "prywatnych" atrybutów.

Nazwy z głównym podwójnym podkreśleniem i bez końcowego podwójnego podkreślenia są zniekształcone , aby chronić je przed kolizjami po dziedziczeniu. Podklasy mogą definiować własną metodę __private() i nie będą kolidować z tą samą nazwą na klasie nadrzędnej. Takie nazwy są uważane za class private; są one nadal dostępne spoza klasy, ale są znacznie mniej prawdopodobne, aby przypadkowo zderzyć.

Mangling jest wykonywany przez dodanie dodatkowego podkreślenia i nazwy klasy (niezależnie od tego, jak nazwa jest używana lub czy istnieje), co skutkuje nadaniem im przestrzeni nazw . W klasie Parent każdy identyfikator __private jest zastępowany (w czasie kompilacji) nazwą _Parent__private, podczas gdy w klasie Child identyfikator jest zastępowany przez _Child__private, wszędzie w definicji klasy.

Następujący Testament praca:

class Child(Parent):
    def foo(self):
        self._protected()

    def bar(self):
        self._Parent__private()

Zobacz zarezerwowane klasy identyfikatorów w dokumentacji analizy leksykalnej:

__*
Klasa-nazwy prywatne. Nazwy w tej kategorii, gdy są używane w kontekście definicji klasy, są zapisywane ponownie, aby używać zniekształconej formy, aby uniknąć starć nazw między "prywatnymi" atrybutami klas bazowych i pochodnych.

I odnośnik dokumentacja nazw :

Nazwa prywatna mangling: gdy identyfikator, który występuje tekstowo w definicji klasy, zaczyna się od dwóch lub więcej znaków podkreślenia i nie kończy się na dwóch lub więcej podkreśleń, jest uważany za prywatną nazwę tej klasy. Nazwy prywatne są przekształcane do dłuższej formy przed wygenerowaniem dla nich kodu. Transformacja wstawia nazwę klasy, z usuniętym znakiem podkreślenia i wstawionym pojedynczym znakiem podkreślenia przed nazwą. Na przykład identyfikator __spam występujący w klasie o nazwie Ham zostanie przekształcona na _Ham__spam. Transformacja ta jest niezależna od kontekstu składniowego, w którym używany jest identyfikator.

Nie używaj nazw prywatnych klas, chyba że w szczególności chcesz uniknąć mówienia programistom, którzy chcą podklasować Twoją klasę, że nie mogą używać określonych nazw lub ryzykować złamania twojej klasy. Poza opublikowanymi frameworkami i bibliotekami funkcja ta jest mało przydatna.

PEP 8 Python Style Guide ma to do powiedz o imieniu prywatnym:

Jeśli twoja klasa ma być podklasowana, a masz atrybuty których nie chcesz używać podklas, rozważ nazwanie ich z podwójne podkreślenie na początku i brak podkreślenia na końcu. To wywołuje Nazwa algorytmu mangling Pythona, gdzie nazwa klasy jest w nazwie atrybutu. Pomaga to uniknąć nazwy atrybutu kolizje powinny zawierać atrybuty z to samo imię.

Uwaga 1: zauważ, że tylko prosta nazwa klasy jest używana w mangled nazwa, więc jeśli podklasa wybierze zarówno tę samą nazwę klasy, jak i atrybut nazwa, nadal możesz uzyskać kolizje nazw.

Uwaga 2: manipulowanie nazw może mieć pewne zastosowania, takie jak debugowanie i __getattr__(), mniej wygodne. Jednak nazwa algorytmu mangling jest dobrze udokumentowany i łatwy do wykonania ręcznie.

Uwaga 3: nie wszyscy lubią wymachiwać imionami. Spróbuj zrównoważyć potrzebę unikaj przypadkowej nazwy zderzenia z potencjalnym wykorzystaniem przez zaawansowanych rozmówców.

 60
Author: Martijn Pieters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-18 18:22:26

Również PEP8 mówi

Użyj jednego podkreślenia wiodącego tylko dla niepublicznych metod I instancji zmienne.

Aby uniknąć kolizji nazw z podklasami, użyj dwóch pierwszych podkreślników, aby wywołaj Zasady mangowania nazwy Pythona.

Python wymazuje te nazwy z nazwą klasy: jeśli class Foo mA atrybut o nazwie __a, nie może być dostępny przez Foo.__a. (Natarczywy użytkownik może nadal uzyskać dostęp przez wywołanie Foo._Foo__a.) Ogólnie, podwójne podkreślenie powinno być używane tylko w celu uniknięcia konfliktów nazw z atrybutami w klasach przeznaczonych do podklasowania.

Ty też powinieneś trzymać się z dala od _such_methods, zgodnie z konwencją. Należy traktować je jako private
 6
Author: Deck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-28 09:04:06

Atrybut double __ zmienia się na _ClassName__method_name, co czyni go bardziej prywatnym niż semantyczna prywatność implikowana przez _method_name.

Technicznie możesz się tym zająć, jeśli naprawdę chcesz, ale prawdopodobnie nikt tego nie zrobi, więc ze względu na zachowanie abstrakcji kodu, metoda równie dobrze może być prywatna w tym momencie.

class Parent(object):
    def _protected(self):
        pass

    def __private(self):
        print("Is it really private?")

class Child(Parent):
    def foo(self):
        self._protected()

    def bar(self):
        self.__private()

c = Child()
c._Parent__private()

Ma to dodatkową zaletę (lub niektórzy powiedzieliby główną zaletę) pozwalającą metodzie nie kolidować z nazwami klas podrzędnych.

 6
Author: Tim Wilder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-28 09:04:35

Deklarując prywatność członka danych:

__private()

Po prostu nie możesz uzyskać do niego dostępu spoza klasy

Python obsługuje technikę o nazwie name mangling.

Ta funkcja zamienia element klasy poprzedzony dwoma podkreślnikami na:

_className.memberName

Jeśli chcesz uzyskać do niego dostęp z Child() możesz użyć: self._Parent__private()

 3
Author: Kobi K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-28 09:03:57

Chociaż jest to stare pytanie, natknąłem się na nie i znalazłem dobre obejście.

W przypadku, gdy nazwałeś mangled na klasie rodzica, ponieważ chciałeś naśladować chronioną funkcję, ale nadal chciałeś uzyskać dostęp do funkcji w łatwy sposób na klasie potomnej.

parent_class_private_func_list = [func for func in dir(Child) if func.startswith ('_Parent__')]

for parent_private_func in parent_class_private_func_list:
        setattr(self, parent_private_func.replace("_Parent__", "_Child"), getattr(self, parent_private_func))        

Pomysł polega na ręcznym zastąpieniu nazwy funkcji rodzicielskiej na jedną pasującą do bieżącej przestrzeni nazw. Po dodaniu tego w funkcji init klasy potomnej, można wywołać funkcję w łatwy sposób.

self.__private()
 1
Author: Rohi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-20 15:47:34

AFAIK, w drugim przypadku Python wykonuje "name mangling", więc nazwa metody _ _ prywatnej rodzica klasy jest tak naprawdę:

_Parent__private

I nie można go używać w dziecko w tej formie ani

 0
Author: volcano,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-28 09:00:54