self в Python: от основ до продвинутых концепций
В объектно-ориентированном программировании на Python ключевое слово self играет фундаментальную роль. Оно используется для доступа к атрибутам и методам экземпляра класса. Понимание self необходимо для написания корректных и эффективных программ.
Основное понимание self
self - это первый параметр любого метода экземпляра. При вызове метода Python автоматически передаёт экземпляр объекта в качестве первого аргумента. Традиционно этот параметр называют self, хотя можно использовать любое имя, но соглашение обязывает использовать self для читаемости.
class MyClass:
def __init__(self, value):
self.value = value
def show(self):
print(self.value)
obj = MyClass(10)
obj.show() # выведет 10атрибуты класса python (атрибуты классов и объектов в python)
При вызове obj.show() Python преобразует это в MyClass.show(obj). self указывает на сам объект. Без self метод не сможет обратиться к атрибутам экземпляра.
Как обратиться к атрибутам и методам экземпляра через self?
Через self можно читать и изменять атрибуты, а также вызывать другие методы экземпляра. Например:
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
def reset(self):
self.count = 0библиотека классов python (библиотека классов в python)
Методы increment и reset работают с атрибутом count через self.
В чем различие между self и cls?
self относится к экземпляру класса, а cls - к самому классу. Методы класса (classmethod) принимают cls в качестве первого параметра. Пример:
class Person:
species = 'Homo sapiens'
def __init__(self, name):
self.name = name
@classmethod
def get_species(cls):
return cls.species
print(Person.get_species()) # Homo sapiens
метод объекта python (методы объектов в python)
Здесь self используется в __init__, а cls - в методе класса get_species.
Как создать метод, не требующий доступа к экземпляру?
Статические методы (staticmethod) не имеют self или cls. Они ведут себя как обычные функции, но принадлежат классу.
class MathUtils:
@staticmethod
def add(a, b):
return a + b
print(MathUtils.add(3, 4)) # 7Python структура объекта (структура объекта в python)
Статические методы полезны для вспомогательных функций, логически связанных с классом.
Как self работает в декораторе property?
self используется в геттерах, сеттерах и удаляторах свойства (property) для доступа к атрибутам экземпляра. Пример:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError('Radius cannot be negative')
self._radius = value
Здесь self в геттере и сеттере обращается к защищённому атрибуту _radius.
Типичные ошибки и их решение
Ошибка: отсутствие self в определении метода
Пример: def method(): pass. При вызове obj.method() Python выдаст TypeError: method() takes 0 positional arguments but 1 was given. Решение: добавить self как первый параметр: def method(self): pass.
Ошибка: обращение к self внутри статического метода
Статический метод не получает self. Если внутри него попытаться использовать self, возникнет NameError (или обращение к глобальному self, если такой определён). Решение: если нужен доступ к экземпляру, используйте обычный метод или метод класса.
Ошибка: неправильное именование self
Можно назвать параметр по-другому, но это нарушает соглашения и ухудшает читаемость. Лучше всегда использовать self.
Ошибка: использование self вместо cls в методе класса
Если метод украшен @classmethod, первый параметр должен называться cls, а не self. Использование self приведёт к путанице, хотя формально код может работать (если не используется внутри). Рекомендуется соблюдать соглашения.
Расширенные примеры использования self
Пример 1: self в __init__ с аргументами по умолчанию
class Book:
def __init__(self, title, author='Unknown'):
self.title = title
self.author = author
self._pages = 0
def add_pages(self, n):
self._pages += n
b = Book('1984', 'Orwell')
print(b.title, b.author) # 1984 Orwell
b.add_pages(328)
print(b._pages) # 328
1984 Orwell 328
Пример 2: self в __call__ для создания вызываемых объектов
class Multiplier:
def __init__(self, factor):
self.factor = factor
def __call__(self, x):
return x * self.factor
double = Multiplier(2)
triple = Multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
10 15
Пример 3: self в __enter__ и __exit__ для контекстного менеджера
class ManagedFile:
def __init__(self, filename, mode='r'):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
# Использование
with ManagedFile('test.txt', 'w') as f:
f.write('Hello')
# Файл автоматически закрыт
Пример 4: self с __slots__ для ограничения атрибутов
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
def distance_from_origin(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
p = Point(3, 4)
print(p.distance_from_origin()) # 5.0
# p.z = 5 # AttributeError: 'Point' object has no attribute 'z'
5.0
Пример 5: self в множественном наследовании с super()
class A:
def __init__(self):
self.a = 1
class B:
def __init__(self):
self.b = 2
class C(A, B):
def __init__(self):
super().__init__() # вызовет A.__init__
self.c = 3
obj = C()
print(obj.a, obj.c) # 1 3
# Чтобы вызвать B.__init__, нужно явно: B.__init__(self)
1 3
Пример 6: самодельный декоратор свойства через self
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
def get_fahrenheit(self):
return self._celsius * 9/5 + 32
def set_fahrenheit(self, value):
self._celsius = (value - 32) * 5/9
# Эмуляция property без декоратора
fahrenheit = property(get_fahrenheit, set_fahrenheit)
t = Temperature(100)
print(t.fahrenheit) # 212.0
t.fahrenheit = 32
print(t._celsius) # 0.0
212.0 0.0
Пример 7: self в методах сравнения (__eq__, __lt__)
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
if isinstance(other, Person):
return self.name == other.name and self.age == other.age
return False
def __lt__(self, other):
if isinstance(other, Person):
return self.age < other.age
return NotImplemented
alice = Person('Alice', 30)
bob = Person('Bob', 25)
print(alice == bob) # False
print(alice < bob) # False (30 не меньше 25)
False False