Как передать класс в функцию python
Перейти к содержимому

Как передать класс в функцию python

  • автор:

Python: как передать атрибут класса из функции в функцию?

Через функцию choice узнается в какой пункт кинуть очко и передает в функцию pribafka. Но она только убавляет количество очков, и параметр персонажа не прибавляет. Через el/if внутри 2 функции всё работает, но многовато кода получается.

  • Вопрос задан более трёх лет назад
  • 3235 просмотров

Комментировать
Решения вопроса 1

Неудивительно. В функцию pribafka параметры передаются по значению. Внутри к параметру прибавляется единичка, но у нас копия в параметре, а не оригинал переменной! Потому ничего и не меняется. Надо так:
a = int(input(u»Пункт меню: «))
if a == 1:
self.sila = self.pribafka(self.sila)
elif a == 2:
self.lovkost= self.pribafka(self.lovkost)

def pribafka(self, param):
param += 1
self.score -= 1
print(u»прибавлено»)

Ответ написан более трёх лет назад
Нравится 3 2 комментария
Ещё забыл. В функции pribafka дописать внизу return param
Илья @FireGM Автор вопроса
Спасибо, все работает.
Ответы на вопрос 0
Ваш ответ на вопрос

Войдите, чтобы написать ответ

python

  • Python
  • +2 ещё

Отправка сообщения aiogram в произвольной функции без handler использовав существующий bot?

  • 1 подписчик
  • 9 минут назад
  • 4 просмотра

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

Представление переменных в языке Python сильно отличается от традиционных языков программирования: Pascal, C, Java. Любая сущность, с которой работает Python-программа, является объектом. Числа, строки, списки, множества являются объектами. Названия функций также являются объектами и в некотором смысле ничем не отличаются от чисел, кроме того, что у функций есть операции, нетипичные для чисел (например, вызов функции при помощи операции () ), а другие операции, наоборот, отсутствуют.

У каждого объекта есть свой тип (это также называется классом): int , str , set , function и т.д.

Переменные в свою очередь являются ссылками на объекты: в любой момент времени переменная связана с каким-то объектом в памяти. Если мы выполняем присваивание:

a = 5

то имя переменной 5 связывается с объектом типа int , в котором хранится значение 5.

Если теперь выполнить другое присваивание:

a = 'hello'

то старая связь разрывается, создается новый объект типа str , в котором хранится значение ‘hello’ , и переменная a связывается с этим объектом.

Если же теперь выполнить операцию:

def a(): pass

то в памяти создается объект типа function , хранящий тело функции из одной инструкции pass , и переменная a теперь связывается с этой функцией.

Если же справа от оператора = поставить имя другого объекта:

b = a

то второй объект связывается с тем же объектом, с которым был связан первый объект: теперь b и a становятся ссылками на один и тот же объект и являются неразличимыми, пока для одного из них связь не будет разорвана при помощи оператора =

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

a is b

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

a = 'a' * 1000 b = 'aa' * 500

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

a = 2 + 3 b = 1 + 4

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

Такое повторное использование применяется только для небольших чисел, строк и объектов типа bool , поскольку для больших объектов проверка того, что объект уже хранится в памяти требует много времени.

Создание новых типов данных

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

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

Можно использовать кортеж, один из элементов которого будет равен имени учащегося, другой элемент — его баллу. Такой подход лучше тем, что позволяет работать с представлением одного учащегося, как с единым целым, что экономит действия, например, при сортировке, и повышает наглядность программы.

В этом случае придется поставать так:

Vasya = ('Василий', 4) print('Имя:', Vasya[0]) print('Балл:', Vasya[1])

Но элементы кортежей не имеют собственных названий (то есть вовсе неочевидно, балл хранится именно в элементе кортежа с индексом 1). Удобней было бы обозначить атрибуты каждого объекта какими-то осмысленными идентификаторами и далее работать с этими идентификаторами. Пример:

class Person: pass Vasya = Person() Vasya.name = 'Василий' Vasya.score = 4

В этом примере мы объявляем новый класс объектов: Person . Затем мы создаем новый объект класса, при помощи вызова конструктора Person() , который возвращает ссылку на новый объект, и присваиваем ссылку на этот объект переменной Vasya , которая также называется экземпляром класса или объектом. Далее объекту Vasya устанавливается два атрибута: name (типа str ) и score (типа int ).

Поля и методы

Таким образом, объе ты классов представляют собой новые типы данный, объединяющие несколько атрибутов (полей). Атрибуты могут быть произвольными типами данных: числами, строками, списками, множествами, словарями, другими классами. Обращение к атрибуту какого-либо объекта осуществляется при помощи dot-нотации: имя_класса.имя_атрибута .

Помимо полей у классов бывают методы: функции, которые можно применять к экземплярам класса. Например, у списков есть метод sort . Вызов метода также осуществляется при помощи dot-нотации, например: A.sort() .

Можно рассматривать методы, как функции, у которых первым параметром является экземпляр класса. Методы так и объявляются: как функции внутри описания класса, первым параметром которой является экземпляр класса. По соглашению, эта ссылка должна называться self . Вот пример объявления класса Person и метода print , выводящего информацию о полях name и score :

class Person: def print(self): print(self.name, self.score)

Теперь для вызова метода print для объекта Vasya нужно вызвать Vasya.print() . При этом не нужно задавать первый параметр self : в качестве этого параметра автоматически будет передан объект, для которого был вызван метод.

Методы могут принимать дополнительные параметры, как и обычные функции. Эти параметры описываются после параметра self .

Стандартные методы

Наш метод print предполагает, что у объекта есть поля name и score , иначе он завершится с ошибкой. Хочется быть уверенным, что у любого объекта класса Person есть эти поля. Для этого проще всего создать эти поля при создании объекта, т.е. при вызове функции Person . Для этого можнзо использовать конструктор: метод, который автоматически вызывается при создании объекта. Конструктором является метод с именем __init__ :

class Person: def __init__(self): self.name = '' self.score = 0

При создании объекта функцией Person будет автоматически вызван конструктор __init__ (явно вызывать его не нужно), который полю name объекта, для которого он вызван, присвоит пустую строку, а полю score присвоит значение 0.

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

class Person: def __init__(self, name, score): self.name = name self.score = score

В данном случае мы используем одинаковые имена ( name , score ) для обозначения передаваемых параметров и полей класса. Это сделано для удобства — имена могут и различаться.

Теперь мы сможем создавать новый объект с заданными полями так: Person(‘Иванов’, 5) .

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

class Person: def __init__(self, name = '', score = 0): self.name = name self.score = score

Теперь мы можем вызывать конструктор как с параметрами ( Person(‘Иванов’, 5) ), так и без параметров ( Person() ), в последнем случае параметрам будут переданы значения “по умолчанию”, указанные в описании конструктора.

Есть и другие стандартные методы, которые можно определить в описании класса.

Метод __repr__ должен возвращать текстовую строку, содержащую код (на языке Python), создающую объект, равный данному. Естественно, метод __repr__ должен содержать вызов конструктора, которому передаются в качестве параметров все строки исходного объекта, то есть он должен возвращать строку вида «Person(‘Иванов’, 5)»

Пример метода __repr__ (для экономии места опустим описание конструктора __init__ ):

class Person: def __repr__(self): return "Person('" + self.name + "', " + self.score + ")"

Таким образом, метод __repr__ возвращает строку с описанием объекта, которое может быть воспринято итерпретатором языка Питон.

Метод __str__ возвращает строку, являющуюся описанием объекта в том виде, в котором его удобно будет воспринимать человеку. Здесь не нужно выводить имя конструктора, можно, например, просто вернуть строку с содержимым всех полей:

class Person: def __str__(self): return self.name + ' ' + str(self.score)

Метод __str__ будет вызываться, когда вызывается функция str от данного объекта, например, str(Vasya) . То есть создавая метод __str__ вы даете указание Питону, как преобразовывать данный объект к типу str .

Поскольку функция print использует именно функцию str для вывода объекта на экран, то определение метода __str__ позволит выводить объекты на экран удобным способом: при помощи print .

Переопределение стандартный операций

Рассмотрим класс Point (точка), используемый для представления точек (или радиус-векторов) на координатной плоскости. У точки два естественных поля-координаты: x и y . Если рассматривать точку как радиус-вектор, то хотелось бы определить для точек операцию + , чтобы точки можно было складывать столь же удобно, как и числа или строки. Например, чтобы можно было записать так:

A = Point(1, 2) B = Point(3, 4) C = A + B

Для этого необходимо перегрузить операцию + : определить функцию, которая будет использоваться, если операция + будет вызвана для объекта класса Point . Для этого нужно определить метод __add__ класса Point , у которого два параметра: неявная ссылка self на экземпляр класса, для которого она будет вызвана (это левый операнд операции + ) и явная ссылка other на правый операнд:

class Point: def __init__(self, x = 0, y = 0): self.x = x self.y = y def __add__(self, other): return Point(self.x + other.x, self.y + other.y)

Теперь при вызове оператора A + B Питон вызовет метод A.__add__(B) , то есть вызовет указанный метод, где self = A , other = B .

class Point: def __lt__(self, other): return self.x < other.x or self.x == other.x and self.y < other.y

В этом примере оператор вернет True , если у левого операнда поле x меньше, чем у правого операнда, а также если поля x у них равны, а поле y меньше у левого операнда.

Функции type и isinstance

Полезно, чтобы конструктор __init__ мог воспринимать параметры различных типов. Например, удобно инициализировать точку не только двумя числами, но и строкой, в которой через пробел записаны два числа (такая строка может быть считана со стандартного ввода), списком или кортежем. То есть передаваемые конструктору аргументы могут быть разного типа ( int , float , str , list , tuple ). Конструктор должен выполнять различные действия для параметров различного типа, для этого нужно уметь проверять принадлежность объекту какому-либо классу.

Эту задачу можно решить при помощи функций type и isinstance . Функция type возвращает класс, к которому принадлежит объект. Например:

if type(a) == int: print('a - целое число') elif type(a) == str: print('a - строка')

Для этого можно использовать функцию isinstance , у которой два параметра: объект и класс. Функция возращает True , если объект принадлежит классу или False в противном случае. Пример:

if isinstance(a, int): print('a - целое число') elif isinstance(a, str): print('a - строка')

Список возможных перегружаемых операторов

Следующая таблица взята из книги Саммерфильда (стр. 283 и далее).

Упражнения

Класс “Точка” (Point)

Создайте класс Point , определите для него конструктор, метод __str__ , необходимые арифметические операции. У класса Point два поля: x и y .

A: Самая дальняя точка

Программа получает на вход число N, далее координаты N точек. Выведите координаты точки, наиболее удаленной от начала координат.

Для решения этой задачи напишите метод dist , который возвращает расстояние от точки до начала координат.

2
1 2
2 3

B: Центр масс

Выведите координаты центра масс данного множества точек (учтите, что это —два действительных числа).

Для создания точки определите конструктор, который мог бы принимать на вход как два числа, так и строку. Таким образом, точку можно будет создавать как вызовом Point(x, y) , так и Point(input()) .

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

2
1 2
2 3
1.5 2.5

C: Диаметр множества

Выведите диаметр данного множества – максимальное расстояние между двумя данными точками.

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

3
1 1
1 0
0 0
1.4142135623731

D: Сортировка

Определите для точек операцию сравнения __lt__ , сравнивающую точки по значению расстояния от начала координат. Отсортируйте данные точки в порядке возрастания расстояния от начала координат.

3
1 0
-1 -1
0 0
0 0
1 0
-1 -1

E: Максимальный периметр

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

Для нахождения периметра треугольника напишите отдельную функцию Perimeter(A, B, C) .

4
0 0
0 1
1 0
1 1
3.41421356237309

F: Максимальная площадь

Среди данных точек найдите три точки, образующие треугольник с наибольшей площадью. Выведите данную площадь.

Для нахождения площади треугольника напишите отдельную функцию Area(A, B, C) .

4
0 0
0 1
1 0
1 1

Класс “Дробь” (Fraction)

Класс Fraction должен иметь два поля: числитель a и знаменатель b . Оба поля должны быть типа int .

  • Ни одного параметра (в этом случае дробь должна быть равна 0).
  • Один параметр типа Fraction .
  • Один параметр типа int .
  • Два параметра типа int .
  • Один параметр типа str , содержащий два целых числа, записанных либо через пробел, либо через дробную черту или одно целое число.

Необходимо определить метод __str__ , который выводит a/b в каноническом представлении, если же дробь является целым числом, то просто значение этого числа.

G: Сократите дробь

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

1 2
30/-9
8/4
3
1/2
-10/3
2
3

H: Конструкторы

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

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

exec(stdin.read())
a = Fraction()
b = Fraction(1)
c = Fraction(1, 3)
d = Fraction('14 35')
e = Fraction('-6/2')
f = Fraction(e)
print(a, b, c, d, e, f, sep='\n')
0
1
1/3
2/5
-3
-3

I: Сравнения

Определите методы __lt__ , __le__ , __gt__ , __ge__ , __eq__ , __ne__ . В качестве параметра other может выступать объект одного из трех типов: int , float , Fraction .

Тестирование проводится, как в предыдущей задаче.

J: Умножение

  • Вычислять произведение двух дробей.
  • Вычислять произведение дроби и int (результатом является дробь), а также int и дроби.
  • Вычислять произведение дроби и float (результатом является float ), а также float и дроби.
  • Умножать объект класса Fraction на Fraction или int .

Методы типа __imul__ , то есть переопределяющие операторы присваивания ( *= ) должны возвращать self .

K: Деление

Определите методы деления __truediv__ , __rtruediv__ , __itruediv__ .

L: Возведение в степень

Определите операции возведения в степень __pow__ , __rpow__ так, чтобы можно было возводить дроби в степень типа int , float , Fraction , числа типа int и float в степень Fraction . Операция возведения Fraction ** int возвращает Fraction , во всех остальных случаях возвращается float .

Определите операцию __ipow__ для возведения дроби в целочисленную степень.

M: Сложение

Определите операции __add__ , __iadd__ , __radd__ .

N: Вычитание

Определите операции __sub__ , __isub__ , __rsub__ .

O: Знаки

Определите операции __pos__ , __neg__ , __abs__ .

P: Преобразование типов

Определите операции __int__ (должна округлять вниз до ближайшего целого), __float__ , __round__ (должна возвращать значение типа float , можно использовать функцию round для величины типа float ).

[python]Экземпляр класса как параметр по умолчанию

А че нет, замыкания же. Только param инициализируется один раз, это надо помнить.

creepnee
( 28.09.10 00:11:22 MSD )

мочь то можно, но это рассадник багов.

Zubchick ★
( 28.09.10 00:14:12 MSD )
Ответ на: комментарий от Zubchick 28.09.10 00:14:12 MSD

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

swelf ★
( 28.09.10 00:24:04 MSD ) автор топика

Глобальная переменная, нэ? Ее хотя бы поменять можно.

baverman ★★★
( 28.09.10 04:36:43 MSD )

а не судьба вот так сделать:

[code] def f(param = null): if !a a = A() a.bla = 'bla' [/code]

питона не знаю, но, думаю, суть ясна

anonymous
( 28.09.10 08:01:51 MSD )
Ответ на: комментарий от swelf 28.09.10 00:24:04 MSD

true_admin ★★★★★
( 28.09.10 11:20:07 MSD )
Ответ на: комментарий от anonymous 28.09.10 08:01:51 MSD

анонимус прав на все 100. Так и делают когда хочется дефолтовым параметром передать, например пустой список. Но с объектом сложней, они могут иметь отличные от А() параметры. Вообще изменять объекты внутри функций не являющихся методами черевато, но никто не говорит, что этого нельзя делать.

Zubchick ★
( 28.09.10 11:29:42 MSD )
Ответ на: комментарий от true_admin 28.09.10 11:20:07 MSD

«Глобальный синглтон» в python - это модуль. Только и всего.

shylent ★
( 28.09.10 12:12:18 MSD )
Ответ на: комментарий от shylent 28.09.10 12:12:18 MSD

не обязательно. И там есть свои нюансы.

true_admin ★★★★★
( 28.09.10 13:02:34 MSD )
Ответ на: комментарий от true_admin 28.09.10 13:02:34 MSD

Эх, ну вы же понимаете, что про все что угодно можно сказать, что там «есть свои нюансы» без всяких объяснений.

Просто, в самом деле, наипростейший вариант реализации синглтона без всяких вывертов (тем более, что все эти выверты без проблем в python обходятся, благодаря его динамизму), это объект внутри модуля.

shylent ★
( 28.09.10 14:03:27 MSD )
Ответ на: комментарий от shylent 28.09.10 14:03:27 MSD

В моём понимании класс тоже может быть синглтоном и я бы загнал все глобальные переменные в класс(для красоты). Про нюанс я имел в виду что если сделать «from module import *», где * это простые типы данных типа int, str итп то работать не будет т.к. они при присвоении значения получится своя локальная копия переменной которая и будет изменена.

true_admin ★★★★★
( 28.09.10 14:32:50 MSD )
Ответ на: комментарий от true_admin 28.09.10 14:32:50 MSD

про синглтоны почитаю потом какнить, пока решил просто сделать функцию методом класса.

Как передать класс в функцию python

В прошлой статье класс Employee полностью перенимал функционал класса Person:

class Person: def __init__(self, name): self.__name = name # имя человека @property def name(self): return self.__name def display_info(self): print(f"Name: ") class Employee(Person): def work(self): print(f" works")

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

Например, изменим классы следующим образом:

class Person: def __init__(self, name): self.__name = name # имя человека @property def name(self): return self.__name def display_info(self): print(f"Name: ") class Employee(Person): def __init__(self, name, company): super().__init__(name) self.company = company def display_info(self): super().display_info() print(f"Company: ") def work(self): print(f" works") tom = Employee("Tom", "Microsoft") tom.display_info() # Name: Tom # Company: Microsoft

Здесь в классе Employee добавляется новый атрибут - self.company , который хранит компания работника. Соответственно метод __init__() принимает три параметра: второй для установки имени и третий для установки компании. Но если в базом классе определен конструктор с помощью метода __init__, и мы хотим в производном классе изменить логику конструктора, то в конструкторе производного класса мы должны вызвать конструктор базового класса. То есть в конструкторе Employee надо вызвать конструктор класса Person.

Для обращения к базовому классу используется выражение super() . Так, в конструкторе Employee выполняется вызов:

super().__init__(name)

Это выражение будет представлять вызов конструктора класса Person, в который передается имя работника. И это логично. Ведь имя работника устанавливается именно в конструкторе класса Person. В самом конструкторе Employee лишь устанавливаем свойство company.

Кроме того, в классе Employee переопределяется метод display_info() - в него добавляется вывод компании работника. Причем мы могли определить этот метод следующим образом:

def display_info(self): print(f"Name: ") print(f"Company: ")

Но тогда строка вывода имени повторяла бы код из класса Person. Если эта часть кода совпадает с методом из класса Person, то нет смысла повторяться, поэтому опять же с помощью выражения super() обращаемся к реализации метода display_info в классе Person:

def display_info(self): super().display_info() # обращение к методу display_info в классе Person print(f"Company: ")

Затем мы можем вызвать вызвать конструктор Employee для создания объекта этого класса и вызвать метод display_info:

tom = Employee("Tom", "Microsoft") tom.display_info()

Консольный вывод программы:

Name: Tom Company: Microsoft

Проверка типа объекта

При работе с объектами бывает необходимо в зависимости от их типа выполнить те или иные операции. И с помощью встроенной функции isinstance() мы можем проверить тип объекта. Эта функция принимает два параметра:

isinstance(object, type)

Первый параметр представляет объект, а второй - тип, на принадлежность к которому выполняется проверка. Если объект представляет указанный тип, то функция возвращает True. Например, возьмем следующую иерархию классов Person-Employee/Student:

class Person: def __init__(self, name): self.__name = name # имя человека @property def name(self): return self.__name def do_nothing(self): print(f" does nothing") # класс работника class Employee(Person): def work(self): print(f" works") # класс студента class Student(Person): def study(self): print(f" studies") def act(person): if isinstance(person, Student): person.study() elif isinstance(person, Employee): person.work() elif isinstance(person, Person): person.do_nothing() tom = Employee("Tom") bob = Student("Bob") sam = Person("Sam") act(tom) # Tom works act(bob) # Bob studies act(sam) # Sam does nothing

Здесь класс Employee определяет метод work(), а класс Student - метод study.

Здесь также определена функция act , которая проверяет с помощью функции isinstance , представляет ли параметр person определнный тип, и зависимости от результатов проверки обращается к определенному методу объекта.

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

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