Перейти к содержимому

Как узнать все экземпляры класса python

  • автор:

Атрибуты класса и переменные экземпляра класса в Python

Переменные экземпляра класса предназначены для данных, уникальных для каждого экземпляра класса, а переменные класса (атрибуты данных класса) — для атрибутов и методов, общих для всех экземпляров класса.

Python вызывает специальный метод __init__() , который называют конструктором класса, каждый раз при создании нового экземпляра класса.

class Dog: # атрибут данных (переменная класса), # общая для всех экземпляров класса kind = 'canine' def __init__(self, name): # переменная экземпляра класса # уникальна для каждого экземпляра self.name = name >>> d = Dog('Fido') >>> e = Dog('Buddy') # переменная `kind` будет общая для # всех экземпляров объекта `Dog` >>> d.kind # 'canine' >>> e.kind # 'canine' # переменная `name` будет уникальна # для каждого из экземпляров >>> d.name # 'Fido' >>> e.name # 'Buddy' 

Как говорилось в материале «Классы в языке Python», общие данные могут иметь неожиданный эффект при использовании изменяемых объектов, таких как списки и словари. Например, список tricks (трюки, которые может делать отдельная собака) в примере ниже не следует использовать как атрибут данных/переменную класса, потому что для всех экземпляров класса Dog будет использоваться только один атрибут данных tricks :

class Dog: # ошибочное использование атрибута tricks - # переменной класса tricks = [] def __init__(self, name): self.name = name def add_trick(self, trick): self.tricks.append(trick) >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.add_trick('roll over') >>> e.add_trick('play dead') # неожиданно разделяется всеми собаками >>> d.tricks # ['roll over', 'play dead'] 

Правильный дизайн класса должен использовать tricks не как атрибут данных класса, а как переменную экземпляра класса:

class Dog: def __init__(self, name): self.name = name # создает новый пустой список # трюков для каждой собаки self.tricks = [] def add_trick(self, trick): self.tricks.append(trick) >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.add_trick('roll over') >>> e.add_trick('play dead') >>> d.tricks # ['roll over'] >>> e.tricks # ['play dead'] 

Если одно и то же имя атрибута встречается как в экземпляре класса, так и в самом классе, то поиск атрибута определяет приоритет экземпляра класса:

>>> class Warehouse: purpose = 'storage' region = 'west' >>> w1 = Warehouse() >>> print(w1.purpose, w1.region) # storage west >>> w2 = Warehouse() >>> w2.region = 'east' >>> print(w2.purpose, w2.region) # storage east 

На атрибуты данных класса могут ссылаться как методы, так и обычные пользователи — «клиенты» объекта. Другими словами, классы не могут использоваться для реализации чисто абстрактных типов данных. Фактически, ничто в Python не позволяет принудительно скрывать данные — все основано на соглашении.

Клиенты должны использовать переменные класса с осторожностью — клиенты могут испортить инварианты, поддерживаемые методами, изменив их атрибуты данных. Обратите внимание, что клиенты могут добавлять свои собственные атрибуты данных к объекту экземпляра, не влияя на достоверность методов, до тех пор, пока избегаются конфликты имен — опять же, соглашение об именовании может сэкономить здесь много головной боли.

В Python нет сокращений для ссылки на атрибуты данных или другие методы изнутри методов. Это повышает удобочитаемость методов: нет возможности путать локальные переменные и переменные экземпляра при просмотре метода.

Вместо использования привычной точечной нотации для доступа к атрибутам можно использовать встроенные функции:

  • getattr(obj, name [, default]) — для доступа к атрибуту name объекта класса obj .
  • hasattr(obj, name) — проверить, есть ли в классе obj атрибут name .
  • setattr(obj, name, value) — задать атрибут name со значением value . Если атрибут не существует, он будет создан.
  • delattr(obj, name) — удалить атрибут name из объекта класса obj .
Встроенные атрибуты класса.

Классы Python хранят встроенные атрибуты, к которым можно получить доступ как к любому другому атрибуту данных.

  • __dict__ — словарь, содержащий пространство имен класса.
  • __doc__ — строка документации класса. None если, документация отсутствует.
  • __name__ — имя класса.
  • __module__ — имя модуля, в котором определяется класс.
  • __bases__ — кортеж, содержащий базовые классы, в порядке их появления. Кортеж будет пустым, если наследование не было.
  • __mro__ — Порядок разрешения методов в множественном наследовании.

Где хранятся атрибуты класса и экземпляра класса?

Python не был бы Python без четко определенного и настраиваемого поведения атрибутов. Атрибуты в Python хранятся в магическом методе с именем __dict__ . Получить доступ к нему можно следующим образом:

class MyClass: class_attr = "Class" def __init__(self): self.instance_attr = "Instance" >>> my_object = MyClass() # атрибут экземпляра класса >>> my_object.__dict__ # # атрибут экземпляра класса >>> MyClass.__dict__['class_attr'] # 'Class' >>> my_object.class_attr 'Class' >>> my_object.instance_attr 'Instance' 
  • ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
  • Пространство имен и область видимости в классах
  • Определение классов
  • Объект класса и конструктор класса
  • Создание экземпляра класса
  • Метод экземпляра класса
  • Что такое метод класса и зачем нужен
  • Что такое статический метод в классах Python и зачем нужен
  • Атрибуты класса и переменные экземпляра класса
  • Кэширование методов экземпляра декоратором lru_cache
  • Закрытые/приватные методы и переменные класса Python
  • Наследование классов
  • Множественное наследование классов
  • Абстрактные классы
  • Перегрузка методов в классе Python
  • Что такое миксины и как их использовать
  • Класс Python как структура данных, подобная языку C
  • Создание пользовательских типов данных
  • Специальные (магические) методы класса Python
  • Базовая настройка классов Python магическими методами
  • Настройка доступа к атрибутам класса Python
  • Дескриптор класса для чайников
  • Протокол дескриптора класса
  • Практический пример дескриптора
  • Использование метода .__new__() в классах Python
  • Специальный атрибут __slots__ класса Python
  • Специальный метод __init_subclass__ класса Python
  • Определение метаклассов metaclass
  • Эмуляция контейнерных типов в классах Python
  • Другие специальные методы класса
  • Как Python ищет специальные методы в классах
  • Шаблон проектирования Фабрика и его реализация

Как найти экземпляр класса по значению атрибута?

Здравстуйте, я сейчас решаю задачку и у меня возникла проблема.

условие:
—————————————————————
Реализуйте программу, имитирующую работу почтовых серверов и клиентов.

Клиенты должны иметь возможность принимать и отправлять письма, которые хранятся на серверах.

Вам предстоит спроектировать программу самостоятельно. Чтобы задать направление мысли, давайте обсудим примерный интерфейс почтового клиента.

client = MailClient(server, user) — подключение к учётной записи пользователя user на сервере server.

client.receive_mail() — получение почты. Когда почта получена, она удаляется с сервера. Что конкретно возвращает этот метод, решите самостоятельно.

client.send_mail(server1, user1, message) — послать сообщение message пользователю user1 на сервере server1.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
class MailClient: data = {} def __init__(self, server, user): self.user = user MailClient.data[server] = MailClient.data.get(server, []) MailClient.data[server].append(MailClient(server, user)) self.messages = [] def receive_mail(self): if self.messages: for message in self.messages: print(message) print() self.messages.clear() else: print("К сожалению новых писем нет:(") def send_mail(self, server1, user1, message): if server1 in data: flag = True for elem in data[server1]: if elem.user == user1: elem.messages.append(message) flag = False if flag: print("Такого пользователя не существует") else: print(f"Сервера не существует")

Идея в том, чтобы при инициализации экземпляра класса в словаре data создавался ключ со значением server и в этот ключ записывалось user. И потом, в методе send_mail проверить существует ли сервер и пользователь на этом сервере. Но если добавлять имя пользователя как строку, то ничего не работает(что логично). Поэтому возник вопрос: можно ли как-то перебрать все экземпляры класса и сравнить их атрибут user с user1?
Пока написал какую-то чушь, так как совсем не представляю, как это можно реализовать.

Простите, если всё выглядит скомкано, я даже не могу вопрос корректно поставить, надеюсь, что вы поймёте.

Инстанцирование классов и экземпляры — Python: Введение в ООП

Класс, как мы уже увидели, может хранить данные. Но типичный класс присутствует в программе в единственном экземпляре. Поэтому сам по себе класс не очень полезен, ведь хранить определения можно и в модулях. Весь смысл использования классов заключается в их инстанцировании.

Инстанцированием (instantiation) называют процесс (акт) создания на основе класса экземпляра (instance) — такого объекта, который получает доступ ко всему содержимому класса, но при этом обладает и способностью хранить собственные данные. При этом, имея объект, всегда можно узнать, экземпляром какого класса он является.

Давайте объявим класс и создадим пару экземпляров, а заодно и познакомимся с синтаксисом инстанцирования классов:

class Person: pass bob = Person() bob # alice = Person() alice # bob is alice # False bob is Person # False alice is Person # False 

Что мы можем увидеть в этом примере? Первое, что бросается в глаза, это вызов класса как функции: Person() . Сходство это — не только внешнее. В Python инстанцирование фактически и является вызовом некоторой функции, которая возвращает новый экземпляр класса.

При выводе объекта класса в REPL можно увидеть строку, похожую на вывод информации о классе, только вместо «class» в строчке упоминается «object».

Также стоит обратить внимание на то, что все экземпляры являются отдельными объектами, поэтому оператор is дает False как при соотнесении экземпляров между собой, так и при соотнесении любого экземпляра с объектом класса ( bob , alice и Person — три самостоятельных объекта).

Атрибуты класса и экземпляры

В предыдущем примере класс был пустой. Теперь воспроизведем его, но добавим на этот раз атрибут:

class Person: name = 'Noname' bob, alice = Person(), Person() bob is alice # False bob.name is alice.name # True bob.name is Person.name # True bob.name # 'Noname' 

Этот пример показывает, что а) и bob , и alice имеют атрибут name , б) значение атрибутов name — общее для всех трех объектов.

Давайте же переименуем Боба:

bob.name = 'Bob' bob.name is Person.name # False Person.name # 'Noname' alice.name # 'Noname' bob.name # 'Bob' 

Вот вы и увидели то самое «собственное состояние объекта». Person продолжает давать имя всем экземплярам, пока те не изменят значение своего атрибута. В момент присваивания нового значения атрибуту экземпляра, экземпляр получает свой собственный атрибут!

Атрибут __dict__

Стоит прямо сейчас заглянуть «под капот» объектной системы Python, чтобы вы в дальнейшем могли исследовать объекты самостоятельно. Это и интересно, и полезно — как при обучении, так и при отладке объектного кода.

Итак, внутри каждого объекта Python хранит… словарь! Имена атрибутов в пространствах имен выступают ключами этого словаря, а значения являются ссылками на другие объекты. Словарь этот всегда называется __dict__ и тоже является атрибутом. Обращаясь к этому словарю, вы можете получить доступ к значениям атрибутов:

Person.__dict__['name'] # 'Noname' bob.__dict__['name'] # 'Bob' alice.__dict__['name'] # Traceback (most recent call last): # File "", line 1, in # KeyError: 'name' 

Присмотритесь, и вы увидите: у bob в __dict__ есть его собственное имя, а у alice собственного имени нет. Но при обращении к атрибуту привычным способом «через точку», вы видите имя и у alice ! Как же это работает?

Дело в том, что машинерия объектной системы Python при обращении к атрибуту сначала ищет атрибут в словаре экземпляра. Но если там соответствующего ключа не нашлось, то атрибут ищется уже в классе. Именно так alice получает имя: Python находит его в классе Person .

Надо сказать, что это очень разумный подход! Да, Python мог бы копировать словарь класса при инстанцировании. Но это привело бы к излишнему потреблению памяти. А вот «коллективное использование», напротив, позволяет память экономить!

И, конечно же, словарь __dict__ объекта может быть изменен. Когда мы давали Бобу имя, мы на самом деле сделали что-то такое:

bob.__dict__['name'] = 'Bob' 

Мы даже можем добавить Бобу фамилию и сделать это через модификацию __dict__ :

bob.__dict__['surname'] = 'Smith' bob.surname # 'Smith' 'surname' in Person.__dict__ # False 

А ведь у класса не было атрибута surname ! Каждый экземпляр класса тоже является самостоятельным пространством имен, пригодным для расширения в процессе исполнения программы (за счет использования под капотом словарей, как вы теперь знаете!).

Проверка принадлежности экземпляра к классу

Я выше уже упоминал, что объект всегда связан с классом. Эта связь заключается в наличии у экземпляра атрибута __class__ , который является ссылкой на объект класса:

bob.__class__ # bob.__class__ is Person # True 

Как вы уже могли заметить, в Python многие «внутренние штуки» имеют имена, заключенные в двойные символы подчеркивания. В разговоре питонисты обычно проговаривают подобные имена примерно так: «дАндер-класс», что является калькой с «dunder class», где «dunder», в свою очередь, это сокращение от «double underscore», то есть «двойной символ подчеркивания». Полезно запомнить этот стиль именования!

А еще стоит запомнить, что практически всегда, когда вы хотите использовать что-то, названное в dunder-стиле, «есть способ лучше»! Так с __dict__ напрямую работать не приходится, потому что есть возможность обращаться к атрибутам «через точку». Вот и __class__ в коде встречается редко. А рекомендуемый способ проверки принадлежности к классу выглядит так:

isinstance(bob, Person) # True 

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов

Наши выпускники работают в компаниях:

Метод в Python, выводящий все экземпляры своего класса

Как создать метод в Python, который будет выводить все экземпляры того же класса, в котором определен этот метод?

Отслеживать
задан 28 июн 2017 в 18:44
105 2 2 серебряных знака 8 8 бронзовых знаков
28 июн 2017 в 18:49

4 ответа 4

Сортировка: Сброс на вариант по умолчанию

Вот вариант в котором не течет память, и который поддерживает оборачивание нескольких классов.

import weakref class MyMeta(type): def __init__(self, *args, **kwargs): self.__instances = <> super(MyMeta, self).__init__(*args, **kwargs) def get_instances(self): return list(self.__instances.values()) def delete(self, id_instance): del self.__instances[id_instance] def __call__(self, *args, **kwargs): instance = super(MyMeta, self).__call__(*args, **kwargs) self.__instances[id(instance)] = weakref.proxy(instance) return instance 

Использование (можно создать несколько таких классов, не копируя метакласс):

class MyClass(object, metaclass=MyMeta): def __del__(self): # этот метод и действие внутри него обязательно, иначе будет течь память под прокси self.__class__.delete(id(self)) print(MyClass.get_instances()) a = MyClass() b = MyClass() print(MyClass.get_instances()) del b print(MyClass.get_instances()) 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *