Наследование

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

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

Простое наследование методов родительского класса

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

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

class Table:
    def __init__(self, l, w, h):
        self.lenght = l
        self.width = w
        self.height = h
 
class KitchenTable(Table):
    def setPlaces(self, p):
        self.places = p
 
class DeskTable(Table):
    def square(self):
        return self.width * self.length

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

>>> from test import *
>>> t1 = KitchenTable()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() missing 3 required positional arguments: 'l', 'w', and 'h'
>>> t1 = KitchenTable(2, 2, 0.7)
>>> t2 = DeskTable(1.5, 0.8, 0.75)
>>> t3 = KitchenTable(1, 1.2, 0.8)

Несомненно можно создавать столы и от родительского класса Table. Однако он не будет, согласно неким родственным связям, иметь доступ к методам setPlaces() и square(). Точно также как объект класса KitchenTable не имеет доступа к единоличным атрибутам сестринского класса DeskTable.

>>> t4 = Table(1, 1, 0.5)
>>> t2.square()
1.2000000000000002
>>> t4.square()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Table' object has no attribute 'square'
>>> t3.square()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'KitchenTable' object has no attribute 'square'

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

Полное переопределение метода надкласса

Что если в подклассе нам не подходит код метода его надкласса. Допустим, мы вводим еще один класс столов, который является дочерним по отношению к DeskTable. Пусть это будут компьютерные столы, при вычислении рабочей поверхности которых надо отнимать заданную величину. Имеет смысл внести в этот новый подкласс его собственный метод square():

class ComputerTable(DeskTable):
    def square(self, e):
        return self.width * self.length - e

При создании объекта типа ComputerTable по-прежнему требуется указывать параметры, так как интерпретатор в поисках конструктора пойдет по дереву наследования сначала в родителя, а потом в прародителя и найдет там метод __init__().

Однако когда будет вызываться метод square(), то поскольку он будет обнаружен в самом ComputerTable, то метод square() из DeskTable останется невидимым, т. е. для объектов класса ComputerTable он окажется переопределенным.

>>> from test import ComputerTable
>>> ct = ComputerTable(2, 1, 1)
>>> ct.square(0.3)
1.7

Дополнение, оно же расширение, метода

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

class ComputerTable(DeskTable):
    def square(self, e):
        return DeskTable.square(self) - e 

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

Рассмотрим другой пример. Допустим, в классе KitchenTable нам не нужен метод, поле places должно устанавливаться при создании объекта в конструкторе. В классе можно создать собсвенный конструктор с чистого листа, чем переопределить родительский:

class KitchenTable(Table):
    def __init__(self, l, w, h, p):
        self.length = l
        self.width = w
        self.height = h
        self.places = p

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

class KitchenTable(Table):
    def __init__(self, l, w, h, p):
        Table.__init__(self, l, w, h)
        self.places = p

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

>>> tk = KitchenTable(2, 1.5, 0.7, 10)
>>> tk.places
10
>>> tk.width 
1.5

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

Разработайте программу по следующему описанию.

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

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

Измеряется длина списков солдат противоборствующих команд и выводится на экран. У героя, принадлежащего команде с более длинным списком, поднимается уровень.

Отправьте одного из солдат первого героя следовать за ним. Выведите на экран идентификационные номера этих двух юнитов.

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

Комментарии

class Solder:
	def __init__(self, number, team):
		self.team = team
		self.number = number
 
	def to_hero(self, hero):
		return hero.number, self.number
 
class Hero:
	def __init__(self, number, team_number, team): # Если сдесь обьявить team=[] то почему то у нас список не сбрасывается для каждого отдельного экземпляра.
		self.team_number = team_number
		self.number = number
		self.lvl = 0
		self.team = team
 
	def up_lvl(self):
		self.lvl += 1
 
	def add_solder(self, solder):
		return self.team.append(solder)
 
	def look_count_team(self):
		return len(self.team)
 
hero_team_first = Hero(1, 1, [])
hero_team_second = Hero(2, 2, [])
count = 0
while count < 10:
	team_for_solder = random.randrange(1, 3)
	number_for_solder = random.random()
	if team_for_solder == 1:
		hero_team_first.add_solder(Solder(number_for_solder, team_for_solder))
	else:
		hero_team_second.add_solder(Solder(number_for_solder, team_for_solder))
	count += 1	
 
 
 
if hero_team_first.look_count_team() > hero_team_second.look_count_team():
	hero_team_first.up_lvl()
else:
	hero_team_second.up_lvl()
 
print(hero_team_first.team[0].to_hero(hero_team_first))

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

Второй вопрос как мне сделать метод to_here() без дополнительных аргументов, чтоб он следовал за героем к которому он принадлежит?

Ответ на от Iliner

class Hero:
    def __init__(self, number, team_number, team=[]):
        self.team_number = team_number
        self.number = number
        self.lvl = 0
        self.team = team
 
first = Hero(1, 1)
second = Hero(2, 2)
first.team.append('ben')
second.team.append('ken')
print(first.team, second.team)
print(first.team is second.team)

Результат:

['ben', 'ken'] ['ben', 'ken']
True

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

Когда аргумент не передается в метод, а используется список [] по-умолчанию, то получается, что он создается единожды. Ссылка на него присваивается переменной team. Далее ссылка на этот же список присваивается переменной self.team. Когда создается новый объект, то новая локальная переменная team связывается с уже существующим списком, который уже не пуст.

Второй вопрос. Если ходите избавиться от аргумента hero, то надо в каком-то поле сохранить героя команды например в методе __init__. Получится длиннее, чем с аргументом hero в методе to_hero.

Уникальный идентификатор объектов (number) реализован не совсем верно. Тема про наследование. По-идее общие атрибуты выносятся в общий надкласс.

Ответ на от plustilino

import random
 
class Character:
    uniq_number = 0
    def __init__(self, number_team):
        self.uniq_number = number_team
        Character.uniq_number += 1
        self.uniq_number = Character.uniq_number
 
class Hero(Character):
    def __init__(self, number_tean):
        super(Hero, self).__init__(number_tean)
        self.lvl = 0
        self.team_list = []
 
    def up_lvl(self):
        self.lvl += 1
 
    def add_solder(self, unit):
        self.team_list.append(unit)
 
    def len_team(self):
        return len(self.team_list)
 
class Solder(Character):
 
    def to_hero(self, hero):
        return "Уникальный номер солдата: {}. Уникальный номер Героя: {} ".format(self.uniq_number, hero.uniq_number)
 
first_hero = Hero(1)
second_hero = Hero(2)
count = 1
while count < 10:
    number_team = random.randrange(1, 3)
    if number_team == 1:
        first_hero.add_solder(Solder(number_team))
    else:
        second_hero.add_solder(Solder(number_team))
    count += 1
 
print(first_hero.len_team(), second_hero.len_team())
if first_hero.len_team() > second_hero.len_team():
    first_hero.up_lvl()
else:
    second_hero.up_lvl()
print(first_hero.team_list[0].to_hero(first_hero))

Только не кидайтесь помидорами)) случился такой затык
import random
class unit:
    def __init__(self, team = random.randint(1, 2)):
        self.numb = id(self)
        self.team = team
 
class soldier(unit):
    def go_with_hero(hero):
        pass
class hero(unit):
    def level_up():
        pass
 
Super = hero(1)
Puper = hero(2)
Super_team = []
Puper_team = []
for i in range(int(input("Введите количество солдат: ")):
    i = soldier()
    if i.team == 1:
        Super_team.append(i)
    else:
        Puper_team.append(i)
при проверке длины списков всем солдатам присвоен один и тот же номер команды, либо 1 либо 2.

Вопрос почему не рандомно каждому солдату?

Ответ на от Илья

Видимо выражение random.randint(1, 2) при использовании его в качестве аргумента метода вычисляется единожды, когда интерпретатор первый раз читает класс. Это происходит до вызова класса. Потом когда создаются объекты используется вычисленное значение. Т. е. на момент вызова имеем например def __init__(self, team=1). Когда создаются герои, то значение тим заменяется на переданное. Когда ничего не передается (в случае солдат), то берется по умолчанию, т. е. то которое было вычислено на момент "компиляции" класса.

Ответ на от plustilino

Спасибо за версию. На след день немного переосмыслил код и всё получилось:
import random
import time
class unit:
    def __init__(self):
        self.numb = id(self)
        self.team = random.randint(1,2)
 
class soldier(unit):
    def go_with_hero(hero):
        pass
class hero(unit):
    def __init__(self, team):
        self.team = team
    def level_up():
        pass
 
Super = hero(1)
Puper = hero(2)
Super_team = []
Puper_team = []
for i in range(int(input("Введите количество солдат: "))):
    i = soldier()
    if i.team == 1:
        Super_team.append(i)
    else:
        Puper_team.append(i)
Вывод - во всех непонятных случаях - ложитесь спать)))

Утро вечера мудренее))

Ответ на от Илья

Мой Вариант)))
import random
class unit:
    def __init__(self):
        self.numb = id(self)
        self.team = random.randint(1,2)
 
class soldier(unit):
    def go_with_hero(self, hero = 0):
        return(id(hero), id(self))
class hero(unit):
    lvl = 0
    def __init__(self, team):
        unit.__init__(self)
        self.team = team
    def level_up(self):
        self.lvl += 1
Super = hero(1)
Puper = hero(2)
Super_team = []
Puper_team = []
for i in range(int(input("Введите количество солдат: "))):
    i = soldier()
    if i.team == 1:
        Super_team.append(i)
    else:
        Puper_team.append(i)
if len(Super_team) > len(Puper_team):
    Super.level_up()
elif len(Super_team) < len(Puper_team):
    Puper.level_up()
else:
    print("Герои имеют равные силы!!")
legend_team = Super_team[0].go_with_hero(Super)
print("Уровен героя Super - {}\nУровен героя Puper - {}".format(Super.lvl, Puper.lvl))
print("Два великих воина - {} и {} отправилис навстречу подвигам!!!".format(Super_team[0].go_with_hero(Super)[0],Super_team[0].go_with_hero(Super)[1]))   

import random
 
class unit:
    def __init__(self, id, team):
        self.id = id
        self.team = team
 
class soldier(unit):
    def follow(self, hero):
        print "Soldier with ID {} is following Hero with ID {}".format(self.id, hero.id)
 
class hero(unit):
    def __init__(self, id, team):
        unit.__init__(self, id, team)
        self.level = 0
 
    def levelup(self, team_size):
        self.level = team_size / 10
 
teams = ['good_team', 'bad_team']
gandalf = hero(1, teams[0])
sauron = hero(2, teams[1])
 
soldiers = []
good_team = [gandalf]
bad_team = [sauron]
 
for i in range(1,1000):
    soldiers.append(soldier(i, random.choice(teams)))
 
for warrior in soldiers:
    if warrior.team == 'good_team':
        good_team.append(warrior)
    else:
        bad_team.append(warrior)
 
print "Good team size: {}, Bad team size: {}".format(len(good_team), len(bad_team))
 
gandalf.levelup(len(good_team))
sauron.levelup(len(bad_team))
 
print "Gandalf lvl {}, Sauron lvl {}".format(gandalf.level, sauron.level)
 
random.choice(good_team).follow(gandalf)

import random
class unit: #создаем родительский клас юнитов с атрибутами: номер и команда
    def __init__(self, n,team):
        self.n=n
        self.team=team
class heroes(unit): # создаем клас героев и добавляем атрибут уровень
    def __init__(self,name,n,team,level=0):
        self.name=name
        self.level = level
        self.team=team
    def level_up(self, incr): # создаем метод повышения уровня, который принимает аргумент "на сколько повышен уровень"
        self.level+=incr
 
class soldier(unit): # создаем клас солдата с методом следовать за героем
    def follow_heroes(self,heroes):
        print("\n"+"Для охраны своих владений герой {} выбрал опытного воина № {} и отправился с ним в поход.".format(heroes.name, self.n))
Tristan,Merlin=heroes("Tristan",1,"red"),heroes("Merlin",2,"green") # создаем два объекта героя
red_team=[] #создаем
green_team=[] #две коменды
quantity=int(input("Количество воинов:"))+1 # пользовательский ввод обозначает общее количество воинов
for i in range(1,quantity): #создам воинов и распределяем между командами героев
    t=random.randint(0,1)
    i=soldier(i,t)
    if i.team==0:
        red_team.append(i)
        i.team="red"
    else:
        green_team.append(i)
        i.team="green"
if len(red_team)>len(green_team): # определяем уровень героя в зависимости от того у кого больше солдат
    Tristan.level_up(1)
elif len(red_team)<len(green_team):
    Merlin.level_up(1)
if len(red_team) >= 10:
    Tristan.level_up(len(red_team) // 5)
if len(green_team)>=10:
    Merlin.level_up(len(green_team)//5)
 
# выводим на экран списки команд
print("В войске героя по имени",Tristan.name+",",str(Tristan.level)+"-го уровня,",len(red_team),"знатных воинов!")
for i in red_team:
    print(i.n, i.team, end=", ")
print("\n"+"В войске героя по имени",Merlin.name+",",str(Merlin.level)+"-го уровня,",len(green_team),"знатных воинов!")
for i in green_team:
    print(i.n,i.team, end=", ")
#выводим случайное имя героя и номер одного из солдат
who=random.randint(0,1)
if who==1:
    t=red_team
    h=Tristan
else:
    t=green_team
    h=Merlin
random.choice(t).follow_heroes(h)

import random
 
class Person:
    def __init__(self, team_id):
        self.id = id(self)
        self.team_id = team_id
    #def __del__(self):
    #    print(f"Я ({self.id}) из команды {self.team_id} покидаю Вас, до свидания.")
 
class Hero(Person):
    level = 0
    def level_up(self, delta = 0):
        if delta:
            self.level += delta
        else:
            self.level += 1
        print(f"Я герой ({self.id}) из команды {self.team_id}, мой уровень = {self.level}")
 
class Solder(Person):
    number = -1
    def set_number(self, number):
        self.number = number
    def to_hero(self, hero):
        self.hero = hero
    def folow_to_hero(self):
        try:
            print(f"Я {self.number} солдат ({self.id}) из команды {self.team_id} следую за героем ({self.hero.id}).")
        except AttributeError:
            print(f"Я {self.number} солдат ({self.id}) из команды {self.team_id} не имею героя.")
 
teams = []
 
teams_cnt = 2
for i in range(teams_cnt):
    teams.append([[], Hero(i+1)])
 
solder_cnt = 100
for i in range(solder_cnt):
    t = random.randint(1,teams_cnt)
    s = Solder(t)
    s.to_hero(teams[t-1][1])
    s.set_number(len(teams[t-1][0])+1)
    teams[t-1][0].append(s)
 
teams_sol_cnt = []
for i in range(teams_cnt):
    print(f"В команде {i+1}: {len(teams[i][0])} солдат(а)")
    teams_sol_cnt.append(len(teams[i][0]))
 
team_max = max(teams_sol_cnt)
for i in range(len(teams_sol_cnt)):
    if team_max == teams_sol_cnt[i]:
        teams[i][1].level_up()
 
rnd_t = random.randint(1,teams_cnt)-1
teams[rnd_t][0][random.randint(1,teams_sol_cnt[rnd_t])-1].folow_to_hero()
 
teams.clear()

import itertools
import random
 
class Unit:
  new_id = itertools.count()
  def __init__(self, command):
    self.id = next(self.new_id)
    self.command = command
 
class Soldier(Unit):
  def __init__(self, command):
    Unit.__init__(self, command)
 
  def move_to_hero(self, Hero):
    print('Moving for hero -', Hero.name, sep=' ')
 
 
class Hero(Unit):
  def __init__(self, command, name):
    Unit.__init__(self, command)
    self.name = name
    self.level = 1
 
  def level_up(self):
    self.level += 1
    print(self.name,', level up!')
 
commands = ['British', 'Norway']
 
h1 = Hero(commands[0], 'Olaf')
h2 = Hero(commands[1], 'Henrich')
 
british_army = []
norway_army = []
 
 
print(range(random.randint(14, 16), random.randint(30, 32)))
 
for x in range(random.randint(14, 16), random.randint(30, 32)):
  random_nations = random.choice(commands)
  if random_nations == 'British':
    british_army.append(Soldier(random_nations))
  else: 
    norway_army.append(Soldier(random_nations))
 
print('British - ',len(british_army))
 
print('Norway - ',len(norway_army))
 
if len(british_army) > len(norway_army):
  h2.level_up()
else:
  h1.level_up()
 
random_soldier = random.choice(british_army)
 
random_soldier.move_to_hero(h1)

from random import choice
 
class Unit:
    new_id = 0
    def __init__(self):
        self.id = Unit.new_id
        Unit.new_id += 1
 
 
class Soldier(Unit):
    def __init__(self):
        Unit.__init__(self)
    def move_to_hero(self, hero):
        print('Moving for hero -', hero.id)
 
 
class Hero(Unit):
    def __init__(self):
        Unit.__init__(self)
        self.level = 1
    def level_up(self):
        self.level += 1
        print(f'Level up!\nYour lvl: {self.level}')
 
COUNT_SOLDIER = 10
 
class Command:
    def __init__(self, name, hero):
        self.name = name
        self.hero = hero
        self.soldiers = []
    def add_soldier(self, soldier):
        self.soldiers.append(soldier)
    @property
    def count_soldier(self):
        return len(self.soldiers)
 
def loop(command_names):
    commands = []
    for name in command_names:
        new_team = Command(name, Hero())
        commands.append(new_team)
 
    i = 0
    while i < COUNT_SOLDIER:
        choice(commands).add_soldier(Soldier())
        i += 1
 
    for command in commands:
        count_soldier = command.count_soldier
        if count_soldier == 0:
            count_soldier = 'no soldiers'
        elif count_soldier < 2:
            count_soldier = str(count_soldier) + ' soldier'
        else:
            count_soldier = str(count_soldier) + ' soldiers'
 
        print(f'In command {command.name}: {count_soldier}')
 
    command_win = commands[0]
 
    for command in commands[1:]:
        count_soldiers = command.count_soldier
        if count_soldiers > command_win.count_soldier:
            command_win = command
 
    command_win.hero.level_up()
 
    if commands[0].count_soldier:
        hero_1 = commands[0].hero
        soldier_1 = commands[0].soldiers[0]
        soldier_1.move_to_hero(hero_1)
        print(f'Hero id: {hero_1.id}. Soldier id: {soldier_1.id}')
 
 
loop(['red', 'blue', 'green'])

import random
class war:
    def __init__(self, i, a):
        self.id = i
        self.army = a
 
 
class solder(war):
    def __init__(self, i, a):
        war.__init__(self, i, a)
 
    def myindex(self, nil):
        self.index = nil
 
    def randunit(self, rnd):
        self.runit = rnd
 
    def follow(self, hero):
        self.myhero = hero
        print (f'я солдат этой дивизии под номером {self.index}, мой id:{self.runit} иду за капитаном id: {self.myhero}') 
 
 
class hero(war):
    def __init__(self, i, a):
        war.__init__(self, i, a)
    def up(self, level=50):
        print (f'а я командор id: {self.id}, мой уровень {level+50}!')
 
air = []
land = []
sea = []
 
#механизм уникального числа)
num = list(range(507))
i = 0
for e in num:
    num[i] = random.random()
    num[i] = str(num[e])
    i+=1
 
idx = 0
for st in num:
    num[idx] = st[2:]
    idx+=1
 
Gregory = hero(num[0], 'air')
air.append(Gregory)
Donnie = hero(num[1], 'land')
land.append(Donnie)
Bilco = hero(num[2], 'sea')
sea.append(Bilco)
 
un = 3
s = 0
while s < 504:
    rand = random.randint(1,3)
    if rand == 1:
        newbie = solder(num[un], 'воздух')
        air.append(newbie)
        un+=1
        s+=1
    elif rand == 2:
        newbie = solder(num[un], 'земля')
        land.append(newbie)
        un+=1
        s+=1
    elif rand == 3:
        newbie = solder(num[un], 'вода')
        sea.append(newbie)
        un+=1
        s+=1
 
 
A = len(air)
L = len(land)
S = len(sea)
 
print (f'воздух - {A}, земля {L}, вода {S}')
 
if (A>L) and (A>S):
    print(f'силы воздушные победили!')
    rs = random.randint(1, A)
    newbie.myindex(rs)
    newbie.randunit(air[rs].id)
    newbie.follow(Gregory.id)
    Gregory.up()
if (L>A) and (L>S):
    print(f'силы сухопутные победили!')
    rs = random.randint(1, L)
    newbie.myindex(rs)
    newbie.randunit(land[rs].id)
    newbie.follow(Donnie.id)
    Donnie.up()
if (S>L) and (S>A):
    print(f'силы морские победили!')
    rs = random.randint(1, S)
    newbie.myindex(rs)
    newbie.randunit(sea[rs].id)
    newbie.follow(Bilco.id)
    Bilco.up()
 
input('')

Домашнее задание.
import random
 
class Solder(object):
    def __init__(self, number, team):
        self.number = number
        self.team = team
 
    def follow_the_hero(self, hero):
        print("Солдат под номером", self.number, "следует за героем", hero.number)
 
class Hero(Solder):
    def __init__(self, number, team, level):
        Solder.__init__(self, number, team)
        self.level = level
 
    def growth_level(self):
        self.level += 1
        print("У героя", self.number, "повысился уровень")
 
 
s1=[]
s2=[]
h1 = Hero(1, 1, 0)
h2 = Hero(2, 2, 0)
n=2
while n < 22:
    s = Solder(n+1, random.randint(1,2))
    n += 1
    if s.team == 1:
        s1.append(s) 
    elif s.team == 2:
        s2.append(s) 
print("Количество солдат героя", h1.number, "составляет", len(s1),\
      "\nКоличество солдат героя",h2.number, "составляет", len(s2))
if len(s1)>=len(s2):
    h1.growth_level()
    s = random.choice(s1)
    s.follow_the_hero(h1)
else:
    h2.growth_level()
    s = random.choice(s2)
    s.follow_the_hero(h2)

from random import randrange as rnd
 
 
class Human:
    """Общий класс Человек"""
    def __init__(self, name):
        self._uin = rnd(100000, 999999)  # Уникальный номер
        self.name = name  # Имя
 
    def __repr__(self):
        return f"{self.name}-{self.get_uin()}"
 
    def get_uin(self):  # Возвращает уникальный номер
        return self._uin
 
 
class Hero(Human):
    """Класс Герой"""
    lvl_max = 80
 
    def __init__(self, name, team_n, lvl=1):
        super().__init__(name)
        self.team = team_n  # Принадлежность к команде
        if lvl > self.lvl_max:
            lvl = self.lvl_max
        if lvl <= 0:
            lvl = 1
        self.lvl = lvl  # Уровень героя
 
    def __repr__(self):
        return f"Герой: " + super().__repr__() + f" уровень={self.lvl}, команда={self.team}"
 
    def lvl_up(self):  # Метод увеличения уровня героя
        self.lvl += int(self.lvl < self.lvl_max)
 
 
class Soldier(Human):
    """Класс Солдат"""
    def __init__(self, name, team_n):
        super().__init__(name)
        self.team = team_n  # Принадлежность к команде
 
    def __repr__(self):
        return "Солдат: " + super().__repr__()
 
    def follow_hero(self, hero):
        if isinstance(hero, Hero):
            print(f"Я, солдат {self.name}, следую за героем {hero.name}!")
        else:
            print(f"Я, солдат {self.name}, не знаю такого героя.")
 
 
human_name = ["John", "Peter", "Michael", "Jim", "Paul", "Alex"]
team_name = ["красные", "зеленые"]
sold_max = 9  # Максимальное количество солдат в команде
team = dict()
for i in team_name:  # Формируем команды
    team[i] = [Hero(human_name[rnd(len(human_name))], i), []]  # Герои
    for j in range(rnd(1, sold_max+1)):
        team[i][1].append(Soldier(human_name[rnd(len(human_name))], i))  # Солдаты
team_winner_name = None  # Имя команды-победителя
team_winner_len = 0  # Количество солдат в команде-победителе
for i in team_name:  # Выводим длину команд и вычисляем победителя
    x = len(team[i][1])
    print(f"Команда {i} состоит из {x} солдат")
    if x > team_winner_len:
        team_winner_name = i
        team_winner_len = x
print(f"Команда победитель - {team_winner_name}")
team[team_winner_name][0].lvl_up()  # Герой команды-победителя поднимает уровень
num_follow_sold = rnd(team_winner_len)  # Случайный солдат из команды-победителя...
team[team_winner_name][1][num_follow_sold].follow_hero(team[team_winner_name][0])  # ...следует за своим героем
print(team[team_winner_name][0])  # Выводим данные на героя команды-победителя
print(team[team_winner_name][1][num_follow_sold])  # Выводим данные на солдата, который следует за своим героем

from random import randint
 
class Charet:
    def __init__(self, id, fraction):
        self.id = id
        self.fraction = fraction
        if self.fraction == 0:
            del self.fraction
            self.fraction = 'Норки'
        else:
            del self.fraction
            self.fraction = 'Горноры'
 
class Hero(Charet):
    def __init__(self, id, fraction, level = 1):
        Charet.__init__(self, id, fraction)
        self.level = level
 
    def levelUp(self):
        self.level += 1
 
 
 
class Solider(Charet):
    def goHero(self, hero):
        print("{0} Идёт за героем {1} ".format(self.id, hero.id))
 
hero_comand1 = Hero(1, 0)
hero_comand2 = Hero(2, 1)
 
list1 = []
list2 = []
 
i = 0
 
while i < 30:
    sold = Solider(i+3, randint(0,1))
    if sold.fraction == 'Норки':
        list1.append(sold)
        i += 1
    else:
        list2.append(sold)
        i += 1
 
 
print("Команда 1 {0} человек ".format(len(list1)))
print("Команда 2 {0} человек ".format(len(list2)))
 
if len(list1) > len(list2):
    hero_comand1.levelUp()
else:
    hero_comand2.levelUp()
 
list1[4].goHero(hero_comand1)
list2[4].goHero(hero_comand2)

import uuid
import random
 
 
teams = ['blue', 'red']
 
 
class Unit:
    def __init__(self, team):
        self.team = team
        self.id = uuid.uuid1()
 
class Hero(Unit):
    def __init__(self, team):
        Unit.__init__(self, team)
        self.level = 1
 
    def level_up(self):
        self.level += 1
 
 
class Soldier(Unit):
    def __init__(self, team):
        Unit.__init__(self, team)
        self.hero = None
 
    def follow_the_hero(self, hero):
        self.hero = hero
 
 
red_hero = Hero('red')
blue_hero = Hero('blue')
 
red_team = []
blue_team = []
 
for soldier in range(20):
    s = Soldier(random.choice(teams))
    red_team.append(s) if s.team == 'red' else blue_team.append(s)
 
red_hero.level_up() if len(red_team) > len(blue_team) else blue_hero.level_up()
print(f'red team: {len(red_team)} | blue team: {len(blue_team)}')
print(f'red hero: {red_hero.level} | blue hero: {blue_hero.level}')
 
s = random.choice(red_team)
s.follow_the_hero(red_hero)
print(f'red hero: {red_hero.id} | red soldier: {s.id}')

import random
 
class Strategy():
    def __init__(self, number, team):
        self.team = team
        self.number = number
 
 
class Soldier(Strategy):
    def Following_for_hero(self, Hero):
        self.Hero = Hero
        print ('Im following hero!')
 
class Hero(Strategy):
    lvl = 0
    def lvlup(self, ):
        self.lvl += 1
 
Hero_one = Hero(random.random(), 1)
Hero_two = Hero(random.random(), 2)
 
first_team = []
second_team = []
 
for i in range(10):
    team_for_soldier = random.randint(1, 3)
    number_for_soldier = random.random()
 
    if team_for_soldier == 1:
        Voin = Soldier(number_for_soldier, team_for_soldier)
        first_team.append(Voin)
    else:
        Voin = Soldier(number_for_soldier, team_for_soldier)
        second_team.append(Voin)
 
print (len(first_team), len(second_team))
 
 
if len(first_team) > len(second_team):
    Hero_one.lvlup()
elif len(first_team) == len(second_team):
    print ("DRAW !!!")    #В случае одинаковом кол-ве солдат в списке
elif len(first_team) == 0 or len(second_team) == 0:
    print ('CLEAN VICTORY!')     #В случае "сухой" победы
else:
    Hero_two.lvlup()
 
print ('Level Hero\'s One:' ,Hero_one.lvl,'\n'
       'Level Hero\'s Two:' ,Hero_two.lvl,'\n')
 
if len(first_team) != 0:    #сделано что бы в случае отсутсвия солдат в списке №1 не было ошибки
    Follow_command = first_team[0].Following_for_hero(Hero_one)
    print ('Hero one Number:', Hero_one.number,'\n'   #Идентификационный номер героя
           'Soldier\'s Number:', first_team[0].number    #Идентификационный номер солдата(в данном случае на первой позиции в списке), который следует за данным героем
           )
Всё работает. Подскажите, что можно сделать что бы код был более чистым ? Если такое возможно, в общем, жду критики. В особенности интересует в начале кода при создании классов и объектов Спасибо :)

Скажите, зачем в методе setPlaces торчит параметр "р", если все данные он берёт от родителя? Я к этому аргументу даже обратиться не могу. IDE-шка меня сразу на три буквы посылает. Или по другому, как передать данные в этот арумент?