Python Language
Klasy
Szukaj…
Wprowadzenie
Python oferuje się nie tylko jako popularny język skryptowy, ale obsługuje także obiektowy paradygmat programowania. Klasy opisują dane i udostępniają metody manipulowania tymi danymi, wszystkie zawarte w jednym obiekcie. Ponadto klasy pozwalają na abstrakcję, oddzielając konkretne szczegóły implementacji od abstrakcyjnych reprezentacji danych.
Kod wykorzystujący klasy jest ogólnie łatwiejszy do odczytania, zrozumienia i utrzymania.
Podstawowe dziedzictwo
Dziedziczenie w Pythonie opiera się na podobnych pomysłach używanych w innych językach obiektowych, takich jak Java, C ++ itp. Nowa klasa może zostać wyprowadzona z istniejącej klasy w następujący sposób.
class BaseClass(object):
pass
class DerivedClass(BaseClass):
pass
BaseClass
to już istniejąca ( nadrzędna ) klasa, a DerivedClass
to nowa ( podrzędna ) klasa, która dziedziczy (lub podklasy ) atrybuty z BaseClass
. Uwaga : Począwszy od Python 2.2, wszystkie klasy domyślnie dziedziczą po klasie object
, która jest klasą bazową dla wszystkich typów wbudowanych.
Nadrzędną klasę Rectangle
definiujemy w poniższym przykładzie, który domyślnie dziedziczy po object
:
class Rectangle():
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
def perimeter(self):
return 2 * (self.w + self.h)
Klasa Rectangle
może być używana jako klasa podstawowa do definiowania klasy Square
, ponieważ kwadrat jest specjalnym przypadkiem prostokąta.
class Square(Rectangle):
def __init__(self, s):
# call parent constructor, w and h are both s
super(Square, self).__init__(s, s)
self.s = s
Klasa Square
automatycznie odziedziczy wszystkie atrybuty klasy Rectangle
a także klasę obiektów. super()
służy do wywołania metody __init__()
klasy Rectangle
, w zasadzie wywołując dowolną przesłoniętą metodę klasy bazowej. Uwaga : w Pythonie 3 super()
nie wymaga argumentów.
Obiekty klasy pochodnej mogą uzyskiwać dostęp i modyfikować atrybuty swoich klas podstawowych:
r.area()
# Output: 12
r.perimeter()
# Output: 14
s.area()
# Output: 4
s.perimeter()
# Output: 8
Wbudowane funkcje działające z dziedziczeniem
issubclass(DerivedClass, BaseClass)
: zwraca True
jeśli DerivedClass
jest podklasą BaseClass
isinstance(s, Class)
: zwraca True
jeśli s jest instancją Class
lub dowolnej pochodnej klasy Class
# subclass check
issubclass(Square, Rectangle)
# Output: True
# instantiate
r = Rectangle(3, 4)
s = Square(2)
isinstance(r, Rectangle)
# Output: True
isinstance(r, Square)
# Output: False
# A rectangle is not a square
isinstance(s, Rectangle)
# Output: True
# A square is a rectangle
isinstance(s, Square)
# Output: True
Zmienne klas i instancji
Zmienne instancji są unikalne dla każdej instancji, podczas gdy zmienne klasy są wspólne dla wszystkich instancji.
class C:
x = 2 # class variable
def __init__(self, y):
self.y = y # instance variable
C.x
# 2
C.y
# AttributeError: type object 'C' has no attribute 'y'
c1 = C(3)
c1.x
# 2
c1.y
# 3
c2 = C(4)
c2.x
# 2
c2.y
# 4
Dostęp do zmiennych klas można uzyskać w instancjach tej klasy, ale przypisanie do atrybutu class spowoduje utworzenie zmiennej instancji, która zasłoni zmienną klasy
c2.x = 4
c2.x
# 4
C.x
# 2
Zauważ, że mutowanie zmiennych klas z instancji może prowadzić do nieoczekiwanych konsekwencji.
class D:
x = []
def __init__(self, item):
self.x.append(item) # note that this is not an assigment!
d1 = D(1)
d2 = D(2)
d1.x
# [1, 2]
d2.x
# [1, 2]
D.x
# [1, 2]
Metody powiązane, niezwiązane i statyczne
Idea związanych i niezwiązanych metod została usunięta w Pythonie 3 . W Pythonie 3, kiedy deklarujesz metodę w klasie, używasz słowa kluczowego def
, tworząc obiekt funkcji. Jest to regularna funkcja, a otaczająca klasa działa jako jej przestrzeń nazw. W poniższym przykładzie deklarujemy metodę f
w klasie A
i staje się ona funkcją Af
:
class A(object):
def f(self, x):
return 2 * x
A.f
# <function A.f at ...> (in Python 3.x)
W Pythonie 2 zachowanie było inne: obiekty funkcji w klasie zostały domyślnie zastąpione obiektami typu instancemethod
, które nazwano metodami niezwiązanymi, ponieważ nie były powiązane z żadną konkretną instancją klasy. Można było uzyskać dostęp do funkcji podstawowej za pomocą właściwości .__func__
.
A.f
# <unbound method A.f> (in Python 2.x)
A.f.__class__
# <type 'instancemethod'>
A.f.__func__
# <function f at ...>
Te ostatnie zachowania są potwierdzane przez inspekcję - metody są rozpoznawane jako funkcje w Pythonie 3, podczas gdy rozróżnienie jest utrzymywane w Pythonie 2.
import inspect
inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False
import inspect
inspect.isfunction(A.f)
# False
inspect.ismethod(A.f)
# True
W obu wersjach funkcji / metody Python Af
można wywoływać bezpośrednio, pod warunkiem, że jako pierwszy argument podasz instancję klasy A
A.f(1, 7)
# Python 2: TypeError: unbound method f() must be called with
# A instance as first argument (got int instance instead)
# Python 3: 14
a = A()
A.f(a, 20)
# Python 2 & 3: 40
Załóżmy teraz, że a
jest instancją klasy A
, co to jest af
? Cóż, intuicyjnie powinna to być ta sama metoda f
klasy A
, tylko powinna jakoś „wiedzieć”, że została zastosowana do obiektu a
- w Pythonie nazywa się to metodą związaną z a
.
Dane sedna są następujące: pisanie af
wywołuje magia __getattribute__
metodę , który najpierw sprawdza, czy posiada atrybut o nazwie e a
a
f
(to nie ma), a następnie sprawdza, klasa czy zawiera ona metodę o takiej nazwie A
(robi) i tworzy nowy obiekt m
method
typu method
który ma odniesienie do oryginalnego Af
m.__func__
i odniesienie do obiektu a
w m.__self__
. Gdy ten obiekt jest wywoływany jako funkcja, wykonuje on następujące czynności: m(...) => m.__func__(m.__self__, ...)
. W ten sposób obiekt ten nazywany jest metodą powiązaną, ponieważ po wywołaniu wie, jak dostarczyć obiekt, do którego został przypisany jako pierwszy argument. (Te rzeczy działają tak samo w Pythonie 2 i 3).
a = A()
a.f
# <bound method A.f of <__main__.A object at ...>>
a.f(2)
# 4
# Note: the bound method object a.f is recreated *every time* you call it:
a.f is a.f # False
# As a performance optimization you can store the bound method in the object's
# __dict__, in which case the method object will remain fixed:
a.f = a.f
a.f is a.f # True
Wreszcie, Python ma metody klasowe i metody statyczne - specjalne rodzaje metod. Metody klasowe działają tak samo jak zwykłe metody, z tym wyjątkiem, że po wywołaniu na obiekcie wiążą się z klasą obiektu zamiast z obiektem. Zatem m.__self__ = type(a)
. Kiedy wywołujesz taką powiązaną metodę, przekazuje klasę a
jako pierwszy argument. Metody statyczne są jeszcze prostsze: w ogóle nic nie wiążą, a po prostu zwracają podstawową funkcję bez żadnych przekształceń.
class D(object):
multiplier = 2
@classmethod
def f(cls, x):
return cls.multiplier * x
@staticmethod
def g(name):
print("Hello, %s" % name)
D.f
# <bound method type.f of <class '__main__.D'>>
D.f(12)
# 24
D.g
# <function D.g at ...>
D.g("world")
# Hello, world
Zauważ, że metody klas są powiązane z klasą, nawet jeśli są dostępne w instancji:
d = D()
d.multiplier = 1337
(D.multiplier, d.multiplier)
# (2, 1337)
d.f
# <bound method D.f of <class '__main__.D'>>
d.f(10)
# 20
Warto zauważyć, że na najniższym poziomie funkcje, metody, metody statyczne itp. Są tak naprawdę deskryptorami, które wywołują metody specjalne __get__
, __set
__ i opcjonalnie __del__
. Aby uzyskać więcej informacji na temat metod klas i metod statycznych:
- Jaka jest różnica między @staticmethod i @classmethod w Pythonie?
- Znaczenie @classmethod i @staticmethod dla początkującego?
Klasy w nowym stylu a w starym stylu
Klasy nowego stylu zostały wprowadzone w Pythonie 2.2 w celu ujednolicenia klas i typów . Dziedziczą po typie object
najwyższego poziomu. Klasa nowego stylu jest typem zdefiniowanym przez użytkownika i jest bardzo podobna do typów wbudowanych.
# new-style class
class New(object):
pass
# new-style instance
new = New()
new.__class__
# <class '__main__.New'>
type(new)
# <class '__main__.New'>
issubclass(New, object)
# True
Klasy w starym stylu nie dziedziczą po object
. Wystąpienia w starym stylu są zawsze implementowane z wbudowanym typem instance
.
# old-style class
class Old:
pass
# old-style instance
old = Old()
old.__class__
# <class __main__.Old at ...>
type(old)
# <type 'instance'>
issubclass(Old, object)
# False
W Pythonie 3 klasy w starym stylu zostały usunięte.
Klasy nowego stylu w Pythonie 3 domyślnie dziedziczą po object
, więc nie trzeba już określać MyClass(object)
.
class MyClass:
pass
my_inst = MyClass()
type(my_inst)
# <class '__main__.MyClass'>
my_inst.__class__
# <class '__main__.MyClass'>
issubclass(MyClass, object)
# True
Domyślne wartości zmiennych instancji
Jeśli zmienna zawiera wartość niezmiennego typu (np. Ciąg znaków), można przypisać taką wartość domyślną jak ta
class Rectangle(object):
def __init__(self, width, height, color='blue'):
self.width = width
self.height = height
self.color = color
def area(self):
return self.width * self.height
# Create some instances of the class
default_rectangle = Rectangle(2, 3)
print(default_rectangle.color) # blue
red_rectangle = Rectangle(2, 3, 'red')
print(red_rectangle.color) # red
Należy zachować ostrożność podczas inicjowania obiektów modyfikowalnych, takich jak listy w konstruktorze. Rozważ następujący przykład:
class Rectangle2D(object):
def __init__(self, width, height, pos=[0,0], color='blue'):
self.width = width
self.height = height
self.pos = pos
self.color = color
r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [4, 0] r2's pos has changed as well
To zachowanie jest spowodowane faktem, że w Pythonie domyślne parametry są powiązane podczas wykonywania funkcji, a nie w deklaracji funkcji. Aby uzyskać domyślną zmienną instancji, która nie jest współużytkowana przez instancje, należy użyć takiej konstrukcji:
class Rectangle2D(object):
def __init__(self, width, height, pos=None, color='blue'):
self.width = width
self.height = height
self.pos = pos or [0, 0] # default value is [0, 0]
self.color = color
r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [0, 0] r2's pos hasn't changed
Zobacz także Zmienne argumenty domyślne i „Najmniejsze zdziwienie” oraz Zmienny argument domyślny .
Wielokrotne dziedziczenie
Python wykorzystuje algorytm linearyzacji C3 do określenia kolejności rozwiązywania atrybutów klas, w tym metod. Jest to znane jako Order Resolution Order (MRO).
Oto prosty przykład:
class Foo(object):
foo = 'attr foo of Foo'
class Bar(object):
foo = 'attr foo of Bar' # we won't see this.
bar = 'attr bar of Bar'
class FooBar(Foo, Bar):
foobar = 'attr foobar of FooBar'
Teraz, jeśli utworzymy instancję FooBar, jeśli sprawdzimy atrybut foo, zobaczymy, że atrybut Foo zostanie znaleziony jako pierwszy
fb = FooBar()
i
>>> fb.foo
'attr foo of Foo'
Oto MRO FooBar:
>>> FooBar.mro()
[<class '__main__.FooBar'>, <class '__main__.Foo'>, <class '__main__.Bar'>, <type 'object'>]
Można po prostu stwierdzić, że algorytm MRO Pythona jest
- Najpierw głębokość (np.
FooBar
a następnieFoo
), chyba że - wspólny rodzic (
object
) jest blokowany przez dziecko (Bar
) i - niedozwolone są relacje kołowe.
Oznacza to na przykład, że Bar nie może dziedziczyć po FooBar, podczas gdy FooBar dziedziczy po Bar.
Wyczerpujący przykład w Pythonie znajduje się na stronie wikipedia .
Kolejną potężną funkcją dziedziczenia jest super
. super może pobierać funkcje klas nadrzędnych.
class Foo(object):
def foo_method(self):
print "foo Method"
class Bar(object):
def bar_method(self):
print "bar Method"
class FooBar(Foo, Bar):
def foo_method(self):
super(FooBar, self).foo_method()
Wielokrotne dziedziczenie za pomocą metody init klasy, gdy każda klasa ma własną metodę init, wówczas próbujemy wielokrotnego dziedziczenia, a następnie wywoływana jest tylko metoda init klasy, która jest dziedziczona jako pierwsza.
na poniższym przykładzie tylko metoda klasy Foo startowych coraz zwaną klasę bar nie init, nazywane coraz
class Foo(object):
def __init__(self):
print "foo init"
class Bar(object):
def __init__(self):
print "bar init"
class FooBar(Foo, Bar):
def __init__(self):
print "foobar init"
super(FooBar, self).__init__()
a = FooBar()
Wynik:
foobar init
foo init
Ale to nie znaczy, że klasa Bar nie jest dziedziczona. Instancja końcowej klasy FooBar jest również instancją klasy Bar i klasy Foo .
print isinstance(a,FooBar)
print isinstance(a,Foo)
print isinstance(a,Bar)
Wynik:
True
True
True
Deskryptory i odnośniki przerywane
Deskryptory to obiekty, które są (zwykle) atrybutami klas i które mają jedną z metod specjalnych __get__
, __set__
lub __delete__
.
Deskryptory danych mają dowolne z __set__
lub __delete__
Mogą one kontrolować wyszukiwanie kropkowane w instancji i służą do implementacji funkcji, staticmethod
, classmethod
i property
. Kropkowane wyszukiwanie (np. Instancja foo
klasy Foo
patrząc na bar
atrybutów - tj. foo.bar
) wykorzystuje następujący algorytm:
bar
jest podniesiony w klasie,Foo
. Jeśli istnieje i jest deskryptorem danych , wówczas używany jest deskryptor danych. W ten sposóbproperty
może kontrolować dostęp do danych w instancji, a instancje nie mogą tego zastąpić. Jeśli nie ma deskryptora danych , tobar
jest sprawdzany w instancji__dict__
. Dlatego możemy przesłonić lub zablokować metody wywoływane z instancji za pomocą wyszukiwania przerywanego. Jeślibar
istnieje w instancji, jest on używany. Jeśli nie, to myspójrz w klasie
Foo
nabar
. Jeśli jest to deskryptor , używany jest protokół deskryptora. W ten sposób implementowane są funkcje (w tym kontekście metody niezwiązane),classmethod
istaticmethod
. W przeciwnym razie po prostu zwraca tam obiekt lub istniejeAttributeError
Metody klasowe: alternatywne inicjalizatory
Metody klas przedstawiają alternatywne sposoby budowania instancji klas. Aby to zilustrować, spójrzmy na przykład.
Załóżmy, że mamy stosunkowo prostą klasę Person
:
class Person(object):
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
self.full_name = first_name + " " + last_name
def greet(self):
print("Hello, my name is " + self.full_name + ".")
Przydałby się sposób budowania instancji tej klasy, określający pełną nazwę zamiast imienia i nazwiska osobno. Jednym ze sposobów, aby to zrobić, byłoby ustawienie parametru last_name
jako parametru opcjonalnego i przy założeniu, że jeśli nie zostanie podany, przekażemy pełną nazwę w:
class Person(object):
def __init__(self, first_name, age, last_name=None):
if last_name is None:
self.first_name, self.last_name = first_name.split(" ", 2)
else:
self.first_name = first_name
self.last_name = last_name
self.full_name = self.first_name + " " + self.last_name
self.age = age
def greet(self):
print("Hello, my name is " + self.full_name + ".")
Istnieją jednak dwa główne problemy z tym bitem kodu:
Parametry
first_name
ilast_name
są teraz mylące, ponieważ można wpisać pełną nazwęfirst_name
. Ponadto, jeśli istnieje więcej przypadków i / lub więcej parametrów, które mają tego rodzaju elastyczność, rozgałęzienie if / elif / else może szybko irytujące.Nie tak ważne, ale wciąż warte podkreślenia: co jeśli
last_name
toNone
, alefirst_name
nie dzieli się na dwie lub więcej rzeczy przez spacje? Mamy jeszcze jedną warstwę sprawdzania poprawności danych wejściowych i / lub obsługi wyjątków ...
Wprowadź metody klasowe. Zamiast jednego inicjatora utworzymy osobny inicjator o nazwie from_full_name
i from_full_name
go (wbudowanym) dekoratorem classmethod
.
class Person(object):
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
self.full_name = first_name + " " + last_name
@classmethod
def from_full_name(cls, name, age):
if " " not in name:
raise ValueError
first_name, last_name = name.split(" ", 2)
return cls(first_name, last_name, age)
def greet(self):
print("Hello, my name is " + self.full_name + ".")
Zwróć uwagę na cls
zamiast self
jako pierwszy argument from_full_name
. Metody klasowe są stosowane do całej klasy, a nie do wystąpienia danej klasy (co zwykle oznacza self
). Tak więc, jeśli cls
jest naszą Person
klasę, następnie zwracana wartość od from_full_name
metody klasy jest Person(first_name, last_name, age)
, który wykorzystuje Person
„s __init__
stworzyć instancję Person
klasie. W szczególności, jeśli byliśmy do podklasy Employee
of Person
, następnie from_full_name
będzie działać w Employee
klasy, jak również.
Aby pokazać, że działa to zgodnie z oczekiwaniami, utwórzmy instancje Person
na więcej niż jeden sposób bez rozgałęziania w __init__
:
In [2]: bob = Person("Bob", "Bobberson", 42)
In [3]: alice = Person.from_full_name("Alice Henderson", 31)
In [4]: bob.greet()
Hello, my name is Bob Bobberson.
In [5]: alice.greet()
Hello, my name is Alice Henderson.
Inne referencje:
https://docs.python.org/2/library/functions.html#classmethod
https://docs.python.org/3.5/library/functions.html#classmethod
Kompozycja klasowa
Kompozycja klas pozwala na wyraźne relacje między obiektami. W tym przykładzie ludzie mieszkają w miastach należących do krajów. Kompozycja umożliwia dostęp do liczby wszystkich osób mieszkających w ich kraju:
class Country(object):
def __init__(self):
self.cities=[]
def addCity(self,city):
self.cities.append(city)
class City(object):
def __init__(self, numPeople):
self.people = []
self.numPeople = numPeople
def addPerson(self, person):
self.people.append(person)
def join_country(self,country):
self.country = country
country.addCity(self)
for i in range(self.numPeople):
person(i).join_city(self)
class Person(object):
def __init__(self, ID):
self.ID=ID
def join_city(self, city):
self.city = city
city.addPerson(self)
def people_in_my_country(self):
x= sum([len(c.people) for c in self.city.country.cities])
return x
US=Country()
NYC=City(10).join_country(US)
SF=City(5).join_country(US)
print(US.cities[0].people[0].people_in_my_country())
# 15
Łatka Monkey
W tym przypadku „łatanie małp” oznacza dodawanie nowej zmiennej lub metody do klasy po jej zdefiniowaniu. Załóżmy na przykład, że zdefiniowaliśmy klasę A
jako
class A(object):
def __init__(self, num):
self.num = num
def __add__(self, other):
return A(self.num + other.num)
Ale teraz chcemy dodać kolejną funkcję w dalszej części kodu. Załóżmy, że ta funkcja jest następująca.
def get_num(self):
return self.num
Ale jak dodać to jako metodę w A
? To proste, po prostu zasadniczo umieszczamy tę funkcję w A
za pomocą instrukcji przypisania.
A.get_num = get_num
Dlaczego to działa? Ponieważ funkcje są obiektami jak każdy inny obiekt, a metody są funkcjami należącymi do klasy.
Funkcja get_num
powinna być dostępna dla wszystkich istniejących (już utworzonych), a także dla nowych instancji A
Te dodatki są dostępne we wszystkich instancjach tej klasy (lub jej podklas) automatycznie. Na przykład:
foo = A(42)
A.get_num = get_num
bar = A(6);
foo.get_num() # 42
bar.get_num() # 6
Zauważ, że w przeciwieństwie do niektórych innych języków, ta technika nie działa w przypadku niektórych wbudowanych typów i nie jest uważana za dobry styl.
Lista wszystkich członków klasy
Za pomocą funkcji dir()
można uzyskać listę członków klasy:
dir(Class)
Na przykład:
>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Często szuka się tylko członków „niemagicznych”. Można to zrobić za pomocą prostego zrozumienia, które zawiera listę członków o nazwach nie rozpoczynających się od __
:
>>> [m for m in dir(list) if not m.startswith('__')]
['append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Ostrzeżenia:
Klasy mogą definiować __dir__()
. Jeśli ta metoda istnieje, wywołanie dir()
wywoła __dir__()
, w przeciwnym razie Python spróbuje utworzyć listę członków klasy. Oznacza to, że funkcja dir może mieć nieoczekiwane wyniki. Dwa cytaty istotne z oficjalnej dokumentacji Pythona :
Jeśli obiekt nie udostępnia funkcji dir (), funkcja stara się zebrać informacje z atrybutu dict obiektu, jeśli jest zdefiniowany, oraz z obiektu typu. Wynikowa lista niekoniecznie jest kompletna i może być niedokładna, gdy obiekt ma niestandardową funkcję getattr ().
Uwaga: Ponieważ dir () jest dostarczany przede wszystkim jako wygoda do użycia w interaktywnym pytaniu, próbuje dostarczyć interesujący zestaw nazw więcej niż próbuje podać rygorystycznie lub konsekwentnie zdefiniowany zestaw nazw, a jego szczegółowe zachowanie może się zmieniać wydania. Na przykład atrybuty metaklasy nie znajdują się na liście wyników, gdy argumentem jest klasa.
Wprowadzenie do zajęć
Klasa funkcjonuje jako szablon, który określa podstawowe cechy określonego obiektu. Oto przykład:
class Person(object):
"""A simple class.""" # docstring
species = "Homo Sapiens" # class attribute
def __init__(self, name): # special method
"""This is the initializer. It's a special
method (see below).
"""
self.name = name # instance attribute
def __str__(self): # special method
"""This method is run when Python tries
to cast the object to a string. Return
this string when using print(), etc.
"""
return self.name
def rename(self, renamed): # regular method
"""Reassign and print the name attribute."""
self.name = renamed
print("Now my name is {}".format(self.name))
Patrząc na powyższy przykład, należy zwrócić uwagę na kilka rzeczy.
- Klasa składa się z atrybutów (danych) i metod (funkcji).
- Atrybuty i metody są po prostu zdefiniowane jako normalne zmienne i funkcje.
- Jak zauważono w odpowiednim
__init__()
metoda__init__()
jest nazywana inicjalizatorem . Jest to odpowiednik konstruktora w innych językach obiektowych i jest to metoda uruchamiana po raz pierwszy podczas tworzenia nowego obiektu lub nowej instancji klasy. - Atrybuty mające zastosowanie do całej klasy są definiowane jako pierwsze i nazywane są atrybutami klasy .
- Atrybuty mające zastosowanie do określonej instancji klasy (obiektu) są nazywane atrybutami instancji . Są one ogólnie zdefiniowane w
__init__()
; nie jest to konieczne, ale jest zalecane (ponieważ atrybuty zdefiniowane poza__init__()
narażają się na dostęp zanim zostaną zdefiniowane). - Każda metoda zawarta w definicji klasy przekazuje przedmiotowy obiekt jako swój pierwszy parametr. Słowo „
self
jest używane w tym parametrze (użycie „self
jest w zasadzie umowne, ponieważ słowo „self
nie ma w Pythonie żadnego znaczenia, ale jest to jedna z najbardziej szanowanych konwencji w Pythonie i zawsze należy go przestrzegać). - Osoby przyzwyczajone do programowania obiektowego w innych językach mogą być zaskoczone kilkoma rzeczami. Jednym z nich jest to, że Python nie ma prawdziwej koncepcji elementów
private
, więc wszystko domyślnie imituje zachowaniepublic
słowa kluczowego C ++ / Java. Aby uzyskać więcej informacji, zobacz przykład „Członkowie klasy prywatnej” na tej stronie. - Niektóre metody klasy mają następującą postać:
__functionname__(self, other_stuff)
. Wszystkie takie metody są nazywane „metodami magicznymi” i są ważną częścią klas w Pythonie. Na przykład przeciążanie operatorów w Pythonie jest realizowane za pomocą magicznych metod. Aby uzyskać więcej informacji, zapoznaj się z odpowiednią dokumentacją .
Zróbmy teraz kilka przykładów naszej klasy Person
!
>>> # Instances
>>> kelly = Person("Kelly")
>>> joseph = Person("Joseph")
>>> john_doe = Person("John Doe")
Obecnie mamy trzy Person
przedmiotów, kelly
, joseph
i john_doe
.
Możemy uzyskać dostęp do atrybutów klasy z każdej instancji za pomocą operatora kropki .
Zwróćmy uwagę na różnicę między atrybutami klasy i instancji:
>>> # Attributes
>>> kelly.species
'Homo Sapiens'
>>> john_doe.species
'Homo Sapiens'
>>> joseph.species
'Homo Sapiens'
>>> kelly.name
'Kelly'
>>> joseph.name
'Joseph'
Możemy wykonać metody klasy za pomocą tego samego operatora kropki .
:
>>> # Methods
>>> john_doe.__str__()
'John Doe'
>>> print(john_doe)
'John Doe'
>>> john_doe.rename("John")
'Now my name is John'
Nieruchomości
Klasy Python obsługują właściwości , które wyglądają jak zwykłe zmienne obiektowe, ale z możliwością dołączenia niestandardowego zachowania i dokumentacji.
class MyClass(object):
def __init__(self):
self._my_string = ""
@property
def string(self):
"""A profoundly important string."""
return self._my_string
@string.setter
def string(self, new_value):
assert isinstance(new_value, str), \
"Give me a string, not a %r!" % type(new_value)
self._my_string = new_value
@string.deleter
def x(self):
self._my_string = None
Obiekt klasy MyClass
wydaje się mieć właściwość .string
, jednak jego zachowanie jest teraz ściśle kontrolowane:
mc = MyClass()
mc.string = "String!"
print(mc.string)
del mc.string
Oprócz użytecznej składni, jak wyżej, składnia właściwości pozwala na walidację lub inne rozszerzenia, które można dodać do tych atrybutów. Może to być szczególnie przydatne w przypadku publicznych interfejsów API - w których poziom pomocy powinien zostać udzielony użytkownikowi.
Innym powszechnym zastosowaniem właściwości jest umożliwienie klasie prezentacji „atrybutów wirtualnych” - atrybutów, które nie są tak naprawdę przechowywane, ale są obliczane tylko na żądanie.
class Character(object):
def __init__(name, max_hp):
self._name = name
self._hp = max_hp
self._max_hp = max_hp
# Make hp read only by not providing a set method
@property
def hp(self):
return self._hp
# Make name read only by not providing a set method
@property
def name(self):
return self.name
def take_damage(self, damage):
self.hp -= damage
self.hp = 0 if self.hp <0 else self.hp
@property
def is_alive(self):
return self.hp != 0
@property
def is_wounded(self):
return self.hp < self.max_hp if self.hp > 0 else False
@property
def is_dead(self):
return not self.is_alive
bilbo = Character('Bilbo Baggins', 100)
bilbo.hp
# out : 100
bilbo.hp = 200
# out : AttributeError: can't set attribute
# hp attribute is read only.
bilbo.is_alive
# out : True
bilbo.is_wounded
# out : False
bilbo.is_dead
# out : False
bilbo.take_damage( 50 )
bilbo.hp
# out : 50
bilbo.is_alive
# out : True
bilbo.is_wounded
# out : True
bilbo.is_dead
# out : False
bilbo.take_damage( 50 )
bilbo.hp
# out : 0
bilbo.is_alive
# out : False
bilbo.is_wounded
# out : False
bilbo.is_dead
# out : True
Klasa Singleton
Singleton to wzorzec, który ogranicza tworzenie instancji klasy do jednej instancji / obiektu. Aby uzyskać więcej informacji na temat wzorców projektowych singletona w języku Python, zobacz tutaj .
class Singleton:
def __new__(cls):
try:
it = cls.__it__
except AttributeError:
it = cls.__it__ = object.__new__(cls)
return it
def __repr__(self):
return '<{}>'.format(self.__class__.__name__.upper())
def __eq__(self, other):
return other is self
Inną metodą jest udekorowanie swojej klasy. Postępując zgodnie z przykładem z tej odpowiedzi, stwórz klasę Singleton:
class Singleton:
"""
A non-thread-safe helper class to ease implementing singletons.
This should be used as a decorator -- not a metaclass -- to the
class that should be a singleton.
The decorated class can define one `__init__` function that
takes only the `self` argument. Other than that, there are
no restrictions that apply to the decorated class.
To get the singleton instance, use the `Instance` method. Trying
to use `__call__` will result in a `TypeError` being raised.
Limitations: The decorated class cannot be inherited from.
"""
def __init__(self, decorated):
self._decorated = decorated
def Instance(self):
"""
Returns the singleton instance. Upon its first call, it creates a
new instance of the decorated class and calls its `__init__` method.
On all subsequent calls, the already created instance is returned.
"""
try:
return self._instance
except AttributeError:
self._instance = self._decorated()
return self._instance
def __call__(self):
raise TypeError('Singletons must be accessed through `Instance()`.')
def __instancecheck__(self, inst):
return isinstance(inst, self._decorated)
Aby użyć, możesz użyć metody Instance
@Singleton
class Single:
def __init__(self):
self.name=None
self.val=0
def getName(self):
print(self.name)
x=Single.Instance()
y=Single.Instance()
x.name='I\'m single'
x.getName() # outputs I'm single
y.getName() # outputs I'm single