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 :

Python 3.x 3.0
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__ .

Python 2.x 2.3
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.

Python 3.x 3.0
import inspect

inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False
Python 2.x 2.3
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:

Klasy w nowym stylu a w starym stylu

Python 2.x 2.2.0

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
Python 3.x 3.0.0

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

  1. Najpierw głębokość (np. FooBar a następnie Foo ), chyba że
  2. wspólny rodzic ( object ) jest blokowany przez dziecko ( Bar ) i
  3. 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:

  1. bar jest podniesiony w klasie, Foo . Jeśli istnieje i jest deskryptorem danych , wówczas używany jest deskryptor danych. W ten sposób property może kontrolować dostęp do danych w instancji, a instancje nie mogą tego zastąpić. Jeśli nie ma deskryptora danych , to

  2. bar jest sprawdzany w instancji __dict__ . Dlatego możemy przesłonić lub zablokować metody wywoływane z instancji za pomocą wyszukiwania przerywanego. Jeśli bar istnieje w instancji, jest on używany. Jeśli nie, to my

  3. spójrz w klasie Foo na bar . 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 i staticmethod . W przeciwnym razie po prostu zwraca tam obiekt lub istnieje AttributeError

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:

  1. Parametry first_name i last_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.

  2. Nie tak ważne, ale wciąż warte podkreślenia: co jeśli last_name to None , ale first_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:

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.

  1. Klasa składa się z atrybutów (danych) i metod (funkcji).
  2. Atrybuty i metody są po prostu zdefiniowane jako normalne zmienne i funkcje.
  3. 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.
  4. Atrybuty mające zastosowanie do całej klasy są definiowane jako pierwsze i nazywane są atrybutami klasy .
  5. 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).
  6. 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ć).
  7. 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 zachowanie public słowa kluczowego C ++ / Java. Aby uzyskać więcej informacji, zobacz przykład „Członkowie klasy prywatnej” na tej stronie.
  8. 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


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow