Форум сайта python.su
0
Хочу, с одной стороны, застраховаться от разрастания модулей до многострок и застраховаться от рекурсивных импортов; а с другой - сохранить возможность перекрестной проверки на isinstance базовых классов.
Есть одна базовая ветка классов Ast, которой хорошо бы все про всех знать (кол-во классов == кол-во перегружаемых операторов), есть базовая ветка Var(Var, StrVar, NumVar, IntVar(NumVar), FloatVar(NumVar), ArrayVar), которой уж точно надо явно конструировать объекты классов Ast.*.
И еще возможно появление пары классов Callable, которые хорошо бы держать в том же слое, что и предыдущие.
Но ориентироваться удобно, когда они находятся в трех разных модулях:
Отредактировано Levitanus (Июль 28, 2018 20:58:26)
Офлайн
857
Модуль должен представлять из себя автономную единицу. Должен быть точный ответ на вопрос “Что делает этот модуль?”. То же самое касается классов и функций. Если ты точно не знаешь, что делает класс (делает первое, второе и третье) или функция, значит надо их разделить на несколько.
Пример хорошей функции:
Что делает эта функция?
def f(a, b): return a + b
def f(a, b): c = a + b print(c) return a * 2
Отредактировано py.user.next (Июль 29, 2018 02:56:07)
Офлайн
0
Спасибо.
А теперь перефразиую:
как организовать слабое (сейчас у меня в голове только сильное) взаимодействие классов, если я хочу сделать вполне конкреную вещь вполне конкретным классом, но при этом не хочу потерять полиморфизм?
Вот сейчас, к сожалению, реального примера под рукой нет, т.к. нет пока такого “плохого” кода, но я не исключаю его появление. Допустим, что-то в таком духе:
from abc import abstractmethod class Operator: def __init__(self, data): self._data = data @abstractmethod def operate(self): pass class Operator1(Operator): def operate(self): return Operation1(self._data) class Operator2(Operator): def operate(self): return Operation2(self._data) class Composition: '''и вдруг появляется какой-нибудь класс, вроде:''' def __init__(self, data1, data2): self._data_op1 = Operator1(data1) self._data_op2 = Operator2(data2) def get(self): a = self._data_op1.operate() b = self._data_op2.operate() return a, b class Operation: def __init__(self, data): '''для которого мы делаем отдельную распаковку или я неверно понимаю полиморфизм?''' if isinstance(data, Composition): data = data.get() self._data = data @abstractmethod def run(self): pass class Operation1(Operation): def run(self): return make_something(self._data) class Operation2(Operation): def run(self): return make_something_else(self._data) class Composition2: '''а потом переходим на другой уровень абстракции''' def __init__(self, data1, data2, arg3: bool): self._data = Composition(data1, data2) self._attr = arg3 def operate(self): a, b = self._data.get() if self._attr: return Operation1(a + b) return Operation2(a + b)
Офлайн
857
LevitanusТы всё это можешь делать в одном модуле. К модульности это никак не относится.
как организовать слабое (сейчас у меня в голове только сильное) взаимодействие классов
Отредактировано py.user.next (Июль 29, 2018 02:55:40)
Офлайн
857
Что касается полиморфизма. В питоне нет перегрузки методов, а вот в Java в C++ есть перегрузка методов. Поэтому там можно делать полиморфные методы. Что это значит - по сигнатуре вызова метода определяется то, какой вариант тела метода будет выполняться. То есть метод один, а поведений у него много разных. Часто можно увидеть разные конструкторы у одного класса, они все выполняются по-разному - это полиморфный конструктор. Имя у него одно, а аргменты различаются, и вот по аргументам в вызове происходит выбор варианта.
Есть понятие переопределения метода и перегрузки метода. Переопределение метода - это замена метода, а перегрузка метода - это добавление вариантов метода. Часто путают эти понятия, хотя это разные вещи.
На питоне сложно приводить примеры вот этих классических вещей, потому что они подразумеваются, у них нет своего синтакиса. Поэтому если хочешь понять ООП, то бери Java или C++, так как там всё явно и ты полиморфизм тот же делаешь с помощью явных синтаксических конструкций. А в питоне ты потом про них просто думаешь и всё, а синтаксис никакой писать не надо, потому что он как бы подразумевается. А некоторых вещей в питоне и нет вообще (типа перегрузки методов; используют *args, хотя это просто костыль неинформативный). В общем, в других языках с ООП всё просто яснее, полнее и лучше запоминается. Вот интерфейсов в питоне синтаксически нет, вот как их объяснишь на примере питона? Только рассказывать, как он там подразумевается.
Отредактировано py.user.next (Июль 29, 2018 06:22:38)
Офлайн
0
спасибо большое, что помогаете!
Вообще я согласен, что образование в любом виде должно быть постепенным. Но желания прям постигать дзен, к сожалению, нет. Есть желание решить конкретную задачу для конкретного случая на конкретном ЯП… По отношению к ситуейшну звучит плохо, но на полноценное “образование” в каком бы то ни было виде, времени просто нет. Есть другая предметная область, требующая нормального погружения.
Но вот в рамках этих конкретных задач уж разбираться - совсем другое дело 
Тем более, если вдруг повезет на такие здоровские развернутые ответы.
С этим, вроде, стало понятнее, попробую сегодня покрутить.
Офлайн
0
py.user.nextтак можно?
Вот интерфейсов в питоне синтаксически нет, вот как их объяснишь на примере питона?
class INameLocal(KSP): '''Object name interface. Example: class Test: def __init__(self, name='myname'): self.name = INameLocal(name) ''' script_prefix = '' def __init__(self, name, prefix='', postfix=''): self._name = name self._prefix = prefix self._postfix = postfix def __call__(self): return self._prefix + IName.script_prefix +\ self._name + self._postfix
Офлайн
857
Для понимания интерфейсов возьми Java или Go, там они встроенные и для них специальная синтаксическая конструкция есть.
Здесь можно простые примеры и задания по интерфейсам найти
https://tour.golang.org/
А в питоне интерфейсов нет, ты их как бы делаешь самостоятельно, представляя их только в голове.
Отредактировано py.user.next (Июль 29, 2018 23:56:05)
Офлайн