Python – język zorientowany obiektowo

Ogłoszenia

Python jest językiem zorientowanym obiektowo od kiedy istnieje. Z tego powodu tworzenie i używanie klas i obiektów jest bardzo proste. Ten rozdział pomoże Ci stać się ekspertem w korzystaniu z obsługi programowania obiektowego w Pythonie.

Jeśli nie masz wcześniejszego doświadczenia z programowaniem obiektowym (OO), możesz chcieć skonsultować się z kursem wprowadzającym do tego języka lub przynajmniej z jakimś samouczkiem, tak abyś zrozumiał podstawowe pojęcia.

Jednakże, oto małe wprowadzenie do programowania zorientowanego obiektowo (OOP), aby przyśpieszyć –

Overview of OOP Terminology

  • Klasa – zdefiniowany przez użytkownika prototyp obiektu, który definiuje zestaw atrybutów charakteryzujących każdy obiekt tej klasy. Atrybutami są członkowie danych (zmienne klasy i zmienne instancji) oraz metody, dostępne poprzez notację kropkową.

  • Zmienna klasy – Zmienna, która jest wspólna dla wszystkich instancji klasy. Zmienne klasowe są definiowane wewnątrz klasy, ale poza jej metodami. Zmienne klasowe nie są używane tak często jak zmienne instancji.

  • Członek danych – Zmienna klasowa lub zmienna instancji, która przechowuje dane związane z klasą i jej obiektami.

  • Przeciążanie funkcji – Przypisanie więcej niż jednego zachowania danej funkcji. Wykonywana operacja różni się w zależności od typów obiektów lub argumentów biorących udział.

  • Zmienna instancyjna – Zmienna, która jest zdefiniowana wewnątrz metody i należy tylko do bieżącej instancji klasy.

  • Dziedziczenie – Przekazywanie cech danej klasy innym klasom, które są z niej pochodne.

  • Instancja – Indywidualny obiekt pewnej klasy. Obiekt obj, który należy do klasy Circle, jest na przykład instancją klasy Circle.

  • Instytucja – Tworzenie instancji klasy.

  • Metoda – Specjalny rodzaj funkcji, która jest zdefiniowana w definicji klasy.

  • Obiekt – Unikalna instancja struktury danych, która jest zdefiniowana przez swoją klasę. Obiekt zawiera zarówno członków danych (zmienne klasowe i zmienne instancji) jak i metody.

  • Przeciążanie operatorów – przypisanie więcej niż jednej funkcji do konkretnego operatora.

Tworzenie klas

Konstrukcja klasy tworzy nową definicję klasy. Nazwa klasy następuje bezpośrednio po słowie kluczowym class, po którym następuje dwukropek w następujący sposób –

class ClassName: 'Optional class documentation string' class_suite
  • Klasa posiada łańcuch dokumentacji, który może być dostępny poprzez ClassName.__doc__.

  • Klasa_suite składa się ze wszystkich deklaracji składowych definiujących członków klasy, atrybuty danych i funkcje.

Przykład

Poniżej znajduje się przykład prostej klasy Pythona –

class Employee: 'Common base class for all employees' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salary
  • Zmienna empCount jest zmienną klasową, której wartość jest współdzielona pomiędzy wszystkie instancje tej klasy. Można do niej uzyskać dostęp jako Employee.empCount z wnętrza klasy lub spoza niej.

  • Pierwsza metoda __init__() jest specjalną metodą, która jest nazywana konstruktorem klasy lub metodą inicjalizacyjną, którą Python wywołuje, gdy tworzysz nową instancję tej klasy.

  • Inne metody klasy deklarujesz jak normalne funkcje z tym wyjątkiem, że pierwszym argumentem każdej metody jest self. Python dodaje argument self do listy za Ciebie; nie musisz go dołączać podczas wywoływania metod.

Tworzenie instancji obiektów

Aby utworzyć instancje klasy, wywołujesz klasę za pomocą jej nazwy i przekazujesz argumenty, które przyjmuje jej metoda __init__.

"This would create first object of Employee class"emp1 = Employee("Zara", 2000)"This would create second object of Employee class"emp2 = Employee("Manni", 5000)

Dostęp do atrybutów

Dostęp do atrybutów obiektu uzyskujemy za pomocą operatora kropki z obiektem. Dostęp do zmiennej klasy byłby możliwy przy użyciu nazwy klasy w następujący sposób –

emp1.displayEmployee()emp2.displayEmployee()print "Total Employee %d" % Employee.empCount

Teraz, łącząc wszystkie koncepcje razem –

#!/usr/bin/pythonclass Employee: 'Common base class for all employees' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salary"This would create first object of Employee class"emp1 = Employee("Zara", 2000)"This would create second object of Employee class"emp2 = Employee("Manni", 5000)emp1.displayEmployee()emp2.displayEmployee()print "Total Employee %d" % Employee.empCount

Gdy powyższy kod jest wykonywany, daje następujący wynik –

Name : Zara ,Salary: 2000Name : Manni ,Salary: 5000Total Employee 2

Możesz dodawać, usuwać lub modyfikować atrybuty klas i obiektów w dowolnym momencie –

emp1.age = 7 # Add an 'age' attribute.emp1.age = 8 # Modify 'age' attribute.del emp1.age # Delete 'age' attribute.

Zamiast używać normalnych deklaracji dostępu do atrybutów, można użyć następujących funkcji –

  • Getattr(obj, nazwa) – aby uzyskać dostęp do atrybutu obiektu.

  • The hasattr(obj,nazwa) – aby sprawdzić czy atrybut istnieje czy nie.

  • The setattr(obj,nazwa,wartość) – aby ustawić atrybut. Jeżeli atrybut nie istnieje, to zostanie utworzony.

  • Delattr(obj,nazwa) – aby usunąć atrybut.

hasattr(emp1, 'age') # Returns true if 'age' attribute existsgetattr(emp1, 'age') # Returns value of 'age' attributesetattr(emp1, 'age', 8) # Set attribute 'age' at 8delattr(empl, 'age') # Delete attribute 'age'

Wbudowane atrybuty klasy

Każda klasa Pythona posiada następujące wbudowane atrybuty i można się do nich dostać za pomocą operatora kropki, jak do każdego innego atrybutu –

  • __dict__ – Słownik zawierający przestrzeń nazw klasy.

  • __doc__ – Łańcuch dokumentacji klasy lub jego brak, jeśli jest niezdefiniowana.

  • __name__ – Nazwa klasy.

  • __module__ – Nazwa modułu, w którym zdefiniowana jest klasa. W trybie interaktywnym atrybutem tym jest “__main__”.

  • __bases__ – Możliwie pusta tuple zawierająca klasy bazowe w kolejności ich występowania na liście klas bazowych.

Dla powyższej klasy spróbujmy uzyskać dostęp do wszystkich tych atrybutów –

#!/usr/bin/pythonclass Employee: 'Common base class for all employees' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salaryprint "Employee.__doc__:", Employee.__doc__print "Employee.__name__:", Employee.__name__print "Employee.__module__:", Employee.__module__print "Employee.__bases__:", Employee.__bases__print "Employee.__dict__:", Employee.__dict__

Gdy powyższy kod zostanie wykonany, daje on następujący wynik –

Employee.__doc__: Common base class for all employeesEmployee.__name__: EmployeeEmployee.__module__: __main__Employee.__bases__: ()Employee.__dict__: {'__module__': '__main__', 'displayCount':<function displayCount at 0xb7c84994>, 'empCount': 2, 'displayEmployee': <function displayEmployee at 0xb7c8441c>, '__doc__': 'Common base class for all employees', '__init__': <function __init__ at 0xb7c846bc>}

Usuwanie obiektów (Garbage Collection)

Python usuwa niepotrzebne obiekty (typy wbudowane lub instancje klas) automatycznie, aby zwolnić miejsce w pamięci. Proces, w którym Python okresowo odzyskuje bloki pamięci, które nie są już używane, jest określany jako Garbage Collection.

Python’s garbage collector działa podczas wykonywania programu i jest uruchamiany, gdy liczba referencji obiektu osiągnie zero. Liczba referencji obiektu zmienia się wraz ze zmianą liczby aliasów, które na niego wskazują.

Liczba referencji obiektu wzrasta, gdy zostanie mu nadana nowa nazwa lub zostanie umieszczony w kontenerze (liście, krotce lub słowniku). Liczba referencji obiektu maleje, gdy zostanie on usunięty za pomocą del, jego referencja zostanie ponownie przypisana lub jego referencja wyjdzie poza zakres. Gdy liczba referencji obiektu osiągnie zero, Python automatycznie go zbiera.

a = 40 # Create object <40>b = a # Increase ref. count of <40> c = # Increase ref. count of <40> del a # Decrease ref. count of <40>b = 100 # Decrease ref. count of <40> c = -1 # Decrease ref. count of <40> 

Zazwyczaj nie zauważysz, kiedy śmieciarz zniszczy osieroconą instancję i odzyska jej miejsce. Ale klasa może zaimplementować specjalną metodę __del__(), zwaną destruktorem, która jest wywoływana, gdy instancja ma zostać zniszczona. Metoda ta może być użyta do wyczyszczenia wszelkich zasobów niepamięci używanych przez instancję.

Przykład

Ten destruktor __del__() wypisuje nazwę klasy instancji, która ma zostać zniszczona –

#!/usr/bin/pythonclass Point: def __init__( self, x=0, y=0): self.x = x self.y = y def __del__(self): class_name = self.__class__.__name__ print class_name, "destroyed"pt1 = Point()pt2 = pt1pt3 = pt1print id(pt1), id(pt2), id(pt3) # prints the ids of the obejctsdel pt1del pt2del pt3

Gdy powyższy kod zostanie wykonany, daje następujący rezultat –

3083401324 3083401324 3083401324Point destroyed

Uwaga – Idealnie byłoby, gdybyś zdefiniował swoje klasy w osobnym pliku, a następnie zaimportował je do swojego głównego pliku programu używając instrukcji import.

Dziedziczenie klas

Zamiast zaczynać od zera, możesz stworzyć klasę poprzez wyprowadzenie jej z wcześniej istniejącej klasy, wymieniając klasę nadrzędną w nawiasie po nazwie nowej klasy.

Klasa dziecięca dziedziczy atrybuty swojej klasy nadrzędnej, i możesz używać tych atrybutów tak jakby były zdefiniowane w klasie dziecięcej. Klasa potomna może również nadpisać dane członków i metod klasy nadrzędnej.

Syntaktyka

Klasy pochodne są deklarowane podobnie jak ich klasa nadrzędna; jednak po nazwie klasy podawana jest lista klas bazowych, z których należy dziedziczyć –

class SubClassName (ParentClass1): 'Optional class documentation string' class_suite

Przykład

#!/usr/bin/pythonclass Parent: # define parent class parentAttr = 100 def __init__(self): print "Calling parent constructor" def parentMethod(self): print 'Calling parent method' def setAttr(self, attr): Parent.parentAttr = attr def getAttr(self): print "Parent attribute :", Parent.parentAttrclass Child(Parent): # define child class def __init__(self): print "Calling child constructor" def childMethod(self): print 'Calling child method'c = Child() # instance of childc.childMethod() # child calls its methodc.parentMethod() # calls parent's methodc.setAttr(200) # again call parent's methodc.getAttr() # again call parent's method

Po wykonaniu powyższego kodu otrzymujemy następujący wynik –

Calling child constructorCalling child methodCalling parent methodParent attribute : 200

Podobnie, możesz prowadzić klasę z wielu klas nadrzędnych w następujący sposób –

class A: # define your class A.....class B: # define your class B.....class C(A, B): # subclass of A and B.....

Możesz użyć funkcji issubclass() lub isinstance() do sprawdzenia relacji dwóch klas i instancji.

  • Funkcja boolean issubclass(sub, sup) zwraca wartość true, jeśli podana podklasa sub jest rzeczywiście podklasą nadklasy sup.

  • Funkcja boolean isinstance(obj, Class) zwraca wartość true, jeśli obj jest instancją klasy Class lub jest instancją podklasy Class

Nadpisywanie metod

Zawsze możesz nadpisać metody klasy nadrzędnej. Jednym z powodów nadpisywania metod rodzica jest to, że możesz chcieć specjalnej lub innej funkcjonalności w swojej podklasie.

Przykład

#!/usr/bin/pythonclass Parent: # define parent class def myMethod(self): print 'Calling parent method'class Child(Parent): # define child class def myMethod(self): print 'Calling child method'c = Child() # instance of childc.myMethod() # child calls overridden method

Gdy powyższy kod jest wykonywany, daje on następujący wynik –

Calling child method

Metody przeciążania bazy

Następująca tabela zawiera listę pewnych ogólnych funkcjonalności, które można nadpisać we własnych klasach –

Sr.No. Metoda, opis & Przykładowe wywołanie
1

__init__ ( self )

Konstruktor (z dowolnymi opcjonalnymi argumentami)

Przykładowe wywołanie : obj = className(args)

2

__del__( self )

Destruktor, usuwa obiekt

Przykładowe wywołanie : del obj

3

__repr__( self )

Wartościowa reprezentacja łańcucha

Przykładowe wywołanie : repr(obj)

4

__str__( self )

Wydrukowana reprezentacja ciągu znaków

Przykładowe wywołanie : str(obj)

5

__cmp__ ( self, x )

Porównanie obiektów

Przykładowe wywołanie : cmp(obj, x)

Przeciążanie operatorów

Załóżmy, że stworzyłeś klasę Vector do reprezentowania dwuwymiarowych wektorów, co się stanie, gdy użyjesz operatora plusa, aby je dodać? Najprawdopodobniej Python zacznie na Ciebie krzyczeć.

Mógłbyś jednak zdefiniować w swojej klasie metodę __add__, aby wykonać dodawanie wektorów, a wtedy operator plusa zachowałby się zgodnie z oczekiwaniami –

Przykład

#!/usr/bin/pythonclass Vector: def __init__(self, a, b): self.a = a self.b = b def __str__(self): return 'Vector (%d, %d)' % (self.a, self.b) def __add__(self,other): return Vector(self.a + other.a, self.b + other.b)v1 = Vector(2,10)v2 = Vector(5,-2)print v1 + v2

Gdy powyższy kod zostanie wykonany, daje następujący rezultat –

Vector(7,8)

Ukrywanie danych

Atrybuty obiektu mogą lub nie mogą być widoczne poza definicją klasy. Musisz nazwać atrybuty z przedrostkiem podwójnego podkreślenia, a atrybuty te nie będą wtedy bezpośrednio widoczne dla osób z zewnątrz.

Przykład

#!/usr/bin/pythonclass JustCounter: __secretCount = 0 def count(self): self.__secretCount += 1 print self.__secretCountcounter = JustCounter()counter.count()counter.count()print counter.__secretCount

Po wykonaniu powyższego kodu otrzymamy następujący wynik –

12Traceback (most recent call last): File "test.py", line 12, in <module> print counter.__secretCountAttributeError: JustCounter instance has no attribute '__secretCount'

Python chroni tych członków poprzez wewnętrzną zmianę nazwy tak, aby zawierała nazwę klasy. Możesz uzyskać dostęp do takich atrybutów jako object._className__attrName. Jeśli zastąpiłbyś ostatnią linię następującą linią, wtedy to zadziała dla ciebie –

.........................print counter._JustCounter__secretCount

Gdy powyższy kod jest wykonywany, daje następujący wynik –

Leave a Comment

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *