Обектно-орентирано програмиране, част 1

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

25.03.2009г.

Класовете, обектите и питоните

Основи на класовете (1)


class Vector:
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    def coords(self):
        return [self.x, self.y, self.z]

    def length(self):
        return sum(c ** 2 for c in self.coords()) ** 0.5

>>> v = Vector(1.0, 2.0, 3.0)
>>> print(v.coords())
[1.0, 2.0, 3.0]
>>> print(v.length())
3.74165738677

Основи на класовете (2)

Полета (1)


class Spam:
    def __init__(self, arg):
        self.stored = arg

>>> spam = Spam(42)
>>> print(spam.stored)
42
>>> spam.stored = 60
>>> print(spam.stored)
60
>>> spam.foo = 10
>>> print(spam.foo)
10

Полета (2)

По този начин може да използвате класовете като структури:

class Student: pass

mityo = Student()
mityo.name = "Mityo the Python"
mityo.age = 22
mityo.faculty_number = 42424

Методи


class Person:
    def __init__(self, name):
        self.name = name

    def greet(self, somebody):
        print("Hello {0}, I'm {1}!".format(somebody, self.name))

>>> mityo = Person("Mityo the Python")
>>> mityo.greet('Stefan')
Hello Stefan, I'm Mityo the Python!
>>> Person.greet(mityo, 'Stefan')
Hello Stefan, I'm Mityo the Python!

Методи (2)

Интересен страничен (или не толкова страничен) ефект е следното:

>>> person = Person("Mityo the Python")
>>> greet_someone = person.greet
>>> greet_someone("Stefan")
Hello Stefan, I'm Mityo the Python!

Обаче:

>>> greeter = Person.greet
>>> greeter(mityo, "Stefan")
Hello Stefan, I'm Mityo the Python!

Статични методи

При статичните методи положението е малко странно:

class Person:
    people = []
    def register(name):
        Person.people.append(name)
        print(len(Person.people), "people are registered now")
    register = staticmethod(register)

>>> Person.register("Mityo the Python")
1 people are registered now
>>> Person.register("Pooh")
2 people are registered now

Има по-хубав синтаксис, но ще ви го покажем, като говорим за декоратори.

Класови методи

В Python има "класови" методи, които вземат класът на който са извикани като първи аргумент. Понякога е полезно при наследяване:

class Something:
    def greet(cls, someone):
        print(someone, "was greeted from", cls)
    greet = classmethod(greet)

>>> Something.greet("Mityo")
Mityo was greeted from <class '__main__.Something'>

Конструкция


class Person:
    def __init__(self, name, age = 20, location = "Sofia"):
        self.name = name
        self.age = age
        self.location = location

>>> person = Person("Mityo the Python", 30, "Great Tarnovo")

Преговор до момента


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)

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


>>> 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)

__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)

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

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

Колекции

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 government")
>>> stamp("That thing there")
That thing there was stamped by The government

Още въпроси?