Python zorientowany obiektowo - bloki konstrukcyjne
W tym rozdziale omówimy szczegółowo terminy zorientowane obiektowo i koncepcje programowania. Klasa to tylko przykład fabryki. Ta fabryka zawiera plan, który opisuje, jak tworzyć instancje. Instancje lub obiekt są konstruowane z klasy. W większości przypadków możemy mieć więcej niż jedno wystąpienie klasy. Każda instancja ma zestaw atrybutów, a te atrybuty są zdefiniowane w klasie, więc oczekuje się, że każda instancja określonej klasy będzie miała te same atrybuty.
Pakiety klas: zachowanie i stan
Klasa pozwoli ci połączyć zachowanie i stan obiektu. Aby lepiej zrozumieć, zapoznaj się z poniższym diagramem -
Podczas omawiania pakietów klas warto zwrócić uwagę na następujące punkty:
Słowo behavior jest identyczny z function - jest to fragment kodu, który coś robi (lub implementuje zachowanie)
Słowo state jest identyczny z variables - jest to miejsce do przechowywania wartości w klasie.
Kiedy razem potwierdzamy zachowanie i stan klasy, oznacza to, że klasa pakuje funkcje i zmienne.
Klasy mają metody i atrybuty
W Pythonie tworzenie metody definiuje zachowanie klasy. Metoda słowa to nazwa OOP nadana funkcji zdefiniowanej w klasie. Podsumowując -
Class functions - jest synonimem methods
Class variables - jest synonimem name attributes.
Class - plan instancji z dokładnym zachowaniem.
Object - jedna z instancji klasy, wykonuje funkcje zdefiniowane w klasie.
Type - wskazuje klasę, do której należy instancja
Attribute - Dowolna wartość obiektu: object.attribute
Method - „atrybut wywoływalny” zdefiniowany w klasie
Przyjrzyj się na przykład poniższemu fragmentowi kodu -
var = “Hello, John”
print( type (var)) # ‘str’> or <class 'str'>
print(var.upper()) # upper() method is called, HELLO, JOHN
Tworzenie i tworzenie instancji
Poniższy kod pokazuje, jak utworzyć naszą pierwszą klasę, a następnie jej wystąpienie.
class MyClass(object):
pass
# Create first instance of MyClass
this_obj = MyClass()
print(this_obj)
# Another instance of MyClass
that_obj = MyClass()
print (that_obj)
Tutaj stworzyliśmy klasę o nazwie MyClassi który nie spełnia żadnego zadania. Argumentobject w MyClass class obejmuje dziedziczenie klas i zostanie omówione w dalszych rozdziałach. pass w powyższym kodzie wskazuje, że ten blok jest pusty, czyli jest to pusta definicja klasy.
Stwórzmy instancję this_obj z MyClass() klasę i wydrukuj jak pokazano -
<__main__.MyClass object at 0x03B08E10>
<__main__.MyClass object at 0x0369D390>
Tutaj utworzyliśmy instancję MyClass.Kod szesnastkowy odnosi się do adresu, pod którym przechowywany jest obiekt. Inna instancja wskazuje na inny adres.
Teraz zdefiniujmy jedną zmienną wewnątrz klasy MyClass() i pobierz zmienną z instancji tej klasy, jak pokazano w poniższym kodzie -
class MyClass(object):
var = 9
# Create first instance of MyClass
this_obj = MyClass()
print(this_obj.var)
# Another instance of MyClass
that_obj = MyClass()
print (that_obj.var)
Wynik
Po wykonaniu powyższego kodu można zaobserwować następujące dane wyjściowe -
9
9
Ponieważ instancja wie, z której klasy jest tworzona, więc gdy żąda się atrybutu z instancji, szuka ona atrybutu i klasy. Nazywa się toattribute lookup.
Metody instancji
Funkcja zdefiniowana w klasie nazywa się a method.Metoda instancji wymaga instancji, aby ją wywołać i nie wymaga dekoratora. Podczas tworzenia metody instancji pierwszym parametrem jest zawszeself. Chociaż możemy to nazwać (self) pod jakąkolwiek inną nazwą, zaleca się używanie self, ponieważ jest to konwencja nazewnictwa.
class MyClass(object):
var = 9
def firstM(self):
print("hello, World")
obj = MyClass()
print(obj.var)
obj.firstM()
Wynik
Po wykonaniu powyższego kodu można zaobserwować następujące dane wyjściowe -
9
hello, World
Zauważ, że w powyższym programie zdefiniowaliśmy metodę z self jako argumentem. Ale nie możemy wywołać metody, ponieważ nie zadeklarowaliśmy dla niej żadnego argumentu.
class MyClass(object):
def firstM(self):
print("hello, World")
print(self)
obj = MyClass()
obj.firstM()
print(obj)
Wynik
Po wykonaniu powyższego kodu można zaobserwować następujące dane wyjściowe -
hello, World
<__main__.MyClass object at 0x036A8E10>
<__main__.MyClass object at 0x036A8E10>
Kapsułkowanie
Hermetyzacja jest jedną z podstaw OOP. OOP pozwala nam ukryć złożoność wewnętrznego działania obiektu, co jest korzystne dla dewelopera w następujący sposób -
Upraszcza i ułatwia zrozumienie korzystania z obiektu bez znajomości wewnętrznych elementów.
Każda zmiana może być łatwa do opanowania.
Programowanie zorientowane obiektowo w dużej mierze polega na hermetyzacji. Terminy enkapsulacja i abstrakcja (zwane także ukrywaniem danych) są często używane jako synonimy. Są prawie synonimami, ponieważ abstrakcję osiąga się poprzez hermetyzację.
Hermetyzacja zapewnia nam mechanizm ograniczania dostępu do niektórych komponentów obiektu, co oznacza, że wewnętrzna reprezentacja obiektu nie jest widoczna spoza definicji obiektu. Dostęp do tych danych uzyskuje się zwykle za pomocą specjalnych metod -Getters i Setters.
Te dane są przechowywane w atrybutach instancji i można nimi manipulować z dowolnego miejsca poza klasą. Aby to zabezpieczyć, dostęp do tych danych powinien być możliwy tylko przy użyciu metod instancji. Nie należy zezwalać na bezpośredni dostęp.
class MyClass(object):
def setAge(self, num):
self.age = num
def getAge(self):
return self.age
zack = MyClass()
zack.setAge(45)
print(zack.getAge())
zack.setAge("Fourty Five")
print(zack.getAge())
Wynik
Po wykonaniu powyższego kodu można zaobserwować następujące dane wyjściowe -
45
Fourty Five
Dane powinny być przechowywane tylko wtedy, gdy są poprawne i prawidłowe, przy użyciu konstrukcji obsługi wyjątków. Jak widać powyżej, nie ma ograniczeń co do danych wejściowych użytkownika do metody setAge (). Może to być ciąg, liczba lub lista. Musimy więc sprawdzić powyższy kod, aby upewnić się, że jest on przechowywany.
class MyClass(object):
def setAge(self, num):
self.age = num
def getAge(self):
return self.age
zack = MyClass()
zack.setAge(45)
print(zack.getAge())
zack.setAge("Fourty Five")
print(zack.getAge())
Init Constructor
__init__ jest wywoływana niejawnie zaraz po utworzeniu instancji obiektu klasy, co spowoduje zainicjowanie obiektu.
x = MyClass()
Linia kodu pokazana powyżej utworzy nową instancję i przypisze ten obiekt do lokalnej zmiennej x.
To znaczy operacja tworzenia instancji calling a class object, tworzy pusty obiekt. Wiele klas lubi tworzyć obiekty z instancjami dostosowanymi do określonego stanu początkowego. Dlatego klasa może zdefiniować specjalną metodę o nazwie „__init __ ()”, jak pokazano -
def __init__(self):
self.data = []
Python wywołuje __init__ podczas tworzenia instancji, aby zdefiniować dodatkowy atrybut, który powinien wystąpić podczas tworzenia instancji klasy. Może to być konfigurowanie niektórych wartości początkowych dla tego obiektu lub uruchamianie procedury wymaganej podczas tworzenia instancji. W tym przykładzie nową, zainicjowaną instancję można uzyskać przez -
x = MyClass()
Metoda __init __ () może mieć jeden lub wiele argumentów dla większej elastyczności. Init oznacza inicjalizację, ponieważ inicjalizuje atrybuty instancji. Nazywa się konstruktorem klasy.
class myclass(object):
def __init__(self,aaa, bbb):
self.a = aaa
self.b = bbb
x = myclass(4.5, 3)
print(x.a, x.b)
Wynik
4.5 3
Atrybuty klas
Atrybut zdefiniowany w klasie nazywa się „atrybutami klasy”, a atrybuty zdefiniowane w funkcji - „atrybutami instancji”. Podczas definiowania atrybuty te nie są poprzedzane przedrostkiem self, ponieważ są one własnością klasy, a nie konkretnej instancji.
Dostęp do atrybutów klasy można uzyskać za pośrednictwem samej klasy (nazwaKlasy.nazwa_atrybutu), a także instancji tej klasy (nazwa instancji). Zatem instancje mają dostęp zarówno do atrybutu instancji, jak i atrybutów klas.
>>> class myclass():
age = 21
>>> myclass.age
21
>>> x = myclass()
>>> x.age
21
>>>
Atrybut klasy można przesłonić w instancji, nawet jeśli nie jest to dobra metoda przerywania hermetyzacji.
W Pythonie istnieje ścieżka wyszukiwania atrybutów. Pierwsza to metoda zdefiniowana w klasie, a następnie klasa nad nią.
>>> class myclass(object):
classy = 'class value'
>>> dd = myclass()
>>> print (dd.classy) # This should return the string 'class value'
class value
>>>
>>> dd.classy = "Instance Value"
>>> print(dd.classy) # Return the string "Instance Value"
Instance Value
>>>
>>> # This will delete the value set for 'dd.classy' in the instance.
>>> del dd.classy
>>> >>> # Since the overriding attribute was deleted, this will print 'class
value'.
>>> print(dd.classy)
class value
>>>
Zastępujemy atrybut klasy „classy” w instancji dd. Kiedy jest zastępowany, interpreter Pythona odczytuje zastąpioną wartość. Ale gdy nowa wartość zostanie usunięta za pomocą „del”, nadpisana wartość nie jest już obecna w instancji, a zatem wyszukiwanie przechodzi o poziom wyżej i pobiera ją z klasy.
Praca z danymi klas i instancji
W tej sekcji wyjaśnijmy, jak dane klasy odnoszą się do danych instancji. Możemy przechowywać dane w klasie lub w instancji. Projektując klasę, decydujemy, które dane należą do instancji, a które powinny być przechowywane w całej klasie.
Instancja może uzyskać dostęp do danych klasy. Jeśli utworzymy wiele instancji, wtedy te instancje będą miały dostęp do swoich indywidualnych wartości atrybutów, a także do ogólnych danych klas.
Zatem dane klasy to dane, które są współużytkowane przez wszystkie instancje. Przestrzegaj kodu podanego poniżej, aby lepiej rozpoznać -
class InstanceCounter(object):
count = 0 # class attribute, will be accessible to all instances
def __init__(self, val):
self.val = val
InstanceCounter.count +=1 # Increment the value of class attribute, accessible through class name
# In above line, class ('InstanceCounter') act as an object
def set_val(self, newval):
self.val = newval
def get_val(self):
return self.val
def get_count(self):
return InstanceCounter.count
a = InstanceCounter(9)
b = InstanceCounter(18)
c = InstanceCounter(27)
for obj in (a, b, c):
print ('val of obj: %s' %(obj.get_val())) # Initialized value ( 9, 18, 27)
print ('count: %s' %(obj.get_count())) # always 3
Wynik
val of obj: 9
count: 3
val of obj: 18
count: 3
val of obj: 27
count: 3
Krótko mówiąc, atrybuty klas są takie same dla wszystkich instancji klasy, podczas gdy atrybuty instancji są specyficzne dla każdej instancji. Dla dwóch różnych instancji będziemy mieć dwa różne atrybuty instancji.
class myClass:
class_attribute = 99
def class_method(self):
self.instance_attribute = 'I am instance attribute'
print (myClass.__dict__)
Wynik
Po wykonaniu powyższego kodu można zaobserwować następujące dane wyjściowe -
{'__module__': '__main__', 'class_attribute': 99, 'class_method':
, '__dict__':
, '__weakref__':
, '__doc__': None}
The instance attribute myClass.__dict__ as shown −
>>> a = myClass()
>>> a.class_method()
>>> print(a.__dict__)
{'instance_attribute': 'I am instance attribute'}