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

Как создать экземпляр класса python

  • автор:

Создание классов и объектов

В языке программирования Python классы создаются с помощью инструкции class , за которой следует произвольное имя класса, после которого ставится двоеточие, далее с новой строки и с отступом реализуется тело класса:

class ИмяКласса: код_тела_класса

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

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

ИмяКласса()

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

имя_переменной = ИмяКласса()

В последствии к объекту обращаются через связанную с ним переменную.

Пример «пустого» класса и двух созданных на его основе объектов:

>>> class A: . pass . >>> a = A() >>> b = A()

Класс как пространство имен

С точки зрения пространства имен класс можно представить подобным модулю. Также как в модуле в классе могут быть свои переменные со значениями и функции. Также как в модуле у класса есть собственное пространство имен, доступ к которому возможен через имя класса:

>>> class B: . n = 5 . def adder(v): . return v + B.n . >>> B.n 5 >>> B.adder(4) 9

Однако в случае классов используется особая терминология. Пусть имена, определенные в классе, называются атрибутами этого класса. В примере имена n и adder – это атрибуты класса B . Атрибуты-переменные часто называют полями или свойствами (в других языках понятия «поле» и «свойство» не совсем одно и то же). Полем является n . Атрибуты-функции называются методами. Методом в классе B является adder . Количество свойств и методов в классе может быть любым.

Класс как шаблон для создания объектов

На самом деле классы – не модули. Они своего рода шаблоны, от которых создаются объекты-экземпляры. Такие объекты наследуют от класса его атрибуты. Вернемся к нашему классу B и создадим на его основе два объекта:

>>> class B: . n = 5 . def adder(v): . return v + B.n . >>> a = B() >>> b = B()

У объектов, связанных с переменными a и b , нет собственного поля n . Однако они наследуют его от своего класса:

>>> a.n 5 >>> a.n is B.n True

То есть поля a.n и B.n – это одно и то же поле, к которому можно обращаться и через имя a , и через имя b , и через имя класса. Поле одно, ссылок на него три.

Однако что произойдет в момент присваивания этому полю значения через какой-нибудь объект-экземпляр?

>>> a.n = 10 >>> a.n 10 >>> b.n 5 >>> B.n 5

В этот момент у экземпляра появляется собственный атрибут n , который перекроет (переопределит) родительский, то есть тот, который достался от класса.

>>> a.n is B.n False >>> b.n is B.n True

При этом присвоение через B.n отразится только на b и B , но не на a :

>>> B.n = 100 >>> B.n, b.n, a.n (100, 100, 10)

Иная ситуация нас ожидает с атрибутом adder . При создании объекта от класса функция adder не наследуется как есть, а как бы превращается для объекта в одноименный метод:

>>> B.adder is b.adder False >>> type(B.adder) >>> type(b.adder)

Через имя класса мы вызываем функцию adder :

>>> B.adder(33) 133

Через имя объекта вызываем метод adder :

>>> b.adder(33) Traceback (most recent call last): File "", line 1, in TypeError: adder() takes 1 positional argument but 2 were given

В сообщении об ошибке говорится, что adder принимает только один аргумент, а было передано два. Откуда появился второй, если в скобках было указано только одно число?

Дело в том, что в отличии от функции в метод первым аргументом всегда передается объект, к которому применяется этот метод. То есть выражение b.adder(33) как бы преобразовывается в adder(b, 33) . Сам же b.adder как объект типа method хранит сведения, с каким классом он связан и какому объекту-экземпляру принадлежит:

>>> b.adder >

В нашем случае, чтобы вызывать adder через объекты-экземпляры, класс можно переписать так:

>>> class B: . n = 5 . def adder(obj, v): . return v + obj.n . >>> b = B() >>> b.adder(33) 38

В коде выше при вызове метода adder переменной-параметру obj присваивается объект, связанный с переменной, к которой применяется данный метод. В данном случае это объект, связанный с b . Если adder будет вызван на другой объект, то уже он будет присвоен obj :

>>> a = B() >>> a.n = 9 >>> a.adder(3) 12

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

>>> class B: . n = 5 . def adder(self, v): . return v + self.n

Можем ли мы все также вызывать adder как функцию, через имя класса? Вполне. Только теперь в функцию надо передавать два аргумента:

>>> B.adder(B, 200) 205 >>> B.adder(a, 200) 209

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

Однако если атрибут определен так, что предполагается его работа в качестве метода, а не функции, то через класс его уже не вызывают (нет смысла, логика программы этого не подразумевает).

С другой стороны, в ООП есть понятие «статический метод». По сути это функция, которая может вызываться и через класс, и через объект, и которой первым аргументом не подставляется объект, на который она вызывается. В Python статический метод можно создать посредством использования специального декоратора.

Атрибут __dict__

В Python у объектов есть встроенные специальные атрибуты. Мы их не определяем, но они есть. Одним из таких атрибутов объекта является свойство __dict__ . Его значением является словарь, в котором ключи – это имена свойств экземпляра, а значения – текущие значения свойств.

>>> class B: . n = 5 . def adder(self, v): . return v + self.n . >>> w = B() >>> w.__dict__ <> >>> w.n = 8 >>> w.__dict__

В примере у экземпляра класса B сначала нет собственных атрибутов. Свойство n и метод adder – это атрибуты объекта-класса, а не объекта-экземпляра, созданного от этого класса. Лишь когда мы выполняем присваивание новому полю n экземпляра, у него появляется собственное свойство, что мы наблюдаем через словарь __dict__ .

В следующем уроке мы увидим, что свойства экземпляра обычно не назначаются за пределами класса. Это происходит в методах классах путем присваивание через self . Например, self.n = 10 .

Атрибут __dict__ используется не только для просмотра свойств объекта. С его помощью можно удалять, добавлять свойства, а также изменять их значения.

>>> w.__dict__['m'] = 100 >>> w.__dict__ >>> w.m 100

Практическая работа

Напишите программу по следующему описанию. Есть класс «Воин». От него создаются два экземпляра-юнита. Каждому устанавливается здоровье в 100 очков. В случайном порядке они бьют друг друга. Тот, кто бьет, здоровья не теряет. У того, кого бьют, оно уменьшается на 20 очков от одного удара. После каждого удара надо выводить сообщение, какой юнит атаковал, и сколько у противника осталось здоровья. Как только у кого-то заканчивается ресурс здоровья, программа завершается сообщением о том, кто одержал победу.

Курс с примерами решений практических работ:
pdf-версия

X Скрыть Наверх

Объектно-ориентированное программирование на Python

Создание экземпляра класса в Python

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

  • Создание нового экземпляра целевого класса;
  • Инициализация нового экземпляра с соответствующим начальным состоянием.

Для выполнения первого шага в классах Python есть специальный метод .__new__() , который отвечает за создание и возврат нового пустого объекта. Затем другой специальный метод .__init__() , принимает результирующий объект вместе с аргументами конструктора класса.

Метод .__init__() принимает новый объект в качестве первого аргумента self . Затем он устанавливает любой требуемый атрибут экземпляра в допустимое состояние, используя аргументы, переданные ему конструктором класса.

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

Чтобы изучить внутреннюю работу процесса создания экземпляров Python, рассмотрите следующий пример класса Point , который (в демонстрационных целях) реализует пользовательскую версию обоих методов, .__new__() и .__init__() :

# point.py class Point: def __new__(cls, *args, **kwargs): print("1. Создается новый экземпляр Point.") return super().__new__(cls) def __init__(self, x, y): print("2. Инициализируется новый экземпляр Point.") self.x = x self.y = y def __repr__(self) -> str: return f"type(self).__name__>(x=self.x>, y=self.y>)" 

Описание того, что делает этот код:

  • Строка def __new__(cls, *args, **kwargs) определяет метод, который принимает класс в качестве первого аргумента. Обратите внимание, что использование cls в качестве имени этого аргумента является строгим соглашением в Python, точно так же, как использование self для имени текущего экземпляра. Метод также принимает *args и **kwargs , что позволяет передавать неопределенное количество аргументов инициализации базовому экземпляру.
  • Строка return super().__new__(cls) создает и возвращает новый экземпляр Point, вызывая метод родительского класса .__new__() с cls в качестве аргумента. Этот экземпляр будет первым аргументом для .__init__() . В этом примере объект является родительским классом, и вызов super() дает доступ к нему.
  • Строка def __init__(self, x, y) определяет метод конструктора, который отвечает за этап инициализации. Этот метод принимает первый аргумент с именем self , который содержит ссылку на текущий экземпляр и два дополнительных аргумента, x и y .
  • Внутри метода конструктора .__init__() инициализируются начальные значения атрибутов экземпляра Point.x и Point.y соответственно. Для этого он использует входные аргументы x и y .

Сохраним код, представленный выше в файл с именем point.py и запустим интерпретатор Python:

>>> from point import Point >>> point = Point(21, 42) # 1. Создается новый экземпляр Point. # 2. Инициализируется новый экземпляр Point. >>> point # Point(x=21, y=42) 

Операции, доступные экземплярам класса.

Единственные операции, понятные объектам-экземплярам класса, являются ссылки на атрибуты класса. Существует два вида допустимых имен атрибутов класса, это атрибуты данных и методы класса.

Атрибуты данных соответствуют «переменным экземпляра» в языке Smalltalk или «членам данных» в языке C++. Атрибуты данных класса в Python можно не объявлять, как например это делается с локальным переменным, они появляться динамически, когда им впервые присваивается значение. При этом, динамически созданные атрибуты хранятся в специальном словаре объекта-экземпляра x.__dict__ . Например, если x это экземпляр MyClass , то следующий фрагмент кода напечатает значение 16 .

Создайте файл test.py с определением класса MyClass и запустите его в интерактивном режиме командой: python3 -i test.py .

# файл `test.py` class MyClass: """Простой пример класса""" i = 12345 def f(self): return 'hello world' # запускаем: $ python3 -i test.py >>> x = MyClass() # обратите внимание, что атрибут # `counter` в классе не определен >>> x.counter = 1 # динамически созданные атрибуты экземпляра класса # хранятся в специальных словарях этих экземпляров >>> x.__dict__ # >>> while x.counter  10: . x.counter = x.counter * 2 . >>> x.counter # 16 # удаляем динамически созданный атрибут >>> del x.counter # смотрим специальный словарь экземпляра >>> x.__dict__ # <> # пытаемся получить значение x.counter # Traceback (most recent call last): # File "", line 1, in # AttributeError: 'MyClass' object has no attribute 'counter' 

Другой вид ссылки на атрибут объекта-экземпляра — это метод. Метод — это функция, которая принадлежит объекту класса. В языке Python термин «метод«, для экземпляров классов, не уникален: другие типы объектов также могут иметь свои методы. Например, объекты списка list() имеют методы list.append , list.insert , list.remove , list.sort и т. д. Дальше будем использовать термин «метод» исключительно для обозначения «методов объектов экземпляра класса«, если явно не указано иное.

Допустимые имена методов объекта экземпляра класса зависят от его класса. По определению, все атрибуты класса, являющиеся объектами функций, определяют соответствующие методы его экземпляров. Таким образом, в нашем примере x.f это допустимая ссылка на связанный метод, так как MyClass.f это функция. Тогда как x.i это НЕ метод, а ссылка на атрибут класса MyClass.i . При этом выражение x.f это объект связанного метода, т.е. не то же самое, что MyClass.f . Так как MyClass.f — это объект функции.

Смотрим пример, который это показывает:

>>> MyClass.f # >>> x.f # > >>> MyClass.i # 12345 >>> x.i # 12345 
  • ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
  • Пространство имен и область видимости в классах
  • Определение классов
  • Объект класса и конструктор класса
  • Создание экземпляра класса
  • Метод экземпляра класса
  • Что такое метод класса и зачем нужен
  • Что такое статический метод в классах Python и зачем нужен
  • Атрибуты класса и переменные экземпляра класса
  • Кэширование методов экземпляра декоратором lru_cache
  • Закрытые/приватные методы и переменные класса Python
  • Наследование классов
  • Множественное наследование классов
  • Абстрактные классы
  • Перегрузка методов в классе Python
  • Что такое миксины и как их использовать
  • Класс Python как структура данных, подобная языку C
  • Создание пользовательских типов данных
  • Специальные (магические) методы класса Python
  • Базовая настройка классов Python магическими методами
  • Настройка доступа к атрибутам класса Python
  • Дескриптор класса для чайников
  • Протокол дескриптора класса
  • Практический пример дескриптора
  • Использование метода .__new__() в классах Python
  • Специальный атрибут __slots__ класса Python
  • Специальный метод __init_subclass__ класса Python
  • Определение метаклассов metaclass
  • Эмуляция контейнерных типов в классах Python
  • Другие специальные методы класса
  • Как Python ищет специальные методы в классах
  • Шаблон проектирования Фабрика и его реализация

Как создать новый экземпляр класса с новыми рандомными данными?

Создаются 2 разных объекта, но значение number у них будет всегда одинаковое. Как создать несколько экземпляров класса, в которых будут каждый раз генерироваться случайные значения number (и чтобы вручную не передавать туда number=. )? Чтобы выполнялось такое условие:

test1 = Test() test2 = Test() assert test1.number != test2.number 

Отслеживать
48.6k 17 17 золотых знаков 56 56 серебряных знаков 100 100 бронзовых знаков
задан 30 мар 2021 в 9:43
71 8 8 бронзовых знаков

Пояснение. Важен именно такой подход в создании класса, без функции инициализации экземпляров. Возможно ли это вообще?

30 мар 2021 в 10:17

Еще одно пояснение. Необходимо, чтобы оставалась возможность передать свое значение number при создании нового экземпляра. А если не передано — тогда генерировать рандомное.

30 мар 2021 в 10:54

5 ответов 5

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

В обычном случае, вам подойдёт вариант, предложенный @strawdog:

class Test: number: str def __init__(): self.number = random.randint(100, 500) 

Если же вы используете pydantic.BaseModel , то данные можно передать в super().__init__(**data) при инициализации:

class Test(pydantic.BaseModel): number: str def __init__(self, number=None, **data): super().__init__( number=random.randint(100, 500) if number is None else number, **data ) 

Отслеживать
48.6k 17 17 золотых знаков 56 56 серебряных знаков 100 100 бронзовых знаков
ответ дан 30 мар 2021 в 10:21
73 6 6 бронзовых знаков

Во втором примере я не смогу самостоятельно указать number при создании экземпляра, а нужно также учитывать и эту возможность.

30 мар 2021 в 10:52

@Gentleman, изменил ответ: в __init__ можно передать number, а если он не указан, значение None по умолчанию заменится случайным числом при super инициализации.

30 мар 2021 в 10:59

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

import random class Test: def __init__(self): self.number = random.randint(100, 500) test1 = Test() test2 = Test() test1.number==test2.number #False 

Отслеживать
ответ дан 30 мар 2021 в 9:53
25.4k 4 4 золотых знака 20 20 серебряных знаков 36 36 бронзовых знаков

Дело в том, что я использую pydantic для создания модели, поэтому данный способ не подходит для меня.

30 мар 2021 в 9:57
@Gentleman это крайне важное уточнение обязано присутствовать в тексте вопроса
30 мар 2021 в 11:19
@andreymal спасибо. Я осознал, что не указал в вопросе все требования. Дописал их комментариях.
30 мар 2021 в 11:35

Определение поля в классе в pydantic — это значение по умолчанию для всех экземпляров, для которых такое значение не задано. По-этому, то, что вы наблюдаете — это нормальное ожидаемое поведение.

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

class Test(pydantic.BaseModel): number: str def __init__(self, **data): if 'number' not in data: data['number'] = random.randint(100, 500) super().__init__(**data) 

Ну и еще есть factoryboy как раз для генерации тестовых данных.

Отслеживать
ответ дан 30 мар 2021 в 11:00
Roman-Stop RU aggression in UA Roman-Stop RU aggression in UA
23.4k 1 1 золотой знак 18 18 серебряных знаков 29 29 бронзовых знаков
Спасибо за наводку. Начал изучать информацию о factoryboy и наткнулся на pydactory. Решение отпишу.
30 мар 2021 в 11:27

Всем спасибо за ответы, они действительно правильные. Но мне кажется более изящным и читабельным следующее решение:

import random from pydantic import BaseModel from pydactory import Factory class Test(BaseModel): number: int class TestFactory(Factory[Test]): number: int = lambda: random.randint(100, 500) test1: Test = TestFactory.build() test2: Test = TestFactory.build() assert test1.number != test2.number # True 

Напишите, пожалуйста, в комментариях, что вы думаете по поводу данного решения.

Отслеживать
ответ дан 30 мар 2021 в 11:33
71 8 8 бронзовых знаков

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

25 ноя 2021 в 8:17

В pydantic и dataclasses можно указать default_factory («фабрику» значения по умолчанию) для поля. Обычно это используется для инициализации поля пустым списком (или другим контейнером) например default_factory=list , но можно передать лямбду, которая вызывает random.randint(100, 500) .

Вариант с использованием pydantic :

from pydantic import BaseModel, Field import random class Test(BaseModel): number: str = Field(default_factory = lambda: random.randint(100, 500)) test1 = Test() test2 = Test() print(repr(test1)) print(repr(test2)) 
Test(number=387) Test(number=487) 

Аналогично, на dataclasses :

from dataclasses import dataclass, field import random @dataclass class Test: number: str = field(default_factory = lambda: random.randint(100, 500)) test1 = Test() test2 = Test() print(test1) print(test2) 
Test(number=254) Test(number=247) 

Объектно-ориентированное программирование. Классы и объекты

Python 3 логотип

Сегодня мы поговорим об объектно-ориентированном программировании и о его применении в python.

Объектно-ориентированное программирование (ООП) — парадигма программирования, в которой основными концепциями являются понятия объектов и классов.

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

Python соответствует принципам объектно-ориентированного программирования. В python всё является объектами — и строки, и списки, и словари, и всё остальное.

Но возможности ООП в python этим не ограничены. Программист может написать свой тип данных (класс), определить в нём свои методы.

Это не является обязательным — мы можем пользоваться только встроенными объектами. Однако ООП полезно при долгосрочной разработке программы несколькими людьми, так как упрощает понимание кода.

Приступим теперь собственно к написанию своих классов на python. Попробуем определить собственный класс:

  Теперь мы можем создать несколько экземпляров этого класса:
       File 

Классу возможно задать собственные методы:

И напоследок еще один пример:

Для вставки кода на Python в комментарий заключайте его в теги

  • Модуль csv - чтение и запись CSV файлов
  • Создаём сайт на Django, используя хорошие практики. Часть 1: создаём проект
  • Онлайн-обучение Python: сравнение популярных программ
  • Книги о Python
  • GUI (графический интерфейс пользователя)
  • Курсы Python
  • Модули
  • Новости мира Python
  • NumPy
  • Обработка данных
  • Основы программирования
  • Примеры программ
  • Типы данных в Python
  • Видео
  • Python для Web
  • Работа для Python-программистов
  • Сделай свой вклад в развитие сайта!
  • Самоучитель Python
  • Карта сайта
  • Отзывы на книги по Python
  • Реклама на сайте

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

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