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

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

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

09.04.2008г.

Lecture('09.04.2008').previous().overview()

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

...в Python


class Vector(object):
    def __init__(self, x, y, z): self._coords = 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

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

Класове в нов стил

Класове в нов стил — __slots__


class Something(object):
    __slots__ = ['spam', 'eggs']

>>> smth = Something()
>>> smth.spam = 1
>>> smth.eggs = 2
>>> smth.foo = 3

Traceback (most recent call last):
  File "<stdin>", line 5, in <module>
AttributeError: 'Something' object has no attribute 'foo'

Класове в нов стил — __new__

Обикновенно се налага, когато наследявате immutable стандартни класове


  class SpamTuple(tuple):
      def __new__(cls, number = 1):
          result = ("Spam",) * number
          return tuple.__new__(cls, result)

      def countSpam(self):
          return len(self)

  spams = SpamTuple(3)
  print spams             # ('Spam', 'Spam', 'Spam')
  print spams.countSpam() # 3

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

При класове от стар стил, атрибутите се търсят в родителите по дълбочина

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() # A's spam

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

При класове от нов стил, атрибутите се търсят в родителите по широчина

class A(object):
    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

Атрибути


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(object):
    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(object): pass
spam = Food()
eggs = Food()
moreSpam = spam
print spam == moreSpam, spam is moreSpam # True True
print spam == eggs, spam is eggs # False False

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


class Vector(object):
    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)])

>>> a, b = Vector(1.0, 1.0, 1.0), Vector(1.0, 1.0, 1.0)
>>> print a == b
True
>>> print a != b
True
>>> print "WTF?!"

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

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

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

Можете да предефинирате всичките шест оператора __cmp__(self, other). Този метод трябва да сравнява self и other и да връща:

class Vector(object):
    ...
    def __cmp__(self, other):
        if self.length() < other.length(): return -1
        elif self.length() == other.length(): return 0
        else: return 1

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(object):
    ...
    def __repr__(self):
        return "Person(%s, %s)" % (repr(self.name), repr(self.age))

    def __str__(self):
        return self.name

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

Хеш функции

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

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

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

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

Аритметични оператори (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(object):
    def __init__(self, name): self.name = name
    def __call__(self, something):
        print "%s was stamped by %s" % (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(object):
    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