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

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

19.03.2007г.

Множествено наследяване (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 Person(object):
    def __init__(self, name): self.name = name
    def sayHi(self, someone): print "Hello %s, I'm %s" % (someone, self.name)

class NamedThing(object):
    def sayMyName(self): print "Beware, for I am %s" % self.name

popStarClass = type('PopStar', (Person,), {})
popStarClass.__bases__ = (Person, NamedThing)

def popStarSayHi(self, someone):
    super(popStarClass, self).sayHi(someone)
    print "Do you want my autograph?"

popStarClass.sayHi = popStarSayHi
mityo = popStarClass("Mityo the Python")
mityo.sayHi("Dim")
mityo.sayMyName()

Hello Dim, I'm Mityo the Python
Do you want my autograph?
Beware, for I am Mityo the Python

Равенство на обекти


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

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

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

Предефиниране на равенство (1)

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


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

    def __eq__(self, other):
        return isinstance(other, Person) and self.name == other.name \
                and self.age == other.age

mityo = Person("Mityo the Python", 30)
mityo2 = Person("Mityo the Python", 30)
mityo3 = Person("Mityo the Gun", 30)

print mityo == mityo2 # True
print mityo is mityo2 # False
print mityo == mityo3 # False

Предефиниране на равенство (2)

По подразбиране, __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

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)

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

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

>>> mityo = Person("Mityo the Python")
>>> mityo2 = Person("Mityo the Python")
>>> mityo == mityo2
True
>>> mityo != mityo2
True
>>> print "WTF?!"

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

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

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

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


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

>>> mityo = Person("Mityo the Python")
>>> dim = Persom("Dim")
>>> mityo > dim
True

Хеш функции

Можете да "задавате" хеш стойностите на вашите обекти дефинирайки метода __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

Атрибути (1)


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

Атрибути (2)

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

Атрибути (3)

__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

Атрибути (4)

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

Атрибути (5)

Класовете нов стил имат специален метод __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

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

Просто изброявате повече от един клас в скобите.

class A: pass
class B: pass
class C: pass
class D(A, B, C): pass

Малко по-късно за класическите проблеми, които произтичат от това