Класове и обекти, част 2

„ Програмиране с Python“, ФМИ

map(lambda _: ' '.join(_), [('Стефан', 'Кънев'),
                            ('Николай', 'Бачийски'), 
                            ('Точо', '.', 'Точев'), 
                            ('Димитър', 'Димитров')])

2009.23230593607305938

Lecture('25.03.2009').previous().overview()

Обектно-ориентирано програмиране...

...в Python


class Vector:
    def __init__(self, x, y, z): self._coords = list(map(float, [x, y, z]))
    def length(self): return sum([_ ** 2 for _ in self._coords]) ** 0.5
    def dot(self, other):
        return Vector(self.y() * other.z() - self.z() * other.y(),
                      self.z() * other.x() - self.x() * other.z(),
                      self.x() * other.y() - self.y() * other.x())
    def normalize(self):
        self._coords = [_ / self.length() for _ in self._coords]

    def x(self): return self._coords[0]
    def y(self): return self._coords[1]
    def z(self): return self._coords[2]

x, y = Vector(0, 2.0, 0), Vector(3.0, 0, 0)
z = x.dot(y)
z.normalize()
print(z._coords)

...като начин на мислене

Protected и private


class Spam:
    def __stuff(self): pass

>>> dir(Spam)
['_Spam__stuff', '__doc__', '__module__', ...]

Protected и private (2)

class Base:
    def __init__(self, name, age):
        self.__name = name
        self._age = age
    def report_base(self):
        print("Base:", self.__name, self._age)

class Derived(Base):
    def __init__(self, name, age, derived_name):
        Base.__init__(self, name, age)
        self.__name = derived_name
        self._age = 33
    def report_derived(self):
        print("Derived:", self.__name, self._age)

>>> derived = Derived("Mityo the Python", 30, "Mityo the Gun")
>>> derived.report_base()
Base: Mityo the Python 33
>>> derived.report_derived()
Derived: Mityo the Gun 33
>>> print(derived._Base__name, ", ", derived._Derived__name, sep='')
Mityo the Python, Mityo the Gun

super

class A:
    def spam(self, sth): print("A's spam and {0}".format(sth))

class B(A):
    def spam(self, sth):
        print("B's spam and {0}".format(sth))
        super().spam(sth)    # еквивалентно на super(B, self).method(eggs)
        print("B's spam and {0} (again)".format(sth))

>>> B().spam("eggs")
B's spam and eggs
A's spam and eggs
B's spam and eggs (again)

>>> super(B, B()).spam("eggs")
A's spam and eggs

Множествено наследяване

Атрибутите се търсят в родителите по дълбочина (отляво надясно)

class A:
    def spam(self): print("A's spam")

class B(A): pass

class C:
    def spam(self): print("C's spam")

class D(B, C): pass

d = D()
d.spam() # A's spam

Множествено наследяване (2)

Всъщност в python използват "call-next-method"

class A:
    def spam(self): print("A's spam")

class B(A): pass

class C(A):
    def spam(self): print("C's spam")

class D(B, C): pass

d = D()
d.spam() # C's spam

super (2)

class A:
    def x(self):
        print('A')

class B(A):
    def x(self):
        super().x()
        print('B')

class C(A):
    def x(self):
        super().x()
        print('C')

class D(B,C):
    def x(self):
        super().x()
        print('D')

D().x()

isinstance и issubclass

isinstance(3, int) # True
isinstance(4.5, int) # False
issubclass(int, object) # True
issubclass(float, int) # False

Атрибути


class Spam: pass

>>> spam = Spam()
>>> spam.eggs = "Eggs"
>>> getattr(spam, 'eggs')
Eggs
>>> setattr(spam, 'bacon', 'Spam, eggs and bacon')
>>> spam.bacon
Spam, eggs and bacon
>>> delattr(spam, 'bacon')

Сравняване на обекти


>>> a = ['spam', 'eggs', 42]
>>> b = ['spam', 'eggs', 42]

>>> a is b
False
>>> a == b
True

>>> c = a
>>> a == c
True
>>> a is c
True

Сравняване на обекти (2)

Можете да предефинирате равенството за обекти от даден клас с метода__eq__


class Vector:
    def __init__(self, x, y, z): self._coords = map(float, [x, y, z])
    def __eq__(self, other):
        return all([a == b for a, b in zip(self._coords, other._coords)])

По подразбиране, __eq__ е имплементирана с is


class Food: pass

spam = Food()
eggs = Food()
more_spam = spam
print(spam == more_spam, spam is more_spam) # True True
print(spam == eggs, spam is eggs)           # False False

Сравняване на обекти (3)


class Vector:
    def __init__(self, x, y, z): self._coords = list(map(float, [x, y, z]))
    def __eq__(self, other):
        return all([a == b for a, b in zip(self._coords, other._coords)])

>>> a, b = Vector(1.0, 1.0, 1.0), Vector(1.0, 1.0, 1.0)
>>> print(a == b)
True
>>> print(a != b)
False

Сравняване на обекти (4)

Други методи за сравняване на обекти:

str и repr (1)


>>> print("Spam\nand\neggs")
Spam
and
eggs
>>> print(repr("Spam\nand\neggs"))
'Spam\nand\neggs'

str и repr (2)

Можете да дефинирате текстово представяне и репрезентация със "служебните" методи __str__ и __repr__.


class Person:
    ...
    def __repr__(self):
        return "Person({0!r}, {1!r})".format(self.name, self.age)

    def __str__(self):
        return self.name

>>> mityo = Person("Mityo the Python", 33)
>>> print(str(mityo))
Mityo the Python
>>> print(repr(mityo))
Person('Mityo the Python', 33)
>>> mityo
Person('Mityo the Python', 33)
>>> eval(repr(mityo))
Person('Mityo the Python', 33)

Хеш функции

Можете да "задавате" хеш стойностите на вашите обекти дефинирайки метода __hash__.

class Person:
    ...
    def __hash__(self):
        return len(self.name) + self.age

Можете да вземете хеша на даден обект с функцията hash().

>>> mityo = hash(Person("Mityo da Gun", 30))
42

__format__

Можете да задавате как да излиза обекта при формат.

class Spam:
    def __format__(self, string):
        return 'spam' + (' ' + string if string else '')

>>> print('{0}'.format(Spam()))
spam
>>> print('{0:a lot}'.format(Spam()))
spam a lot

Аритметични оператори (1)

Можете да предефинирате аритметичните оператори за вашите типове.

Аритметични оператори (2)


a, b, c = MagicNumber(3), MagicNumber(5), MagicNumber(7)

a = a + b # MagicNumber.__add__(a, b)
a += c    # MagicNumber.__iadd__(a, c)

Аритметични оператори (3)

Преобразуване до стандартни типове

Има методи, които може да предефинирате, за преобразования от вашия клас към стандартен тип:

Колекции

Python ви предлага и оператори, с които можете да третирате вашия клас като колекция:

Обекти, които могат да бъдат извиквани като функции

Можете да предефинирате оператора две скоби ().

class Stamp:
    def __init__(self, name): self.name = name
    def __call__(self, something):
        print("{0} was stamped by {1}".format(something, self.name))

>>> stamp = Stamp("The goverment")
>>> stamp("That thing there")
That thing there was stamped by The goverment

Още атрибути

Можете да предефинирате достъпа до атрибутите на вашите обекти с __getattr__, __setattr__ и __delattr__. Сигнатурите са следните:

Още атрибути (2)

__getattr__(self, name) се извиква само, ако обекта няма атрибут с име name.


class Spam:
    def __getattr__(self, name):
        print("Getting attribute: " + name)
        return None

spam = Spam()
spam.eggs = "Eggs"
print(spam.eggs)
print(spam.foo)


Eggs
Getting attribute: foo
None

Още атрибути (3)

__setattr__ се извиква, когато присвоявате стойност на атрибута на даден обект. За да не изпаднете в безкрайна рекурсия, ползвайте object.__setattr__


class Turtle(object):
    def __setattr__(self, name, value):
        print("Setting attribute: " + name)
        object.__setattr__(self, name, value)

turtle = Turtle()
turtle.spam = "Spam" # prints 'Setting attribute: spam'

Още атрибути (4)

Класовете имат специален метод __getattribute__, който работи подобно на __getattr__, с тази разлика че се извиква винаги, без значение дали обекта има такъв атрибут или не. Отново, за да не изпаднете в бездънна рекурсия, ползвайте object.__getattribute__.

class Crab(object):
    def __getattribute__(self, name):
        print("Getting attribute: " + name)
        return object.__getattribute__(self, name)
        

crab = Crab()
crab.spam = "Spam"
crab.eggs = "Eggs"
print(crab.spam)
print(crab.eggs)



Getting attribute: spam
Spam
Getting attribute: eggs
Eggs