Обектно-ориентирано програмиране част втора
Абстракция
Енкапсулация
Модулярност
class Muffin:
def __init__(color, taste, calories):
self.color = color
self.taste = taste
self.calories = calories
>>> yum = Muffin("Green", "sweet", 420)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() takes 3 positional arguments but 4 were given
по конвенция така се кръщава първия аргумент на методите на клас
Трябва ли всеки метод на един клас да приема self като първи аргумент?
@staticmethod
@classmethod
първият метод ще е самият клас. Конвенцията е при такъв случай да кръщаваме аргумента cls
class
?class
e запазена дума>>> class Fennec:
... def __init__(self, name):
... self.name = name
...
>>> pesho = Fennec('Пешо')
>>> pesho.name
'Пешо'
>>> pesho.age
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Fennec' object has no attribute 'age'
>>> pesho.age = 2
>>> pesho.age
2
с ключовата дума del
можем да изтриваме атрибути на обект
>>> del pesho.age
>>> pesho.age
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Fennec' object has no attribute 'age'
>>> pesho.name
'Пешо'
>>> del pesho.name
>>> pesho.name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Fennec' object has no attribute 'name'
class Vector:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
v = Vector(3, 4, 5)
print(v.x)
Ако си мислим за математическата абстракция вектор, тя често пъти има смисъла и на наредена енторка.
Защо тогава да не можем да я третираме като такава?
v[0] == v.x
v[1] == v.y
v[2] == v.z
class Vector:
…
def __getitem__(self, i)
return (self.x, self.y, self.z)[i]
Нашия вектор може да бъде ползван като колекция
class Vector:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __getitem__(self, i):
return (self.x, self.y, self.z)[i]
def __str__(self):
return str((self.x, self.y, self.z))
def __len__(self):
return 3
def __add__(self, other):
return Vector(*map(sum, zip(self, other)))
Какво ще се случи тук?
v = Vector(3, 4, 5)
v[0] = 8
TypeError: 'Vector' object does not support item assignment
class Vector:
…
def __setitem__(self, index, value):
if index == 0:
self.x = value
elif index == 1:
self.y = value
elif index == 2:
self.z = value
else:
pass # тук можем да възбудим изключение
v = Vector(1, 2, 3)
v[1] = 10
print(v.y) # 10
getattr(obj, 'name')
е като obj.name
setattr(obj, 'name', value)
е като obj.name = value
delattr(obj, 'name')
е като del obj.name
class Spam: pass
spam = Spam()
spam.eggs = "Eggs"
print(getattr(spam, 'eggs')) # Eggs
setattr(spam, 'bacon', 'Spam, eggs and bacon')
print(spam.bacon) # Spam, eggs and bacon
Може да дефинирате __getitem__
и __setitem__
по-компактно
class Vector:
def __init__(self, x, y, z): ...
def __getitem__(self, i):
return getattr(self, ('x', 'y', 'z')[i])
def __setitem__(self, index, value)
return setattr(self, ('x', 'y', 'z')[i], value)
Може да предефинирате "оператора точка"
Може да предефинираме достъпването на атрибути на нашите обекти
__getattr__(self, name)
за object.name
__setattr__(self, name, value)
за object.name = 'Foo'
__delattr__(self, name)
за del object.name
__getattr__(self, name)
се извиква само ако обекта няма атрибут name
.
class Spam:
def __init__(self):
self.eggs = 'larodi'
def __getattr__(self, name)
return name.upper()
def answer(self)
return 42
spam = Spam()
print(spam.foo) # FOO
print(spam.bar) # BAR
print(spam.eggs) # larodi
print(spam.answer()) # 42
__setattr__ се извиква, когато присвоявате стойност на атрибут на обект.
За да не изпаднете в безкрайна рекурсия, ползвайте object.__setattr__
.
class Spam:
def __setattr__(self, name, value):
print("Setting {0} to {1}".format(name, value))
return object.__setattr__(self, name.upper(), value + 10)
spam = Spam()
spam.foo = 42
print(spam.FOO) # 52
print(spam.foo) # грешка!
__getattr__
се извиква само когато в обекта няма такъв атрибут.
__getattribute__
.(може да е tricky)Можем да разглеждаме всеки обект като съвкупност от две неща:
__dict__
на обекта)__class__
на обекта)class Spam: pass
spam = Spam()
spam.foo = 1
spam.bar = 2
print(spam.__dict__) # {'foo': 1, 'bar': 2}
print(spam.__class__) # <class '__main__.Spam'>
print(spam.__class__ is Spam) # True
Функциите и променливите дефинирани в тялото на класа са атрибути на класа.
class Spam:
def foo(self):
return 1
bar = 42
print(Spam.foo) # <function foo at 0x0c4f3b4b3>
print(Spam.bar) # 42
Когато срещне кода baba.attr
python прави следните неща:
baba.__dict__['attr']
baba.__class__.attr
baba.__getattr__('attr')
object
object.__getattribute__
Познайте дали имате причина?
class Person:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def name(self):
return self.first_name + " " + self.last_name
class Star(Person):
def greet_audience(self):
print("Hello Sofia, I am {0}!".format(self.name()))
david = Star("David", "Gahan")
david.greet_audience()
# Hello Sofia, I am David Gahan!
class Person:
def __init__(self, first_name, last_name):
self.first_name, self.last_name = first_name, last_name
def name(self):
return "{0} {1}".format(self.first_name, self.last_name)
class Japanese(Person):
def name(self):
return "{0} {1}".format(self.last_name, self.first_name)
print(Person("Edward", "Murdsone").name()) # Edward Murdstone
print(Japanese("Yukihiro", "Matsumoto").name()) # Matsumoto Yukihiro
class Person:
def __init__(self, first_name, last_name):
self.first_name, self.last_name = first_name, last_name
def name(self)
return "{0} {1}".format(self.first_name, self.last_name)
class Doctor(Person):
def name(self):
return "{0}, M.D.".format(Person.name(self))
print(Doctor("Gregory", "House").name()) # Gregory House, M.D.
class Spam:
def spam(self): return "spam"
class Eggs:
def eggs(self): return "eggs"
class CheeseShop(Spam, Eggs):
def food(self):
return self.spam() + " and " + self.eggs()
Има два главни случая в които е добра идея да използвате Миксини
Гледайте на миксините като резервни части които не можете да ползвате сами по себе си, но можете да сглобите нещо от тях
class Screen: # ...
class RadioTransmitter: # ...
class GSMTransmitter(RadioTransmitter): # ...
class Input: # ...
class MultiTouchInput(Input): # ...
class ButtonInput(Input): # ...
class MassStorage: # ...
class ProcessingUnit: # ...
class Phone(ProcessingUnit, Screen, GSMTransmitter,
MultiTouchInput, ButtonInput, MassStorage): # ...
class Tablet(ProcessingUnit, Screen, RadioTransmitter,
MultiTouchInput, MassStorage): # ...
class Serializable: # ...
class NetworkSupport: # ...
class Spam:
def __init__(self):
self.__var = 42
print(dir(Spam())) # ['_Spam__var', '__class__', ...]
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("John", 0, "Doe")
print(derived.report_base()) # Base: John 33
print(derived.report_derived()) # Derived: Doe 33
print(derived._Base__name, derived._Derived__name, sep=', ') # John, Doe
print(isinstance(3, int)) # True
print(isinstance(4.5, int)) # False
print(issubclass(int, object)) # True
print(issubclass(float, int)) # False