Найти - Пользователи
Полная версия: как явно указать тип
Начало » Python для новичков » как явно указать тип
1
Ess
Здравствуйте,
подскажите у меня есть 2 файла, в каждом пользовательский класс. P1 - в первом файле, P2 во втором файле

file1.py:
 from file2 import P2
class P1:
    def __init__(self):
        self.head = P2(self)
        
    def MYprint(self):
        print('+')
pers1 = P1()
pers1.head.parent.MYprint()

file2.py:

 class P2():
    def __init__(self, parent: object = None):
        self.parent = parent

для 1 файла я могу использовать импорт из 2 файла. Я хочу в файле 2, явно указать, что переменная parent была типа P1 или получить доступ к P1 из P2, но если я пропишу
from file1 import P1, то я попаду на цикличное замыкание

по идее я могу написать так и ошибки не будет, но метод MYprint будет типа ANY:
pers1 = P1()
pers1.head.parent.MYprint()

подскажите как решается данная проблема в python??

в др языках мы обычно также parent объявляли как Object и импортировали P1 но в др директивах и писали pers1.head.(parent AS P2).MYprint() явно указывая на тип parent и получали доступ к методам parent, т.е. я хочу обращаться к методам или свойствам класса P1 из класса P2

заранее спасибо!


ps: я пробовал делать импорт внутри класса (в доках написано, что это допускается), и также я почитал про TYPE_CHECKING, но когда разбирался с ним понял что он просто выйдет из цикла замыкания если попадет в него, при этом сгенерирует ошибку на участке кода обращения к P1 т.к. импорт не будет выполнен..
ZerG
1. Создайте третий файл main.py
него импортируйте классы из file1 и file2 и дедайте с ними что хотите
2. Наследуйте P2 от P1 (или наоборот - в заыисимости от задачи)
3. Обьедените классы

4. Опишите задачу - возможно вы изначально выбрали неправильный путь для реализации
Ess
Здравствуйте Zerg, я сразу скажу я просто изучаю методы реализации. У меня нет какой то конкретной задачи. Мне просто интересна реализация на этапе изучения.

я приведу пример (это просто, что мне пришло в голову - не задача)

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

 class GAZON:
...
class CHELOVEK:
...

ЧЕЛОВЕК не является наследником класса ГАЗОН (он просто там стоит, но всегда стоит, как бы газон не может быть без человека) ваш пункт (2) не подойдет, от сюда следует что:

 class GAZON():
def __init__(self):
        self.CHELOVEK = CHELOVEK(self)

теперь я хочу знать на каком газоне стоит человек следовательно
  
class CHELOVEK():
    def __init__(self, Gazon: GAZON = None):
        self.Gazon = Gazon

тут нет проблем т.к. человек может существовать и сам без газона. задам умолчание (GAZON = None)

Предположим человек может покрасить свой газон, тут нет проблем - в ГАЗОН добавим свойство ЦВЕТ
и от ЧЕЛОВЕКА его можно изменить

 self.Gazon.Color=red

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

как только я перенесу человека во второй файл то потеряю Gazon: GAZON, но я могу указать Gazon: Object, но теперь я явно не знаю какие методы и свойства есть у Object (питон разрешит неявное обращение к методам и классам. но это не безопасно).

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

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

Это есть частая задача на больших проектах , в других языках (js, java, ++, #, delphi без проблем решаемая там есть директивы и явные указатели)
я понимаю что на python это тоже как то реализуемо (компилятор то сишный), но пока не пойму как т.к. только начал изучать этот язык))


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

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

main.py - основной файл программы
houses.py - файл содержащий обьект Дом
animals.py - файл содержащий обьект Животное

 # houses.py
class House:
    def __init__(self, street, street_num, flat) -> None:
        self.street = street
        self.street_num = street_num
        self.flat = flat
    def address(self):
        print(f"Адресс: ул. {self.street} дом {self.street_num} квартира {self.flat}")
 # animals.py
class Animal:
    def __init__(self, animal, name) -> None:
        self.animal = animal
        self.name = name
    def pet(self):
        print(f"Животное: {self.animal} кличка: {self.name}")

 # main.py
from icecream import ic
from animals import Animal
from houses import House
# animals
cat = Animal("CAT", "Chamor")
dog = Animal("DOG", "Gavcheck")
# houses
bunker = House("Secret Place", 0, -1)
sklad = House("Строителей", 23, 66)
# Show info
cat.pet()
dog.pet()
bunker.address()
sklad.address()

 Животное: CAT кличка: Chamor
Животное: DOG кличка: Gavcheck
Адресс: ул. Secret Place дом 0 квартира -1
Адресс: ул. Строителей дом 23 квартира 66


На данный момент в главном файле вы можете спокойно работать с обоими классами создавать обьекты и так далее. Но все они никак не связанны.
Исходя из нашего примера - есть несколько домов и животных. Предположим что животное должно иметь отношение к месту жительства - тогда животное либо домашнее и имеет адресс либо бродяга.
Тогда поменяем наш код (пока без наследования - по вашему варианту)

 # houses.py
class House:
    def __init__(self, street, street_num, flat) -> None:
        self.street = street
        self.street_num = street_num
        self.flat = flat
# animals.py
class Animal:
    def __init__(self, animal, name, home=None ) -> None:
        self.animal = animal
        self.name = name
        self.home = home
    def info(self):
        print(f"Животное: {self.animal} кличка: {self.name}")
        if self.home:
            print(f"Адресс: ул. {self.home.street} дом {self.home.street_num} квартира {self.home.flat}")
        else:
            print("Бомжара")
# main.py
from animals import Animal
from houses import House
# houses
sklad = House("Строителей", 23, 66)
# animals
cat = Animal("CAT", "Chamor")
dog = Animal("DOG", "Gavcheck", sklad)
# Show info
cat.info()
dog.info()


Животное: CAT кличка: Chamor
Бомжара
Животное: DOG кличка: Gavcheck
Адресс: ул. Строителей дом 23 квартира 66


Ну а более правильный метод(это когда дочитаете) это уже делать наследованием
то есть
 class Animal(House):
    def __init__(....)
        super()......

Но это пока что для вас еще рано.





Ess
Zerg скажите пожалуйста вы не поняли мой вопрос?
ZerG
Я пытаюсь вас подвести к пониманию вами вашей ошибки.
Вы же судя по всему ожидаете готового ответа не собираясь шевельнуть мозгами.

Ок. Попробуем еще раз. Создадим два файла простых класса

 # file1.py
class One:
    def __init__(self, name):
        self.name = name
        self.two = None  # To hold reference to Two object
    def set_two(self, two):
        self.two = two
# file2.py
class Two:
    def __init__(self, age):
        self.age = age
        self.one = None  # To hold reference to One object
    def set_one(self, one):
        self.one = one
Условно мы имеем два модуля которые могут содержать друг друга. Но как мы знаем делать перекрестный импорт мы не можем. И как раз об этом можно почитать в разделе наследования классов.
Так же необходимо разобраться и понять проблему ромба(мнжественное наследование)
https://sorokin.engineer/posts/ru/python_super.html

И вот теперь что бы решить ваш вопрос, как я и писал в самом начале используем третий файл в котором свяжем наши модели как нам угодно решая проблему перекрестного импорта

 # main.py
from file1 import One
from file2 import Two
# Create objects
one = One("John")
two = Two(30)
# Set references
one.set_two(two)
two.set_one(one)
# Accessing attributes from other class
print(f"One's name: {one.name}, Two's age: {one.two.age}")
print(f"Two's age: {two.age}, One's name: {two.one.name}")

One's name: John, Two's age: 30
Two's age: 30, One's name: John

Вот мы и имеем зависимость одного от другого и не имеем проблемы с перекрестным импортом. Ровно так же и на С, Rust и подобных языках. У нас всегда есть точка входа в виде main и отдельные файлы модули содержащие структуры которые позже переплетаются.


В конце концов, если у вас все же остался вопрос - не проблема.
Покажите рабочий пример на C
Ess
Я крайне восхищаюсь вашими усилиями, но я повторюсь
вы не поняли мой вопрос???



py.user.next
Ess
Предположим у меня есть газон и на нем может стоять человек, (также человек может и не стоять на газоне), т.е. у газона всегда есть хозяин, но могут быть и другие люди без своего газона
Есть газон, есть человек. При разрушении газона человек остаётся. При разрушении человека газон остаётся. Сделай третий объект, который следит за людьми и газонами и знает, кто где стоит; у него же и методы для установления хозяев газонов и проверок, кто где стоит и у кого кто хозяин.

Ess
ЧЕЛОВЕК не является наследником класса ГАЗОН (он просто там стоит, но всегда стоит, как бы газон не может быть без человека) ваш пункт (2) не подойдет, от сюда следует что:
Газон - это человек? Нет. Значит, газон не производится от человека. Человек - это газон? Нет. Значит, человек не производится от газона. Так что наследования у них друг от друга не должно быть.

Ess
теперь я хочу знать на каком газоне стоит человек следовательно
Вот у третьего объекта и спроси.

Ess
тут нет проблем т.к. человек может существовать и сам без газона. задам умолчание (GAZON = None)
Газон тоже может сменить хозяина или остаться с разрушенным хозяином.

Ess
Предположим человек может покрасить свой газон, тут нет проблем - в ГАЗОН добавим свойство ЦВЕТ
и от ЧЕЛОВЕКА его можно изменить
Если газон принадлежит человеку, то человек может выполнить операцию над газоном “покрасить в цвет X”. Чтобы определить, принадлежит ли газон человеку, надо это спросить у третьего объекта. Чтобы выполнить операцию над газоном, надо над человеком выполнить операцию “покрасить газон X в цвет Y” и передать туда газон и цвет, а внутри операции над человеком нужно попросить газон покраситься, выполнив над газоном операцию “покрасить в цвет X”.

Ess
Это есть частая задача на больших проектах , в других языках (js, java, ++, #, delphi без проблем решаемая там есть директивы и явные указатели)
Тебе надо сделать интерфейсы. В идеале объекты не должны знать друг про друга ничего, даже о существовании друг друга они знать не должны. Закон Деметры изучи. Поэтому и человек, в идеале, даже знать не должен, что он красит. Он красит газон или забор? Он не знает. Он просто вызывает полиморфную операцию у какого-то объекта - “покрасить в цвет X”. А как приклеить незвестный объект к человеку? Через интерфейс. Есть какая-то хрень, которую можно покрасить, - это интерфейс. Любой объект, у которого есть операция “покрасить в цвет X”, может занять эту роль.

Ess
в других языках (js, java, ++, #, delphi без проблем решаемая
В других языках ты столкнёшься с тем же самым. Потому что ООП не зависит от языков, оно существует без них. И оно везде одинаковое и полноценное. А языки уже там как-то там его реализуют, какой-то лучше, какой-то хуже. Вот в питоне нет интерфейсов, а в Java есть. Поэтому в Java программировать в ООП гораздо легче, потому что там синтаксические конструкции для этого есть. В JavaScript посложнее, там прототипирование, поэтому наследование нужно делать в обход. В питоне ещё сложнее, потому что вообще половины конструкций нет, которые должны быть. В Go есть интерфейсы, но нет наследования. В C++ нет главного корневого класса, который должен быть. Но если в них чего-то нет, то это не значит, что этого нет в ООП.

Поэтому ты вот спрашиваешь “как засунуть человека в газон?”, а у меня возникает вопрос “а что он там вообще делать должен? это что, при разрушении газона человек умирает?”. Даже если его сагрегировать в газон, то получится, что человек является частью газона. И зачем это нужно? Человек-то всё равно не узнает никак, чьей частью он является, потому что есть человеки, не имеющие отношения к газонам. Так что тебе придётся и пронаследовать людей тогда, чтобы были люди, которые знают про газоны и умеют выполнять над газонами операции, и были люди, которые не знают про газоны.

Так что зря ты упрощаешь всё. Это плохо закончится. Для ООП синтаксическую запись класса недостаточно знать. Это капля в море. Верхушка айсберга.

Ess
и я хотел бы сделать декомпозицию классов на файловом уровне
Ну вот классов у тебя нет пока что. Вот класс Человеки, класс Газоны и класс Менеджеры. Можешь их разнести по разным файлам. В файле с классом Человеки нужно пронаследовать класс Человеки и получить производный класс ЧеловекиНаГазонах. У класса ЧеловекиНаГазонах должна быть определена операция “покрасить газон X в цвет Y”. А в файле запуска программы, где функция main() находится, ты делаешь импорт всех этих модулей и после этого строишь человеков, газоны и менеджера. В менеджере заносишь пары (газон, человек) через операцию над менеджером “назначить газону X хозяина Y”. Также менеджер тебе найдёт, кто кому принадлежит и кто чей хозяин (через операции над менеджером такие). Ну вот тогда у тебя получится. Всё будет просто и всё будет работать.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB