elements = (1, 'larodi', 42),
for element in elements:
print(element)
(1, 'larodi', 42)
{x+y for x in range(3) for y in range(3)}
{0, 1, 2, 3, 4}
keys = [1, 2, 3]
keys.append([4, 5, 6])
{key: "Panda" for key in keys}
TypeError: unhashable type: 'list'
len(x ** 2 for x in range(5))
TypeError: object of type 'generator' has no len()
{1, 2, 3, 3, 3, 4, 5}.count(3)
AttributeError: 'set' object has no attribute 'count'
Обектно-ориентирано програмиране
Разбирайте „за програмирането генерално“
Най-общо казано(демек, според википедия):
Abstraction in its main sense is a conceptual process by which general rules and concepts are derived from the usage and classification of specific examples, literal ("real" or "concrete") signifiers, first principles, or other methods. "An abstraction" is the product of this process—a concept that acts as a super-categorical noun for all subordinate concepts, and connects any related concepts as a group, field, or category.
А по-конкретно за областта, която ни интересува
[…] a technique for managing complexity of computer systems. It works by establishing a level of complexity on which a person interacts with the system, suppressing the more complex details below the current level. The programmer works with an idealized interface (usually well defined) and can add additional levels of functionality that would otherwise be too complex to handle.
Encapsulation is the packing of data and functions into a single component.
Има много механизми, с които можем да го реализираме, но обектно ориентираното програмиране е един от най-популярните
Abstraction and encapsulation are complementary concepts: abstraction focuses on the observable behavior of an object... encapsulation focuses upon the implementation that gives rise to this behavior... encapsulation is most often achieved through information hiding, which is the process of hiding all of the secrets of object that do not contribute to its essential characteristics.
Механизъм за организиране на сходна логика, работеща върху свързани видове данни, обработваща сходни видове процеси от моделирания свят, в добре обособени и ясно разделени парчета от кода ни
Разбирайте „конкретно за python“
Последните две с някои малки уговорки, които обаче рядко ще ви интересуват
Съвсем буквално
>>> type(42)
<class 'int'>
>>> type([])
<class 'list'>
>>> type([]) is list
True
>>> type(list)
<class 'type'>
>>> type(list) is type
True
>>> type(type)
???
<class 'type'>
Създаваме класове с ключовата дума class
, след което всяка функция дефинирана в тялото на класа е метод, а всяка променлива е клас променлива
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
spam = Vector(1.0, 1.0)
print(spam.x)
__init__
, той не връща стойностself
, иначе губите огромни количества точки/колегите ви ви мразят/никой не иска да си играе с вас в пясъчника__init__
методът му и като резултат получаваме новоконструирания обект„Конструктор“ е думата, с която сте свикнали, но в случая далеч по-подходяща е „инициализатор“, както си личи от името
Въпреки, че при инстанциране на обект подаваме аргументите, които очаква инициализаторът, той не е първият метод, който се извиква в този момент, но за сега не влагайте много мисъл в това, просто го имайте предвид
import math
class Vector:
def __init__(self, x, y): ...
def length(self):
return math.sqrt(self.x**2 + self.y**2)
spam = Vector(1.0, 2.0)
print(spam.length())
self
, няма никакви магически имплицитни scope-овеобект.име_на_метод()
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
Казахме, че класовете са отворени. Това ще рече, че private и protected концепциите не са това, за което сте свикнали да мислите в езици като C++/Java/C#
Ограниченията за използване на защитени и частни методи в класовете в python са отговорност на програмиста, което по никакъв начин не прави живота ви по-труден
Методи/атрибути започващи с _ са защитени, т.е. би следвало да се ползват само от методи на класа и наследяващи го класове
Методи/атрибути започващи с __ са частни, т.е. би следвало да се ползват само от методи на класа
Достатъчно очевидно е, а в някои много редки случаи може да се наложи тези ограничения да не се спазят
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(list(map(Vector.length, [v1, v2, v3])))
Mutable vs. Immutable
Най-общо повечето обекти в python са mutable, доколкото езикът не ни забранява да ги променяме
Какво в python знаем, че е immutable?
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):
length = self.length()
return Vector(self.x / length,
self.y / length, self.z / length)
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())
class Vector:
def __init__(self, x, y, z):
self._coords = (x, y, z)
def __eq__(self, other):
return self._coords == other._coords
Известни още като "magic methods", dunder(double under) методите в python най-често предефинират някакъв аспект от поведението на обектите ни
__add__(self, other)
- self + other
__sub__(self, other)
- self - other
__mul__(self, other)
- self * other
__truediv__(self, other)
- self / other
__floordiv__(self, other)
- self // other
__mod__(self, other)
- self % other
__lshift__(self, other)
- self << other
__rshift__(self, other)
- self >> other
__and__(self, other)
- self & other
__xor__(self, other)
- self ^ other
__or__(self, other)
- self | other
__int__(self)
- int(обект)
__float__(self)
- float(обект)
__complex__(self)
- complex(обект)
__bool__(self)
- bool(обект)
Можете да дефинирате поведение на обектите си, когато биват извиквани като функции
class Stamp:
def __init__(self, name):
self.name = name
def __call__(self, something):
print("{0} was stamped by {1}".format(something, self.name))
>>> stamp = Stamp("The government")
>>> stamp("That thing there")
That thing there was stamped by The government
Можем да достъпваме/променяме атрибути на обект динамично
>>> v1 = Vector(1, 1, 1)
>>> getattr(v1, 'y')
1
>>> setattr(v1, 'z', 5)
>>> getattr(v1, 'z')
5
Можете да дефинирате методи, така че те да са статични - т.е. да нямат нужда от инстанция
class GoatSimulator:
goats = []
@staticmethod
def register(name):
GoatSimulator.goats.append(name)
print(len(GoatSimulator.goats), " goats are registered now")
>>> GoatSimulator.register("Pip the Happy Goat")
1 goats are registered now
>>> GoatSimulator.register("George the Gutsy Goat")
2 goats are registered now
Можете да използвате и @classmethod за да получите класа, от който е извикан методът като първи аргумент
class Countable:
_count = 0
def __init__(self, data):
self.data = data
type(self).increase_count()
@classmethod
def increase_count(cls):
cls._count += 1
@classmethod
def decrease_count(cls):
cls._count -= 1
Когато обект от даден тип бъде събран от garbage collector-а, се извиква неговият __del__
метод.
Той не прави това, което прави деструкторът в C++, а неща от по-високо ниво, например затваря мрежови връзки или файлове, изпраща съобщения до някъде, че нещо е приключило и прочее.
class Countable:
_count = 0
def __init__(self, data):
self.data = data
type(self).increase_count()
@classmethod
def increase_count(cls):
cls._count += 1
@classmethod
def decrease_count(cls):
cls._count -= 1
def __del__(self):
type(self).decrease_count()