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

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

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

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

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

ИмяКласса()

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

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

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

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

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

Класс как модуль

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

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

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

Класс как создатель объектов

Приведенный выше класс позволяет создавать объекты, но мы не можем применить к объекту метод adder():

>>> l = B()
>>> l.n
5
>>> l.adder(100)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: adder() takes 1 positional argument but 2 were given

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

На самом деле классы – это далеко не модули. Они идут дальше модулей и обладают своими особенностями. Класс создает объекты, которые в определенном смысле являются его наследниками. Это значит, что если у объекта нет собственного поля n, то интерпретатор ищет его уровнем выше, то есть в классе. Таким образом, если мы присваиваем объекту поле с таким же именем как в классе, то оно перекрывает, т. е. переопределяет, поле класса:

>>> l.n = 10
>>> l.n
10
>>> B.n
5

Здесь l.n и B.n – это разные переменные. Первая находится в пространстве имен объекта l. Вторая – в пространстве класса B. Если бы мы не добавили поле n к объекту l, то интерпретатор бы поднялся выше по дереву наследования и пришел бы в класс, где бы и нашел это поле.

Что касается методов, то они также наследуются объектами класса. В данном случае у объекта l нет своего собственного метода adder, значит, он ищется в классе B. Однако от класса B может быть порождено множество объектов. Методы же чаще всего предназначаются для обработки объектов. Таким образом, когда вызывается метод, в него надо передать конкретный объект, который он будет обрабатывать.

Понятно, что передаваемый экземпляр, это объект, к которому применяется метод. Выражение l.adder(100) выполняется интерпретатором следующим образом:

  1. Ищу атрибут adder() у объекта l. Не нахожу.

  2. Тогда иду искать в класс B, так как он создал объект l.

  3. Здесь нахожу искомый метод. Передаю ему объект, к которому этот метод надо применить, и аргумент, указанный в скобках.

Другими словами, выражение l.adder(100) преобразуется в выражение B.adder(l, 100).

Таким образом, интерпретатор попытался передать в метод adder() класса B два параметра – объект l и число 100. Но мы запрограммировали метод adder() так, что он принимает только один параметр. В Python, да и многих других языках, определения методов не предполагают принятие объекта как само собой подразумеваемое. Принимаемый объект надо указывать явно.

По соглашению в Python для ссылки на объект используется имя self. Вот так должен выглядеть метод adder(), если мы планируем вызывать его через объекты:

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

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

Протестируем обновленный метод:

>>> l = B()
>>> m = B()
>>> l.n = 10
>>> l.adder(3)
13
>>> m.adder(4)
9

Здесь от класса B создаются два объекта – l и m. Для объекта l заводится собственное поле n. Объект m, за неимением собственного, наследует n от класса B. Можно в этом убедиться, проверив соответствие:

>>> m.n is B.n
True
>>> l.n is B.n
False

В методе adder() выражение self.n – это обращение к свойству n, переданного объекта, и не важно, на каком уровне наследования оно будет найдено.

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

Изменение полей объекта

В Python объекту можно не только переопределять поля и методы, унаследованные от класса, также можно добавить новые, которых нет в классе:

>>> l.test = "hi"
>>> B.test
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'B' has no attribute 'test'
>>> l.test
'hi'

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

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

>>> class User:
...     def setName(self, n):
...             self.name = n
...     def getName(self):
...             try:
...                     return self.name
...             except:
...                     print("No name")
... 
>>> first = User()
>>> second = User()
>>> first.setName("Bob")
>>> first.getName()
'Bob'
>>> second.getName()
No name

Подобные методы в простонародье называют сеттерами (set – установить) и геттерами (get – получить).

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

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

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

Создано

Обновлено

Комментарии

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

Ответ на от Evg

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

#есть класс "собака"
class Dog:
    name = ""
 
#есть класс "человек"
class Human:
    # у каждого человека по умолчанию есть собака 
    # (конструктор автоматически присваивает человеку некую собаку)
    def __init__(self, dog):
        self.dog = dog
    # человек может дать собаке имя
    def name_the_dog(self):
        self.dog.name = "Jack"
        print(self.dog.name)
 
goodBoy = Dog() #создаем объект "собака"
bill = Human(goodBoy) #создаем объект "человек"
 
dogsName = bill.name_the_dog() # просим человека дать собаке имя

 

Ответ на от Evg

 

class Zerg:
    hp = 50
    dmg = 10
 
    def attack(self, enemy):
        enemy.hp -= self.dmg
 
class Protos:
    hp = 100
    dmg = 5
 
    def attack(self, enemy):
        enemy.hp -= self.dmg
 
zerling = Zerg()
zealot = Protos()
 
print(zerling.hp, zealot.hp)
zerling.attack(zealot)
print(zerling.hp, zealot.hp)
zealot.attack(zerling)
print(zerling.hp, zealot.hp)

 

Ответ на от Гость

НЕПОНЯТНО! Как это происходит?
zerling.attack(zealot)  ???
bill = Human(goodBoy)  ???
Описывается в статье один класс. А здесь уже класс в классе

Ответ на от Andrei

class luha():
         form = 'tolstaya'
class trenazhorny_zal:
         form = 'tolstaya'
         def hudeem(self,newform):
                  self.form = newform
                  return newform
luha1 = luha()
luha2 = trenazhorny_zal()
 
print('1 юха ', luha1.form)
print('2 юха ', luha2.form)
 
luha2.hudeem("Stroinaya")
luha1.form = luha2.form
 
print ('теперь 2 юха , после тренажерки ',luha2.form)
print ('теперь 1 юха тоже , после тренажерки ',luha1.form)

Ответ на от Andrei

Не совсем так. Есть экземпляр класса, это объект(zerling). Мы вызываем метод класса - zerling.attack, и передаем аргументом другой объект, являющийся экземпляром другого класса - zealot. В методе attack из передаваемого в качестве аргумента объекта (zealot) выдергивается атрибут zealot.hp и вычисления идут уже с ним.

Ответ на от Evg

class Unit:
    def makeHealth(self, value): 
        self.health = value 
    def makeAttack(enemy): 
        enemy.health -= 20 
 
you = Unit()
opponent = Unit()
you.makeHealth(100)
opponent.makeHealth(100)
 
while you.health > 0 and opponent.health > 0:
    n = int(input("Введите либо 1, либо 2: "))
    if n == 1:
        Unit.makeAttack(opponent)
        print(f'Вы бьете врага.')
        print(f'У вашего врага осталось {opponent.health} здоровья.')
        print()
    else:
        Unit.makeAttack(you)
        print(f'Враг бьет Вас.')
        print(f'У Вас осталось {you.health} здоровья.')
        print()
 
if you.health > opponent.health:
    print(f'Вы Победили!')
elif you.health < opponent.health:
    print(f'Вы Проиграли!')

Ответ на от Evg

Адепты ООП, подскажите - всё ли окей?
import random
 
class warrior:
    def __init__(self,name,hp,dmg):
        self.name = name
        self.hp = hp
        self.dmg = dmg
    def attack(self,enemy):
        enemy.hp -= self.dmg
        print(f"Юнит {self.name} атаковал юнита {enemy.name}. У юнита {enemy.name} осталось {enemy.hp} HP.")
 
def war(w1,w2):
    while(w1.hp>0 and w2.hp>0):
        r = random.random()
        if r >=0.5:
            w1.attack(w2)
        else:
            w2.attack(w1)
    else:
        if w1.hp<=0:
            print(f"Юнит {w2.name} одержал победу. У {w2.name} осталось {w2.hp} HP")
        if w2.hp<=0:
            print(f"Юнит {w1.name} одержал победу У {w1.name} осталось {w1.hp} HP")
 
 
 
ork = warrior('Орк',100,20)
human = warrior('Чел',100,20)
 
war(ork,human)

Надеюсь я всё правильно понял :)

class car:
    types = "liftback"
    label = "skoda"
    color = "black"
    def equipment(self):
       print ("наша машина до:", self.types,self.label,self.color)
    def equipment2(self):
       if self.color == "black":
           print ("машина не изменилась")
       else:
           print ("наша машина после:", self.types,self.label,self.color)    
 
 
class paintshop:
    colorshop = ["pink","yellow","white"]
    def catalog(self):
        print ("каталог оттенков цвета:", self.colorshop[:])
 
 
    def changecolor(self,newcolor):
        for i in self.colorshop:
             if i == newcolor:
                 car.color = newcolor
                 break
             else:
                 print("такого цвета нет!")
                 break
 
obj1 = car()
obj2 = paintshop()
 
 
obj1.equipment()
obj2.catalog()
 
a = input("введи цвет:")
 
obj2.changecolor(a) 
obj1.equipment2()

Суть, нужно создать объект-массив(одномерный/двумерный) пренадлежащий классу First, а с помощью метода объекта класса Second удалить строки с максимальным элементов массива.
Привожу в пример код:
Вопрос: правильно ли я понял концепцию ООП, реализовав данную программу?

import random
class First:
    res = [0]
    def createms(self, elementov, strok=0):
        x = int(input('vvedite min elem: '))
        y = int(input('vvedite max elem: '))
        if strok > 0 and elementov > 0:
            self.res = [[random.randint(x, y) for j in range(elementov)] for i in range(strok)]
        elif strok == 0 and elementov > 0:
            self.res = [random.randint(x, y) for i in range(elementov)]
        else:
            print("Massiv ne mojet bit` sozdan, dannie vvedeni ne korektno")
        return self.res
    def outms(self):
        if len(str(self.res[0]))>1:
            for i in range(len(self.res)):
                for j in range(len(self.res[i])):
                    print(self.res[i][j], end=' ')
                print()
        else:
            for i in range(len(self.res)):
                print(self.res[i], end=' ')
            print()
class Second:
    def isklmax(self,obj):
        mas = obj.res
        elemax = 0
        k = 0
        for i in range(len(obj.res)):
            if max(obj.res[i]) > elemax:
                elemax = max(obj.res[i])
        while k < len(obj.res):
            if elemax == max(obj.res[k]):
                del obj.res[k]
            else:
                k += 1
 
obj1 = First()
obj2 = Second()
obj1.createms(5,5)
obj1.outms()
obj2.isklmax(obj1)
print()
obj1.outms()

class A(object):
    p_A = 0
 
 
class B(object):
    @staticmethod
    def change_prop(obj, prop, val):
        setattr(obj, "p_A", val)
 
 
a = A()
b = B()
 
b.change_prop(a, "p_A", 100)
print(a.p_A)

А подскажите пожалуйста, как написать что-то вроде игры "танки", где бы можно было управлять каким-нибудь кружочком, и он бы стрелял нажатием кнопки "пробел". С помощью библиотеки "TKinter".

class Figure:
	fill_color = "white"
	name = ""
	def setName(self,name):
		self.name = name
	def showFigInfo(self):
		print "name =",self.name,
		print "fill_color =",self.fill_color
 
class Bucket:	
	color = "white"
	def pickColor(self,color):
		self.color = color	
	def fill(self,obj):
		obj.fill_color = self.color
 
 
buck = Bucket()
fig = Figure()
 
fig.name = "circle"
buck.color = "green"
 
buck.fill(fig)
fig.showFigInfo() # name = circle fill_color = green

Как-то так.
Второй пример:

class animal:                                 ##Создали класс животных. 
    types = "Dog"
    color = "Black"
    age =  "18"
    def feature(self):                        ##Функция вывода
        print(self.types,self.color,self.age)
 
a1 = animal()
a1.feature()
 
class new_animal:                             ##Новый класс 
    def new(self,types,color,age):            ##Создали функцию измениния характеристик.
        a1.types = types     
        a1.color = color
        a1.age = age
 
a2 = new_animal()                             ##Объявляем новый объект
new_types = input("Please input new type: ")  ##Присваиваем новый тип
new_color = input("Please input his color: ") ##Присваиваем новый цвет
new_age = input("Please input his age: ")     ##Присваиваем новый возраст
a2.new(new_types,new_color,new_age)           ##Присваиваем новые значение в метод new
a1.feature()                                  ## Смотрим результат

Ответ на от Вучань

только у тебя получается что объект второго класса (нью энимал) может изменить ТОЛЬКО ОДИН объект другого класса - его название должно быть именно а1.
а вот если ты добавишь в набор параметров неопределенное название изменяемого объекта (напр. obj) - то будет хорошо.

foo = "bar";
baz = "foz";
class new_animal:
     def new(self,obj,types,color,age):
         obj.types=types
         obj.color=color
         obj.age=age

А вообще - бессмыслица какая-то. Меняются параметры объекта абстрактным объектом.
Я вторым объектом у тебя сделал из класса Доктор. Этот доктор может производить операции над первым классом. Прости, но я кастрировал твою виртуальную собачку ((.
Конечно, для этого нужно прописать первому объекту свойство кастрированности.

class Rope:
    length = "long"
 
class Scissors:
    def cutrope(self, length, rope):
        rope.length = length
 
rope1 = Rope()
 
print("Rope length on start: " + rope1.length)
 
scissors1 = Scissors()
 
scissors1.cutrope("short", rope1)
 
print("Rope length on finish: " + rope1.length)

Только начал изучать пайтон, это мой первый ЯП. Подскажите, пожалуйста, кто разбирается, нормально ли всё...

class Fridge:
    temp = "cold"
    color = "white"
    def fridge_change(self, new_temp, new_color):
        self.temp = new_temp
        self.color = new_color
 
class Oven:
    temp = "hot"
    color = "black"
    def oven_change(self, new_temp, new_color, new_param):
        self.temp = new_temp
        self.color = new_color
 
fridgest = Fridge()
ovenest = Oven()
 
print(fridgest.temp)
print(fridgest.color)
print(ovenest.temp)
print(ovenest.color)
 
ovenest.new_param = fridgest.temp + " " + fridgest.color
print(ovenest.new_param)

Ответ на от Sergey

все нормально. Только непонятно, чего ты добился. Аттрибуты объекта ovenest как были, temp='hot',color='black'. Можешь это проверить, набрав
ovenest.temp или(и) ovenest.color.
Предпоследней строчкой ты установил нигде не работающую переменную new_param.
Судя по выбранным объектам, ты не усвоил принципов ООП. Как холодильник может изменить печь и наоборот?
Выбери два объекта так, чтобы один мог реально изменять другой.
Например,

класс Лес:
    количество деревьев(n)
класс Лесник 
    метод посадка новых деревьев 
Метод посадка(объект, количество)
          oбъект.n= oбъект.n+количество

Лесника будут звать Петя
А Лес будет Букингемский
Петя=Лесник()
Букингемкий=Лес()
Петя.посадка(Букингемский,50) - #видишь! переменная "объект" приняла конкретно объект "Букингемский". Методу Лесника по-барабану в какой объект "сажать". Лишь бы у того объекта был аттрибут n. Если, допустим, у класса Лесник окажется аттрибут-свойство n, то можно будет Пете посадить деревья в другого Лесника, и даже в себя))))

Рабочий пример

class Chelovek :
    sv1 = "Head"
    sv2 = "Hands"
    sv3 = "Legs"
    def changechel (self, newsv1) :
        self.sv1 = newsv1
        return self.sv1
class Robot :
    sv1 = "Metal Head"
    sv2 = "Metal Hands"
    sv3 = "Metal Legs"
chel = Chelovek()
rob = Robot()
print (chel.sv1)
chel.changechel("SuperHead")
print (chel.sv1)
print (rob.sv1)
rob.sv1 = chel.changechel("SuperMetalHeadRob")
print (rob.sv1)

class android1:
    """Robot1"""
    weapon="Базука"
    hand=2
    def chweapon(self,newweapon):
        self.weapon=newweapon
    def chhand(self,newhand):
        self.hand=newhand
 
class android2:
    """Robot2"""
    weapon="пукалка"
    hand=4
    def chweapon(self,newweapon):
        self.weapon=newweapon
 
    def chhand(self,newhand):
        self.hand=newhand
 
robot1=android1()
robot2=android2()
 
print ("Имя-",robot1.__doc__,"Оружие=",robot1.weapon,"Рук-",robot1.hand)
print ("Имя-",robot2.__doc__,"Оружие=",robot2.weapon,"Рук-",robot2.hand)
 
robot2.chweapon("стингер")
print ("Имя-",robot1.__doc__,"Оружие=",robot1.weapon,"Рук-",robot1.hand)
print ("Имя-",robot2.__doc__,"Оружие=",robot2.weapon,"Рук-",robot2.hand)
 
robot2.chweapon(robot1.weapon)
print ("Имя-",robot1.__doc__,"Оружие=",robot1.weapon,"Рук-",robot1.hand)
print ("Имя-",robot2.__doc__,"Оружие=",robot2.weapon,"Рук-",robot2.hand)

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

class snake(object):
    hungry = True
    catch_mouse = False
    poison = 0
 
    def set_poison(self, newpoison):
        self.poison = newpoison
 
    def catch(self, flag_catch)
        self.catch_mouse = flag_catch
        if self.catch_mouse:
            self.hungry = False
 
    def atack_poison(self, mouse):
        if self.hungry == False:
            while mouse.health != 0:
                self.poison = self.poison - 10 - mouse.move_speed
                mouse.health =  mouse.health - 10
                print("Health:", mouse.health, "    ", "Poison:",
					self.poison)
 
 
 
class mouse(object):
    health = 0
    move_speed = 1
 
    def set_health(self, newhealth):
        health = newhealth
 
 
 
 
python = snake()
micky = mouse()
 
python.set_poison(120)
micky.set_health(100)
 
python.catch(True)
python.atack_poison(micky)

class apple:
    color = 'red'
    form = 'sphere'
    taste = 'sour'
 
    def changecolor(self,newcolor):
        self.color = newcolor
    def changeform(self, newform):
        self.form = newform
    def changetaste(self, newtaste):
        self.taste = newtaste
apple1 = apple()
apple2 = apple()
 
answear = raw_input('How long is your apple been siting out?')
if answear > '7':
    answear == apple1
    print 'You can NOT eat that'
    apple1.changecolor('brown')
    apple1.changeform('pale')
    apple1.changetaste('like sh*t')
    print (apple1.color, apple1.form, apple1.taste)
 
elif answear <= '7':
    answear == apple2
    print 'You can eat that'
    apple2.changecolor('color is better than it ever been')
    apple2.changeform('form is better than it ever been')
    apple2.changetaste('taste is better than it ever been')

import random
 
class Warriror:
	heals = 100
 
	def hit(self, obj):
		if obj.heals != 0:	
			obj.heals -= 20
			if obj.heals == 0:
				print('Убит', end=' ') 
			return obj.heals	
		else:
			print('Он уже мертв')
 
 
war_1 = Warriror()
war_2 = Warriror()
while war_2.heals > 0 and war_1.heals > 0:
	count = random.random()
	if count > 0.5:
		print('war_2 ', war_1.hit(war_2))
	else:
		print('war_1 ', war_2.hit(war_1))

class warrior:
    hp = 100
    dmg = 20
    def hit(self, enemy):
        enemy.hp -= self.dmg
        print("-20, Left:", enemy.hp)
 
vatnik = warrior()
putin = warrior()
 
from random2 import random
 
while vatnik.hp > 0 and putin.hp > 0:
    a = random()
    if a > 0.5:
        print("Vatnik hits Putin")
        vatnik.hit(putin)
    else:
        print("Putin hits Vatnik")
        putin.hit(vatnik)
 
if vatnik.hp == 0:
    print("Putin wins, Vatnik has died")
else:
    print("Vatnik wins, Putin has died")

class warrior:
 
    def refill_health(self, amount_of_health):
        self.health = amount_of_health
 
    def attack(self, enemy):
        enemy.health -= randint(10, 25)
 
batman = warrior()
batman.refill_health(100)
 
superman = warrior()
superman.refill_health(100)
 
round_count = 0
 
while superman.health > 0 and batman.health > 0:
    superman.attack(batman)
    batman.attack(superman)
    round_count += 1
    print "Round {}: Batman's health is {}, Superman's health is {}".format(round_count, max(0,batman.health), max(0,superman.health))
 
print "Superman is winner" if superman.health > 0 else "Batman is winner"

import random
class Warrior:
    def __init__(self, name, value):
        self.health = value
        self.name = name
 
    def attack(self, enemy):
        enemy.health = enemy.health - 20
 
 
unit1 = Warrior("Ukraine", 100)
unit2 = Warrior("Russia", 100)
 
 
while unit1.health > 0 and unit2.health > 0:
    x = random.randint(0, 1+1)
    if x == 0:
        unit1.attack(unit2)
        print("{} attacked and {} left {} health".format(unit1.name, unit2.name, unit2.health))
    else:
        unit2.attack(unit1)
        print("{} attacked and {} left {} health".format(unit2.name, unit1.name, unit1.health))
 
if unit1.health == 0:
    print("Russia win")
else:
    print("Ukraine win")

from random import randint
from time import sleep
class Voin:
    def stats(self, name, hp, dmg):
        self.name = name
        self.hp = hp
        self.dmg = dmg
 
fedya = Voin()
vova = Voin()
fedya.stats("Fedya", 150, 15)
vova.stats("Vova", 125, 19)
 
Voices = ["Omg! what a kick!", "Please stop it!", "What about your mother!", "U will suffer, bitch!", "I am surrender!"]
 
def battle(voin1, voin2):
    print "The battle starts with {} and {}\n".format(voin1.name, voin2.name)
    sleep(2)
    print "{} stats:\nHP: {}\nDMG: {}\n".format(voin1.name, voin1.hp, voin1.dmg)
    sleep(2)
    print "{} stats:\nHP: {}\nDMG: {}\n".format(voin2.name, voin2.hp, voin2.dmg)
    sleep(2)
    flipe = randint(1, 2)
    print "Lets flip a coin\n Heads for {}\n Tails for {}".format(voin1.name, voin2.name)
    print "\n3...\n"
    sleep(1)
    print "2...\n"
    sleep(1)
    print "1...\n"
    sleep(1)
    if flipe == 1:
        print "Heads!!!{} win!".format(voin1.name)
    if flipe == 2:
        print "Tails!!!{} win!".format(voin2.name)
    sleep(1)
    print "\nThe battle begin!"
    while voin1.hp > 0 and voin2.hp > 0:
        if flipe == 1:
            voin2.hp -= voin1.dmg
            print "{} deal {} damage to {}".format(voin1.name, voin1.dmg, voin2.name)
            print '\n', Voices[randint(0, 4)], "\n"
            sleep(1)
            voin1.hp -= voin2.dmg
            print "{} deal {} damage to {}".format(voin2.name, voin2.dmg, voin1.name)
            print '\n', Voices[randint(0, 4)]
            sleep(1)
            print "\n{} hp: {}\n{} hp: {}\n".format(voin1.name, voin1.hp, voin2.name, voin2.hp,)
            sleep(2)
        if flipe == 2:
            voin1.hp -= voin2.dmg
            print "{} deal {} damage to {}".format(voin2.name, voin2.dmg, voin1.name)
            print '\n', Voices[randint(0, 4)], "\n"
            sleep(1)
            voin2.hp -= voin1.dmg
            print "{} deal {} damage to {}".format(voin1.name, voin1.dmg, voin2.name)
            print '\n', Voices[randint(0, 4)]
            sleep(1)
            print "\n{} hp: {}\n{} hp: {}\n".format(voin1.name, voin1.hp, voin2.name, voin2.hp, )
            sleep(2)
    if voin1.hp <= 0:
        print "{} win!".format(voin2.name)
    if voin2.hp <= 0:
        print "{} win!".format(voin1.name)
 
battle(fedya, vova)

Работает: (результат можна приукрасить как по заданию)
import random
class War:
    def sethp(self, h):
        self.general = h
    def damage (self, d):
        hurt = self.general - d
        self.general = hurt
        if hurt >0:
            return hurt
        else:
            print('игрок: ')
 
 
 
 
unit = War()
wiki = War()
unit.sethp(1000)     #gamer 1
wiki.sethp(1000)     #gamer 2
 
while (unit.general > 0) and (wiki.general >0):
    m = random.randint(1,2)
    if m == 1:
        wiki.damage(20)
        print ('wiki потерпел урон, Health: ', wiki.general)
    else:
        unit.damage(20)
        print('unit потерпел урон, Health: ', unit.general)
input('press any key for ending!')
 
#инфа о здоровье: unit.general