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

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

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

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

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

Комментировать
Решения вопроса 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

Как превратить свою аватарку в Telegram в часы?

  • 1 подписчик
  • 2 часа назад
  • 16 просмотров

python

  • Python
  • +2 ещё

Учу python. Реально ли найти работу в аналитике данных на 4-6 часов в день?

  • 1 подписчик
  • 2 часа назад
  • 18 просмотров

Основы Python — Функции и Объекты

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

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

Функции

Мы уже видели и использовали некоторые функции, такие как type() , str() , .append() , .keys() и range() . Но что такое функции на самом деле?

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

result = inputNumber + 2

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

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

Здесь значение переменной var , которое в данном случае равно 2 , передается функции addFunction , а затем доступно внутри этой функции через переменную inputNumber . Обратите внимание, что имена двух переменных var и inputNumber не обязательно должны совпадать. Когда значение передается функции, оно образует прямую связь между двумя наборами скобок, которые несут данные.

В этом случае var — это глобальная переменная, которая хранит значение 2 в основном скрипте, в то время как inputNumber — это локальная переменная, которая хранит это значение только на время выполнения этой функции. Таким образом, функции «сворачивают» конкретные задачи и все данные, необходимые для выполнения этой задачи, чтобы ограничить количество глобальных переменных, необходимых в основной функции.

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

return ‘Number must be positive!’

result = inputNumber + 2

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

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

def addTwoNumbers(inputNumber1, inputNumber2):

result = inputNumber1 + inputNumber2

print addTwoNumbers(2, 3)

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

def twoNumbers(inputNumber1, inputNumber2):

addition = inputNumber1 + inputNumber2

multiplication = inputNumber1 * inputNumber2

return [addition, multiplication]

result = twoNumbers(2, 3)

print ‘addition: ‘ + str(result[0])

print ‘multiplication: ‘ + str(result[1])

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

Объекты (Классы)

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

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

Давайте подумаем об этом в повседневной жизни. Для животного примером метода может быть «бег». Многие вещи могут работать, поэтому определение работы как функции было бы общим и не обязательно относилось бы к тому, кто выполняет работу. С другой стороны, примером класса может быть «собака», которая будет иметь экземпляр метода «бег», а также другие методы, связанные с тем, чтобы быть собакой, такие как «еда» и «лай». Он также будет иметь набор переменных для хранения информации о данной собаке, такой как ее возраст, порода или вес. Другим классом может быть «человек», который будет хранить различные переменные и будет иметь свою собственную версию методов, таких как «бег» и «еда» (но, надеюсь, не «лай»).

Давайте определим очень простой класс, чтобы увидеть, как он работает. Мы будем использовать пример счетчика, который будет хранить значение и увеличивать это значение на основе запросов пользователей:

def addToCounter(self, inputValue):

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

Как только мы создадим экземпляр класса (называемый «instantiation»), мы сможем запустить методы этого экземпляра и запросить его переменные. Обратите внимание, что общее определение класса — это только конструкция. Все переменные внутри класса применяются только к определенному экземпляру, и методы могут выполняться только в том случае, если они связаны с этим экземпляром. Например:

Сразу же вы заметите некоторые различия между тем, как мы определяем функции и классы. Во-первых, никакие переменные не передаются в первой строке определения, так как ключевое слово class определяет только общую структуру класса. После первой строки вы найдете список переменных, которые являются локальными переменными этого класса, и будете отслеживать данные для отдельных экземпляров. После этого у вас будет коллекция локальных методов (помните, что «методы» — это просто функции, принадлежащие определенному классу), которые определяют функциональность класса. Эти методы определяются так же, как и раньше, за исключением того, что вы видите, что первым вводом всегда является ключевое слово self . Это представляет экземпляр объекта и всегда передается как первый входной сигнал в каждый метод в классе. Это позволяет вам запрашивать локальные переменные экземпляра, как вы можете видеть, что мы делаем с переменной count .

Чтобы вызвать метод внутри класса, вы используете имя переменной, хранящей экземпляр, и используете точечную нотацию » . » для вызова метода. Точка — это в основном ваш путь в экземпляр, а также все его данные и функциональность. Мы уже видели эту точку раньше, например, когда вызывали функцию .append() в списке. Это потому, что список на самом деле является классом сам по себе! Когда вы определяете список, вы фактически создаете экземпляр класса list , который наследует все функциональные возможности этого класса (сумасшествие, правда?). На самом деле в Python существует только небольшая коллекция примитивных типов данных (ints, floats, booleans и некоторые другие), а все остальное определяется как классы в рамках ООП. Даже строки — это специальные классы, которые хранят коллекцию символов.

Кстати, можно также использовать синтаксис » . » для запроса локальных переменных экземпляра класса. Например, если мы хотим найти значение переменной count myCounter , мы можем просто задать его, набрав:

Однако это не рекомендуется, поскольку оно раскрывает конечному пользователю истинное имя локальных переменных. В производственной среде это создало бы серьезные риски для безопасности, но считается плохой практикой даже в частном использовании. Вместо этого рекомендуется создать специальные методы accessor для извлечения значений переменных из экземпляра, как это было сделано с помощью метода getCount() в нашем примере. Еще одно преимущество этой практики (которая называется инкапсуляцией) заключается в том, что код легче поддерживать. Вы можете вносить любые изменения в определение класса, включая изменение имен локальных переменных и того, что они делают. Пока вы поддерживаете функции доступа и они возвращают ожидаемый результат, вам не нужно ничего обновлять в основном коде.

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

Наконец, в приведенном выше примере каждый экземпляр, который мы делаем из CounterClass , будет запускать счетчик в 0 . Однако что делать, если мы хотим указать, каким должно быть это число, когда мы создаем экземпляр класса? Для этого мы можем реализовать метод __init__() (это два подчеркивания на каждой стороне ‘init’):

def __init__(self, inputValue):

def addToCounter(self, inputValue):

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

Представление переменных в языке 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 и далее).

Как передать функцию как параметр в Python?

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

def calculate(a, b, operation): result = operation(a, b) return result def sum(a, b): return a + b def multiply(a, b) return a * b calculate(2, 3, sum) # 5 calculate(3, 4, multiply) # 12 

Функции, которые могут принимать другие функции в качестве аргументов, также называются функциями высшего порядка. Яркий пример функции высшего порядка - функция map() , которая принимает аргументом функцию и итерируемую последовательность, и применяет функцию-параметр к каждому элементу последовательности.

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

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