Как вернуть объект класса python
Перейти к содержимому

Как вернуть объект класса 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: obj.name . Допустимые имена атрибутов — это все имена, которые были определены в пространстве имен класса при создании объекта класса.

Итак, если определение класса выглядит так:

class MyClass: """Простой пример класса""" i = 12345 def f(self): return 'hello world' # `MyClass` - это объект класса >>> MyClass.i # 12345 >>> MyClass.f #

то имя MyClass — это объект класса, а MyClass.i и MyClass.f являются действительными ссылками на атрибуты класса, возвращая целое число и функциональный объект соответственно. Атрибуты объекта класса также могут быть присвоены, так что можно изменить значение MyClass.i путем операции присвоения. __doc__ также является допустимым атрибутом, возвращающим строку документации «Простой пример класса» , принадлежащую классу MyClass .

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

x = MyClass() >>> x.i # 12345 >>> x.f() # 'hello world' 

Конструктор класса __init__() .

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

class MyClass: """Простой пример класса""" i = 12345 def __init__(self): self.data = [] def f(self): return 'hello world' 

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

>>> x = MyClass() >>> x.data # [] 

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

>>> class Complex: . def __init__(self, realpart, imagpart): . self.r = realpart . self.i = imagpart . >>> x = Complex(3.0, -4.5) >>> x.r, x.i (3.0, -4.5) 

Важно отметить, что без учета self , аргументы метода .__init__() — это те же самые аргументы, которые передаются при вызове конструктора класса. Таким образом, сигнатура .__init__() определяет сигнатуру конструктора класса.

Имейте в виду, что конструктор класса .__init__() не должен явно возвращать ничего отличного от None , иначе будет вызываться исключение TypeError :

>>> class Complex: . def __init__(self, realpart, imagpart): . self.r = realpart . self.i = imagpart . return realpart + imagpart >>> x = Complex(3.0, -4.5) # Traceback (most recent call last): # File "", line 1, in # TypeError: __init__() should return None, not 'float' 

В конструкторе класса .__init__() можно выполнить любое преобразование входных аргументов, чтобы правильно инициализировать атрибуты экземпляра. Например, если пользователи будут использовать класс Rectangle , то например, можно проверить предоставленные ширину и высоту, для того, чтобы убедиться в их верном значении:

>>> class Rectangle: . def __init__(self, width, height): . if not (isinstance(width, (int, float)) and width > 0): . raise ValueError(f"Необходима положительная ширина, получена: width>") . self.width = width . if not (isinstance(height, (int, float)) and height > 0): . raise ValueError(f"Необходима положительная высота, получена: height>") . self.height = height >>> rectangle = Rectangle(-21, 42) # Traceback (most recent call last): # File "", line 1, in # File "", line 4, in __init__ # ValueError: Необходима положительная ширина, получена: -21 

Теперь предположим, что используется наследование для создания пользовательской иерархии классов и повторного использования некоторых функций в своем коде. Если подклассы предоставляют конструктор .__init__() , то он должен явно вызывать метод .__init__() базового класса с соответствующими аргументами, чтобы обеспечить правильную инициализацию экземпляров. Для этого необходимо использовать встроенную функцию super() , как в следующем примере:

>>> class Person: . def __init__(self, name, birth_date): . self.name = name . self.birth_date = birth_date >>> class Employee(Person): . def __init__(self, name, birth_date, position): . super().__init__(name, birth_date) . self.position = position >>> john = Employee("John Doe", "2001-02-07", "Разработчик Python") >>> john.name # 'John Doe' >>> john.birth_date # '2001-02-07' >>> john.position # 'Разработчик Python' 
  • ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
  • Пространство имен и область видимости в классах
  • Определение классов
  • Объект класса и конструктор класса
  • Создание экземпляра класса
  • Метод экземпляра класса
  • Что такое метод класса и зачем нужен
  • Что такое статический метод в классах Python и зачем нужен
  • Атрибуты класса и переменные экземпляра класса
  • Кэширование методов экземпляра декоратором lru_cache
  • Закрытые/приватные методы и переменные класса Python
  • Наследование классов
  • Множественное наследование классов
  • Абстрактные классы
  • Перегрузка методов в классе Python
  • Что такое миксины и как их использовать
  • Класс Python как структура данных, подобная языку C
  • Создание пользовательских типов данных
  • Специальные (магические) методы класса Python
  • Базовая настройка классов Python магическими методами
  • Настройка доступа к атрибутам класса Python
  • Дескриптор класса для чайников
  • Протокол дескриптора класса
  • Практический пример дескриптора
  • Использование метода .__new__() в классах Python
  • Специальный атрибут __slots__ класса Python
  • Специальный метод __init_subclass__ класса Python
  • Определение метаклассов metaclass
  • Эмуляция контейнерных типов в классах Python
  • Другие специальные методы класса
  • Как Python ищет специальные методы в классах
  • Шаблон проектирования Фабрика и его реализация

Возврат экземпляра класса из функции

Я создаю функцию, внутри которой создается экземпляр класса Inst. Как правильно вернуть этот экземпляр и все его атрибуты, чтобы им можно было пользоваться в дальнейшем?

return Inst — возвращает, как это правильно назвать, ссылку на экземпляр?

С ходу гугл не помог.

Понимаю, что такие вещи надо в книжках искать, но если кто-нибудь поможет — очень буду благодарен.

94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

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

Почему при вызове метода у экземпляра класса он сначала ищет у экземпляра
Всем привет, после прочтения Лутца про ООП появился такой вопрос почему при вызове метода у.

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

Возврат имени экземпляра класса
Есть класс CLASS1, у него 2 экземпляра- ex1 и ex2. Нужно что бы при вызове метода в одном из.

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

Эксперт по компьютерным сетямЭксперт Pascal/Delphi

4189 / 1290 / 237
Регистрация: 27.07.2009
Сообщений: 3,962

1 2 3 4 5 6 7 8 9 10 11 12 13
class Class1(object): def __init__(self, text): self.var1 = text def func(): g = Class1('text') # создали экземпляр класса g.var1 = 'sometext' # изменили переменную в экземпляре класса return g # вернули экземпляр класса j = Class1('other text') # создали новый экземпляр вне ф-ции h = func() # вызвали ф-цию и результат ее работы (экземпляр класса) присвоили в h print(j.var1) # смотрим что в экземпляре вне ф-ции print(h.var1) # смотрим что в экземпляре от ф-ции

Регистрация: 06.10.2020
Сообщений: 13

ЦитатаСообщение от TAVulator Посмотреть сообщение

TAVulator, Да, именно так я и попытался это реализовать, но видимо загвоздка не в этом.

Попробую чуть более детально описать ситуацию:

В теле основной программы есть функция:

1 2 3 4 5 6 7 8 9 10
def FiberReturn(): fiber1 = FiberFunction(100, 0.1, 0.0001, 1550*10**-(9), 1551*10**(-9), 1, 0.0001) fiber1.LightPropagation() fiber1.Fourier() ui.graphicsView_2.clear() ui.graphicsView_3.clear() ui.graphicsView_2.plot(np.real(fiber1.ffn)) ui.graphicsView_3.plot(np.real(fiber1.fftstr)) return fiber1

Соответственно создается экземпляр fiber1 и вызываются 2 метода, которые рассчитывают некоторые атрибуты. Класс FiberFunction загружается из модуля.

Далее внутри этой же функции рисуем графики атрибутов экземпляра. Они отображаются корректно.

1 2 3
def OpenWindow(): SignalWindow.show() uis.graphicsView.plot(np.real(fiber1.ffn),pen=2)

Обращается к атрибуту экземпляра, который был инициализирован при вызове первой функции. Но она это обращение «не видит».

И да, это все происходит внутри PyQt5 интерфейса, функции вызываются через нажатия кнопок. Я изначально подумал, что проблема именно в базовом подходе.

Если эта проблема возникает именно при работе внутри pyqt5 интерфейса, то прошу прощения и попрошу перенести тему.

Спасибо за уделенное время!

Эксперт Python

5415 / 3839 / 1214
Регистрация: 28.10.2013
Сообщений: 9,554
Записей в блоге: 1

ЦитатаСообщение от 9FiZiK Посмотреть сообщение

uis.graphicsView.plot(np.real(fiber1.ffn),pen=2)

Откуда эта функция берет экземпляр fiber1? Ты же нигде его прокидываешь сюда.
Из глобальной области что ли? Почему PyQT не завернуто в ООП?
Что делает .show()? Отрисовывает окно? Ее же всегда пишут последним вызовом.

Эксперт по компьютерным сетямЭксперт Pascal/Delphi

4189 / 1290 / 237
Регистрация: 27.07.2009
Сообщений: 3,962

как происходит вызов FiberReturn() ?
куда сохраняется результат этой ф-ции?
Вот как найдете в какую переменную охраняется результат ф-ции FiberReturn(), то смело подставляйте ее вместо fiber1 в

ЦитатаСообщение от 9FiZiK Посмотреть сообщение

uis.graphicsView.plot(np.real(fiber1.ffn),pen=2)
Регистрация: 06.10.2020
Сообщений: 13

ЦитатаСообщение от Garry Galler Посмотреть сообщение

Из глобальной области что ли?

Да, я думал что return возвращает в глобальную область и я смогу свободно использовать этот экземпляр.

ЦитатаСообщение от Garry Galler Посмотреть сообщение

Почему PyQT не завернуто в ООП?

Вы имеете ввиду непосредственно функции нажатий кнопок?

ЦитатаСообщение от Garry Galler Посмотреть сообщение

Что делает .show()? Отрисовывает окно? Ее же всегда пишут последним вызовом.

Да, отрисовывает окно, но в данном случае порядок оказывается не важным.

Сделал следующим образом:

1 2 3 4 5 6 7 8 9 10 11
def FiberReturn(): fiber1 = FiberFunction(1000, 0.1, 0.0001, 1550*10**-(9), 1551*10**(-9), 1, 0.0001) fiber1.LightPropagation() fiber1.Fourier() print(fiber1.fftstr[5], fiber1.fftnstr[5]) ui.graphicsView_2.clear() ui.graphicsView_3.clear() ui.graphicsView_2.plot(np.real(fiber1.ffn)) ui.graphicsView_3.plot(np.real(fiber1.fftstr))

Оставил в возврате только один из атрибутов, присвоив его предварительно в «x».

Вторую функцию переписал как:

1 2 3
def OpenWindow(x1): uis.graphicsView.plot(np.real(x1),pen=2) SignalWindow.show()
ui.pushButton_6.clicked.connect(GUIFunc.FiberReturn) #вызов первой функции
ui.pushButton_2.clicked.connect(GUIFunc.OpenWindow(x)) # Вызов второй функции

Если в теле я нигде не объявляю переменную «x», то мне пишет «Undefined name x»
либо если я опишу «x» как например x = 0, или global x — не важно

TypeError: argument 1 has unexpected type ‘NoneType’

Добавлено через 40 минут

В предыдущем ответе немного неправильно написал

ui.pushButton_6.clicked.connect(FiberReturn) #вызов первой функции
ui.pushButton_2.clicked.connect(OpenWindow(x)) # Вызов второй функции

Если в теле я нигде не объявляю переменную «x», то мне пишет «Undefined name x»
либо если я опишу «x» как например x = 0, или global x — не важно

TypeError: argument 1 has unexpected type ‘NoneType’

Эксперт Python

5415 / 3839 / 1214
Регистрация: 28.10.2013
Сообщений: 9,554
Записей в блоге: 1

ЦитатаСообщение от 9FiZiK Посмотреть сообщение

ui.pushButton_2.clicked.connect(OpenWindow(x))

В clicked.connect передается не вызов функции, а сама функция.

P.S. У тебя проблема с созданием правильной структуры приложения. Неявные возвраты, обращение к global и т.д. до добра не доводят.

Добавлено через 38 минут
P.P.S. Если хочешь привязать x к функции без ее вызова используй functools.partial.

Регистрация: 06.10.2020
Сообщений: 13

ЦитатаСообщение от Garry Galler Посмотреть сообщение

В clicked.connect передается не вызов функции, а сама функция.
P.S. У тебя проблема с созданием правильной структуры приложения. Неявные возвраты, обращение к global и т.д. до добра не доводят.

Да, я это понимаю. Даже уже некоторые шаги сделал: завернул модуль в ООП. Но в данной проблеме не могу понять правильного подхода. Мои нативные представления говорят мне: напиши return x и пользуйся им, но не работает)

Не могли бы вы подсказать как будет правильно организовать основную программу? Либо где конкретно об этом можно почитать/посмотреть.

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

Получение имени класса экземпляра в Python

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

Рассмотрим пример. Допустим, у нас есть два класса — Cat и Dog , и мы создаем экземпляры этих классов:

class Cat: pass class Dog: pass fluffy = Cat() buddy = Dog()

Теперь, предположим, мы хотим узнать, к какому классу относится объект fluffy .

В Python для этого есть несколько способов. Один из них — использовать встроенную функцию type() , которая возвращает тип объекта.

print(type(fluffy))

Вывод будет следующим:

<class '__main__.Cat'>

Это означает, что экземпляр fluffy принадлежит классу Cat .

Однако, если требуется получить только имя класса, без лишней информации, можно использовать атрибут __class__.__name__ экземпляра.

print(fluffy.__class__.__name__)

В этом случае вывод будет просто:

Таким образом, имя класса экземпляра можно узнать с помощью функции type() , либо используя атрибут __class__.__name__ . Оба способа эффективны и просты в использовании.

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

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