Python zorientowany obiektowo - zaawansowane funkcje
W tym artykule przyjrzymy się niektórym zaawansowanym funkcjom, które zapewnia Python
Podstawowa składnia w naszym projekcie klasy
W tym artykule przyjrzymy się, jak Python pozwala nam wykorzystać operatory w naszych klasach. Python to w dużej mierze obiekty i metody wywołujące obiekty, a dzieje się to nawet wtedy, gdy jest ukryte przez jakąś wygodną składnię.
>>> var1 = 'Hello'
>>> var2 = ' World!'
>>> var1 + var2
'Hello World!'
>>>
>>> var1.__add__(var2)
'Hello World!'
>>> num1 = 45
>>> num2 = 60
>>> num1.__add__(num2)
105
>>> var3 = ['a', 'b']
>>> var4 = ['hello', ' John']
>>> var3.__add__(var4)
['a', 'b', 'hello', ' John']
Więc jeśli musimy dodać magiczną metodę __add__ do naszych własnych klas, czy możemy to zrobić. Spróbujmy to zrobić.
Mamy klasę o nazwie Sumlist, która ma konstruktora __init__, który przyjmuje list jako argument o nazwie my_list.
class SumList(object):
def __init__(self, my_list):
self.mylist = my_list
def __add__(self, other):
new_list = [ x + y for x, y in zip(self.mylist, other.mylist)]
return SumList(new_list)
def __repr__(self):
return str(self.mylist)
aa = SumList([3,6, 9, 12, 15])
bb = SumList([100, 200, 300, 400, 500])
cc = aa + bb # aa.__add__(bb)
print(cc) # should gives us a list ([103, 206, 309, 412, 515])
Wynik
[103, 206, 309, 412, 515]
Ale jest wiele metod, którymi wewnętrznie zarządzają inne magiczne metody. Poniżej znajduje się kilka z nich,
'abc' in var # var.__contains__('abc')
var == 'abc' # var.__eq__('abc')
var[1] # var.__getitem__(1)
var[1:3] # var.__getslice__(1, 3)
len(var) # var.__len__()
print(var) # var.__repr__()
Dziedziczenie z typów wbudowanych
Klasy mogą również dziedziczyć po typach wbudowanych, co oznacza, że dziedziczy po każdym wbudowanym i korzysta ze wszystkich dostępnych tam funkcji.
W poniższym przykładzie dziedziczymy ze słownika, ale potem implementujemy jedną z jego metod __setitem__. Ten (setitem) jest wywoływany, gdy ustawiamy klucz i wartość w słowniku. Ponieważ jest to metoda magiczna, zostanie ona wywołana niejawnie.
class MyDict(dict):
def __setitem__(self, key, val):
print('setting a key and value!')
dict.__setitem__(self, key, val)
dd = MyDict()
dd['a'] = 10
dd['b'] = 20
for key in dd.keys():
print('{0} = {1}'.format(key, dd[key]))
Wynik
setting a key and value!
setting a key and value!
a = 10
b = 20
Rozszerzmy nasz poprzedni przykład, poniżej wywołaliśmy dwie magiczne metody o nazwach __getitem__ i __setitem__, które są lepiej wywoływane, gdy mamy do czynienia z indeksem listy.
# Mylist inherits from 'list' object but indexes from 1 instead for 0!
class Mylist(list): # inherits from list
def __getitem__(self, index):
if index == 0:
raise IndexError
if index > 0:
index = index - 1
return list.__getitem__(self, index) # this method is called when
# we access a value with subscript like x[1]
def __setitem__(self, index, value):
if index == 0:
raise IndexError
if index > 0:
index = index - 1
list.__setitem__(self, index, value)
x = Mylist(['a', 'b', 'c']) # __init__() inherited from builtin list
print(x) # __repr__() inherited from builtin list
x.append('HELLO'); # append() inherited from builtin list
print(x[1]) # 'a' (Mylist.__getitem__ cutomizes list superclass
# method. index is 1, but reflects 0!
print (x[4]) # 'HELLO' (index is 4 but reflects 3!
Wynik
['a', 'b', 'c']
a
HELLO
W powyższym przykładzie ustawiamy listę trzech pozycji w Mylist i niejawnie wywoływana jest metoda __init__, a kiedy drukujemy element x, otrzymujemy listę trzech pozycji (['a', 'b', 'c']). Następnie dodajemy kolejny element do tej listy. Później prosimy o indeks 1 i indeks 4. Ale jeśli zobaczysz wynik, otrzymujemy element z (indeks-1), o który prosiliśmy. Jak wiemy, indeksowanie listy zaczyna się od 0, ale tutaj indeksowanie zaczyna się od 1 (dlatego otrzymujemy pierwszą pozycję z listy).
Konwencje nazewnictwa
W tym artykule przyjrzymy się nazwom, których będziemy używać dla zmiennych, zwłaszcza zmiennych prywatnych i konwencji używanych przez programistów Pythona na całym świecie. Chociaż zmienne są oznaczone jako prywatne, ale w Pythonie nie ma prywatności i jest to zgodne z projektem. Jak każdy inny dobrze udokumentowany język, Python ma konwencje nazewnictwa i stylów, które promuje, chociaż ich nie wymusza. Istnieje przewodnik stylistyczny napisany przez „Guido van Rossum” the originator of Python, that describe the best practices and use of name and is called PEP8. Here is the link for this, https://www.python.org/dev/peps/pep-0008/
PEP oznacza propozycję ulepszenia języka Python i jest serią dokumentacji rozpowszechnianej wśród społeczności Pythona w celu omówienia proponowanych zmian. Na przykład zaleca się wszystkim,
- Nazwy modułów - all_lower_case
- Nazwy klas i nazwy wyjątków - CamelCase
- Nazwy globalne i lokalne - all_lower_case
- Funkcje i nazwy metod - all_lower_case
- Stałe - ALL_UPPER_CASE
To tylko zalecenia, możesz je zmieniać, jeśli chcesz. Ale ponieważ większość programistów postępuje zgodnie z tymi zaleceniami, może mi się zdaje, że Twój kod jest mniej czytelny.
Po co dostosowywać się do konwencji?
Możemy postępować zgodnie z zaleceniami PEP, które nam pozwala,
- Bardziej znany większości programistów
- Jaśniejsze dla większości czytelników twojego kodu.
- Będzie pasował do stylu innych współpracowników, którzy pracują na tej samej bazie kodu.
- Znak profesjonalnego programisty
- Wszyscy cię zaakceptują.
Nazewnictwo zmiennych - „Publiczne” i „Prywatne”
W Pythonie, gdy mamy do czynienia z modułami i klasami, określamy niektóre zmienne lub atrybut jako prywatne. W Pythonie nie istnieje zmienna instancji „prywatna”, do której nie można uzyskać dostępu poza obiektem. Prywatne oznacza po prostu, że po prostu nie są przeznaczone do użytku przez użytkowników kodu, a zamiast tego mają być używane wewnętrznie. Ogólnie rzecz biorąc, większość programistów Pythona przestrzega pewnej konwencji, na przykład nazwy poprzedzonej podkreśleniem. _attrval (przykład poniżej) należy traktować jako niepubliczną część interfejsu API lub dowolny kod Pythona, niezależnie od tego, czy jest to funkcja, metoda czy element członkowski danych. Poniżej znajduje się konwencja nazewnictwa, którą przestrzegamy,
Atrybuty lub zmienne publiczne (przeznaczone do wykorzystania przez importera tego modułu lub użytkownika tej klasy) -regular_lower_case
Prywatne atrybuty lub zmienne (wewnętrzne użycie przez moduł lub klasę) -_single_leading_underscore
Prywatne atrybuty, które nie powinny być podklasą -__double_leading_underscore
Magiczne atrybuty -__double_underscores__(używaj ich, nie twórz ich)
class GetSet(object):
instance_count = 0 # public
__mangled_name = 'no privacy!' # special variable
def __init__(self, value):
self._attrval = value # _attrval is for internal use only
GetSet.instance_count += 1
@property
def var(self):
print('Getting the "var" attribute')
return self._attrval
@var.setter
def var(self, value):
print('setting the "var" attribute')
self._attrval = value
@var.deleter
def var(self):
print('deleting the "var" attribute')
self._attrval = None
cc = GetSet(5)
cc.var = 10 # public name
print(cc._attrval)
print(cc._GetSet__mangled_name)
Wynik
setting the "var" attribute
10
no privacy!