Обектно-ориентираното програмиране…
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
и str
dict
, 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.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__
. Но за това по-натам__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
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", "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
и issubclass
print(isinstance(3, int)) # True print(isinstance(4.5, int)) # False print(issubclass(int, object)) # True print(issubclass(float, int)) # False