Вызвать атрибут метода python

Магические методы __setattr__, __getattribute__, __getattr__ и __delattr__

На этом занятии мы поговорим о работе с атрибутами класса и его экземплярами. Я напомню, что класс можно воспринимать как некое пространство имен, в котором записаны свойства и методы. Например, если вернуться к классу Point (представления точки на плоскости):

class Point: MAX_COORD = 100 MIN_COORD = 0 def __init__(self, x, y): self.x = x self.y = y def set_coord(self, x, y): self.x = x self.y = y

то здесь мы видим определение четырех атрибутов: двух свойств MAX_COORD и MIN_COORD и двух методов __init__ и set_coord. Это атрибуты класса и при создании экземпляров:

pt1 = Point(1, 2) pt2 = Point(10, 20)

Эти атрибуты остаются в пространстве имен класса, не копируются в экземпляры. Но из экземпляров мы можем совершенно спокойно к ним обращаться, так как пространство имен объектов содержит ссылку на внешнее пространство имен класса. Если какой-либо атрибут не существует в экземпляре, то поиск переходит во внешнее пространство, то есть, в класс и поиск продолжается там. Поэтому мы совершенно спокойно можем через экземпляр обратиться к свойству класса MAX_COORD:

И получается, что атрибуты и методы класса – это общие данные для всех его экземпляров.

Далее, когда мы обращаемся к атрибутам класса внутри методов, объявленных в этом классе, то должны не просто прописать их имена:

def set_coord(self, x, y): if MIN_COORD  x  MAX_COORD: self.x = x self.y = y

а явно указать перед ними ссылку на класс, то есть, на пространство имен. Либо так:

if Point.MIN_COORD  x  Point.MAX_COORD:
if self.MIN_COORD  x  self.MAX_COORD:

Здесь self – это ссылка на экземпляр класса, из которого метод вызывается, поэтому мы можем через этот параметр обращаться к атрибутам класса.

Обо всем этом мы с вами уже говорили, я лишь еще раз повторил эти важные моменты. А теперь один нюанс, о который спотыкаются многие начинающие программисты. Давайте предположим, что нам нужен метод, который бы изменял значение атрибута класса MIN_COORD. Пропишем его как обычный метод:

def set_bound(self, left): self.MIN_COORD = left

Иногда ошибочно здесь рассуждают так. Мы обращаемся к атрибуту класса MIN_COORD и присваиваем ему новое значение left. Те из вас, кто внимательно смотрел предыдущие занятия, понимают, в чем ошибочность такого рассуждения. Да, когда мы через self (ссылку на объект) записываем имя атрибута и присваиваем ему какое-либо значение, то оператор присваивания создает этот атрибут в локальной области видимости, то есть, в самом объекте. В результате, у нас появляется новое локальное свойство в экземпляре класса:

pt1.set_bound(-100) print(pt1.__dict__)

А в самом классе одноименный атрибут остается без изменений:

Поэтому, правильнее было бы здесь объявить метод уровня класса и через него менять значения атрибутов MIN_COORD и MAX_COORD:

@classmethod def set_bound(cls, left): cls.MIN_COORD = left

Тогда в самом объекте не будет создаваться никаких дополнительных свойств, а в классе изменится значение переменной MIN_COORD, так, как мы этого и хотели.

  • __setattr__(self, key, value)__ – автоматически вызывается при изменении свойства key класса;
  • __getattribute__(self, item) – автоматически вызывается при получении свойства класса с именем item;
  • __getattr__(self, item) – автоматически вызывается при получении несуществующего свойства item класса;
  • __delattr__(self, item) – автоматически вызывается при удалении свойства item (не важно: существует оно или нет).
class Point: MAX_COORD = 100 MIN_COORD = 0 def __init__(self, x, y): self.__x = x self.__y = y def __getattribute__(self, item): print("__getattribute__") return object.__getattribute__(self, item)

Здесь добавлен новый магический метод __getattribute__. Он автоматически вызывается, когда идет считывание атрибута через экземпляр класса. Например, при обращении к свойству MIN_COORD:

Но раз это так, то давайте явно запретим считывать такой атрибут из экземпляра класса. Для этого пропишем в методе __getattribute__ проверку:

def __getattribute__(self, item): if item == "_Point__x": raise ValueError("Private attribute") else: return object.__getattribute__(self, item)

То есть, мы смотрим, если идет обращение к приватному атрибуту по внешнему имени _Point__x, то генерируем исключение ValueError. И, действительно, после запуска программы видим отображение этой ошибки в консоли. Вот так, через магический метод __getattribute__ можно реализовывать определенную логику при обращении к атрибутам через экземпляр класса. Следующий магический метод __setattr__ автоматически вызывается в момент присваивания атрибуту нового значения. Пропишем формально этот метод в классе Point:

def __setattr__(self, key, value): print("__setattr__") object.__setattr__(self, key, value)

После запуска видим несколько сообщений «__setattr__». Это связано с тем, что в момент создания экземпляров класса в инициализаторе __init__ создавались локальные свойства __x и __y. В этот момент вызывался данный метод. Также в переопределенном методе __setattr__ мы должны вызывать соответствующий метод из базового класса object, иначе, локальные свойства в экземплярах создаваться не будут. Давайте теперь для примера через этот магический метод запретим создание локального свойства с именем z. Сделаем это следующим образом:

def __setattr__(self, key, value): if key == 'z': raise AttributeError("недопустимое имя атрибута") else: object.__setattr__(self, key, value)
def __setattr__(self, key, value): if key == 'z': raise AttributeError("недопустимое имя атрибута") else: self.__x = value

В этом случае метод __setattr__ начнет выполняться по рекурсии, пока не возникнет ошибка достижения максимальной глубины рекурсии. Если нужно сделать что-то подобное, то используйте коллекцию __dict__:

или, если требуется стандартное поведение метода, то вызывайте его из класса object, как это мы прописывали вначале:

object.__setattr__(self, key, value)

Следующий магический метод __getattr__ автоматически вызывается, если идет обращение к несуществующему атрибуту. Добавим его в наш класс:

def __getattr__(self, item): print("__getattr__: " + item)

то увидим сообщение «__getattr__: a» и значение None, которое вернул данный метод. Если же прописать существующий атрибут:

то этот магический метод уже не вызывается. Зачем он может понадобиться? Например, нам необходимо определить класс, в котором при обращении к несуществующим атрибутам возвращается значение False, а не генерируется исключение. Для этого записывается метод __getattr__ в виде:

def __getattr__(self, item): return False

Наконец, последний магический метод __delattr__ вызывается в момент удаления какого-либо атрибута из экземпляра класса:

def __delattr__(self, item): print("__delattr__: "+item)

Это из-за того, что внутри этого метода нужно вызвать соответствующий метод класса object, который и выполняет непосредственное удаление:

def __delattr__(self, item): object.__delattr__(self, item)

Источник

Читайте также:  Elegant icons font css
Оцените статью