Обектно-ориентираното програмиране…
class Vector:
pass
class Vector:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
spam = Vector(1.0, 2.0, 3.0)
print(spam.x)
()
class Vector:
def __init__(self, x, y, z): ...
def length(self):
return (self.x * self.x + self.y * self.y + self.z * self.z) ** 0.5
spam = Vector(1.0, 2.0, 3.0)
print(spam.length())
selfобект.име_на_метод()
class Vector:
def __init__(self, x, y, z): ...
def _coords(self):
return (self.x, self.y, self.z)
def length(self):
return sum(_ ** 2 for _ in self._coords()) ** 0.5
_coords е protected методself_ е валидно име за променливаv1 = Vector(1.0, 2.0, 3.0) v2 = Vector(4.0, 5.0, 6.0) v3 = Vector(7.0, 8.0, 9.0) print(Vector.length(v1)) print(Vector.length(v2)) print(map(Vector.length, [v1, v2, v3]))
class Vector:
def __init__(self, x, y, z): ...
def length(self): ...
def normalize(self):
length = self.length()
self.x /= length
self.y /= length
self.z /= length
class Vector:
def __init__(self, x, y, z): ...
def length(self): ...
def normalized(self):
return Vector(self.x / self.length(),
self.y / self.length(), self.z / self.length())
normalize vs normalized
class Vector:
def normalize(self):
length = self.length()
self.x /= length
self.y /= length
self.z /= length
def normalized(self):
return Vector(self.x / self.length(), self.y / self.length(), self.z / self.length())
Ако имате само едно от двете, кое предпочитате?
(верен отговор по-късно)
tuple, int и strdict, list и set.Искаме да пишем на всички активни потребители.
emails = []
for user in User.all():
if user.active():
emails.append(user.email)
# или
emails = [user.email for user in User.all() if user.active()]
Кое и защо?
class Vector:
def __init__(self, x, y, z): ...
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
spam = Vector(1.0, 2.0, 3.0)
eggs = Vector(4.0, 5.0, 6.0)
breakfast = spam + eggs
self + other се предефинира с __add__(self, other)
class Vector:
def __init__(self, x, y, z): ...
def _coords(self): ...
def __add__(self, other):
return Vector(*map(sum, zip(self._coords(), other._coords())))
spam = Vector(1.0, 2.0, 3.0)
eggs = Vector(4.0, 5.0, 6.0)
breakfast = spam + eggs
По-хакерско, но спорно дали по-четимо
class Vector:
def __init__(self, x, y, z): ...
def _coords(self): ...
def addition(a, b):
return Vector(a.x + b.x, a.y + b.y, a.z + b.z)
Vector.__add__ = addition
print(Vector(1.0, 2.0, 3.0) + Vector(4.0, 5.0, 6.0))
self е явенАко искате да достъпвате компонентите на вектора с v[0], v[1] и v[2]:
class Vector:
def __init__(self, x, y, z): ...
def __getitem__(self, i):
return (self.x, self.y, self.z)[i]
Можете да направите вектора да се държи като колекция:
class Vector:
def __init__(self, x, y, z): ...
def __getitem__(self, i):
return (self.x, self.y, self.z)[i]
def __len__(self):
return 3
def length(self):
return sum(_ ** 2 for _ in self) ** 0.5
def __add__(self, other):
return Vector(*map(sum, zip(self, other)))
Може и да имплементирате присвояване на индекс:
class Vector:
def __init__(self, x, y, z): ...
def __getitem__(self, i): ...
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
Разбира се, по-добре вектора да е immutable.
getattr(obj, 'name') е като obj.namesetattr(obj, 'name', value) е като obj.name = valuedelattr(obj, 'name') е като del obj.nameclass 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__. Но за това по-натам__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
Когато извикате object.attr:
object.__dict__['attr']object.__class__, ако това е функция, се връща специален обект (bound method), на който може да извикате ().object.__class__ не е функция, то просто се връщаobject.__getattr__('attr')
class Spam:
answer = 42
def __init__(self, x):
self.x = x
def add(self, y):
return self.x * y
spam = Spam(6)
print(spam.add) # <bound method Spam.add of <__main__.Spam object at 0x0d34db33f>>
print(spam.add(9)) # 54
print(spam.answer) # 42
objectobject.__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", "Gaham")
david.greet_audience()
# Hello Sofia, I am David Gaham!
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()
Методи се търсят в широчина (breath-first):
class A:
def spam(self): return "A.spam"
class B(A):
pass
class C(A):
def spam(self): return "C.spam"
class D(B, C):
pass
print(D().spam()) # C.spam
Да, в широчина:
class A:
def spam(self): return "A.spam"
class B(A):
def spam(self): return "B.spam"
class C(A):
def spam(self): return "C.spam"
class D(B, C):
pass
print(D().spam()) # B.spam
Ако изпаднете в диамантено наследяване, имате проблем. Обикновено първопричината не е в кода.
(но има и изключения)
_име са protected__име са private__име до _клас__име. Нарича се name mangling и създава ефект, подобен на този в C++/Java.
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
isinstance и issubclassprint(isinstance(3, int)) # True print(isinstance(4.5, int)) # False print(issubclass(int, object)) # True print(issubclass(float, int)) # False