събота, 17 януари 2009 г.

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


„ Програмиране с 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

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

  • Можете да проверите дали два обекта са равни по стойност с оператора ==
  • Можете да проверите дали две имена сочат към един и същи обект с оператора is

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

  • В Python от всеки обект могат да се получат две важни неща - текстов низ (str) и "репрезентация" (repr)
  • Текстовото представяне се използва при str(обект) или при "%s" % обект
  • Репрезентацията представлява текст, който ако се изпълни като Python код ще създаде обекта на който е извикан - т.е. eval(repr(обект)) == обект
  • Репрезентация се извежда с repr(обект).

>>> 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?!"
  • __eq__ не предефинира !=
  • За целта трябва да дефинирате метода __ne__

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

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

  • __lt__(self, other) # self <>
  • __le__(self, other) # self <= other
  • __gt__(self, other) # self > other
  • __ge__(self, other) # self >= other

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

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

  • Отрицателно число, ако self <>.
  • Нула, ако 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)

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

  • __add__(self, other) за self + other
  • __sub__(self, other) за self - other
  • __mul__(self, other) за self * other
  • __div__(self, other) за self / other
  • __floordiv__(self, other) за self // other
  • __mod__(self, other) за self % other
  • __lshift__(self, other) за self <<>
  • __rshift__(self, other) за self >> other
  • __and__(self, other) за self & other
  • __xor__(self, other) за self ^ other
  • __or__(self, other) за self | other

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

  • Всеки оператор има вариантен метод за прилагане на операцията "на място" - например +=, /= и т.н.
  • Метода има същото име, но със i след двете подчертавки - __add__ става __iadd__
  • Хубаво е когато дефинирате такива методи, те да променят self и да връщат self

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

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

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

  • Аритметичните оператори имат още един вариант - когато обекта е от дясната страна на операцията
  • Те започват с r, т.e. "десния" вариант на __add__ е __radd__
  • При a + b, ще се извика b.__radd__ само ако а не дефинира __add__, а b дефинира __radd__.
  • Ако b е от тип, наследник на a, то Python ще опита да извика b.__radd__ преди да пробва с a.__add__. По този начин наследници могат да предефинират аритметични операции

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

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

  • __int__(self) за int(обект).
  • __long__(self) за long(обект).
  • __float__(self) за float(обект).
  • __complex__(self) за complex(обект).
  • __nonzero__(self) за bool(обект).

Колекции

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

  • __len__(self) за len(обект).
  • __getitem__(self, key) за обект[key].
  • __setitem__(self, key, value) за обект[key] == value
  • __delitem__(self, key) за del обект[key].
  • __contains__(self, item) за item in обект.

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

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

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)

  • getattr(обект, име_на_атрубит) връща обект.атрибут, където атрибут.
  • setattr(обект, име_на_атрибут, стойност) присвоява стойност на обект.атрибут.
  • delattr(обект, име_на_атрибут) работи като del обект.атрибут.

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__. Сигнатурите са следните:

  • __getattr__(self, name) за something = object.name
  • __setattr__(self, name, value) за object.name = "Foo"
  • __delattr__(self, name) за del object.name

Атрибути (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

Няма коментари:

Публикуване на коментар