Уведомления

Группа в Telegram: @pythonsu

#1 Июль 28, 2018 20:56:35

Levitanus
Зарегистрирован: 2018-05-01
Сообщения: 46
Репутация: +  0  -
Профиль   Отправить e-mail  

как корректно раскидать классы по модулям?

Хочу, с одной стороны, застраховаться от разрастания модулей до многострок и застраховаться от рекурсивных импортов; а с другой - сохранить возможность перекрестной проверки на isinstance базовых классов.
Есть одна базовая ветка классов Ast, которой хорошо бы все про всех знать (кол-во классов == кол-во перегружаемых операторов), есть базовая ветка Var(Var, StrVar, NumVar, IntVar(NumVar), FloatVar(NumVar), ArrayVar), которой уж точно надо явно конструировать объекты классов Ast.*.
И еще возможно появление пары классов Callable, которые хорошо бы держать в том же слое, что и предыдущие.
Но ориентироваться удобно, когда они находятся в трех разных модулях:

  • base (AstBase, VarBase)
  • my_ast (Ast.*) * 15
  • my_vars (IntVar, StrVar, FloatVar, ArrayVar)
но так приходится жертвовать специфичной для my_vars.* класссов логикой классов из my_ast, чтобы иметь возможность импорта Ast.* классов в модуле my_vars.

Я вот думаю, вынести в модуль base классы StrVar, NumVar, ArrayVar. А чтобы не писать в тело NumVar 150 строк кода, написать их в классе NumVarProxy, закинуть его в модуль my_vars и наследовать уже IntVar и FloatVar от прокси. Но прям плохо же…

Отредактировано Levitanus (Июль 28, 2018 20:58:26)

Офлайн

#2 Июль 29, 2018 00:31:38

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10022
Репутация: +  857  -
Профиль   Отправить e-mail  

как корректно раскидать классы по модулям?

Модуль должен представлять из себя автономную единицу. Должен быть точный ответ на вопрос “Что делает этот модуль?”. То же самое касается классов и функций. Если ты точно не знаешь, что делает класс (делает первое, второе и третье) или функция, значит надо их разделить на несколько.

Пример хорошей функции:
Что делает эта функция?

  
def f(a, b):
    return a + b
Складывает значения a и b.

Пример плохой функции (требует разделения на несколько функций):
Что делает эта функция?
  
def f(a, b):
    c = a + b
    print(c)
    return a * 2
Складывает значения a и b, выводит результат сложения, умножает a на 2 и возвращает это.


tags: module



Отредактировано py.user.next (Июль 29, 2018 02:56:07)

Офлайн

#3 Июль 29, 2018 01:35:30

Levitanus
Зарегистрирован: 2018-05-01
Сообщения: 46
Репутация: +  0  -
Профиль   Отправить e-mail  

как корректно раскидать классы по модулям?

Спасибо.


А теперь перефразиую:
как организовать слабое (сейчас у меня в голове только сильное) взаимодействие классов, если я хочу сделать вполне конкреную вещь вполне конкретным классом, но при этом не хочу потерять полиморфизм?
Вот сейчас, к сожалению, реального примера под рукой нет, т.к. нет пока такого “плохого” кода, но я не исключаю его появление. Допустим, что-то в таком духе:

 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)
и в принципе, в этом случае мы даже в рекурсию не входим, просто появляется жесткая связность классов. А вместе с этим и рекурсивный импорт, если эти классы находятся в разных модулях (один для операторов, второй для операций, третий для композиции1, четвертый для композиции 2)
Проверять не на принадлежность к классу, а на реализацию интерфейса get()?

Офлайн

#4 Июль 29, 2018 02:55:21

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10022
Репутация: +  857  -
Профиль   Отправить e-mail  

как корректно раскидать классы по модулям?

Levitanus
как организовать слабое (сейчас у меня в голове только сильное) взаимодействие классов
Ты всё это можешь делать в одном модуле. К модульности это никак не относится.

А что насчёт модульности:
Вот прямо можешь в питоне смотреть. Есть, например, модуль csv. Что он делает? Он предоставляет инструментарий для работы с csv-форматом. Он может его читать, может его писать, может переключаться между разными диалектами этого формата.
А этот модуль выводит на экран что-нибудь? Нет.
А этот модуль может обрабатывать звук? Нет.
А этот модуль может скачивать ссылки из Интернета? Нет.
А этот модуль знает язык XML? Нет.
То есть модуль - это такая независимая от всего остального вещь, которая для чего-то предназначена, и это что-то оно одно.

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

В некоторых языках модуль в контексте этих языков вообще распространяется на несколько файлов. В Go, в Java, ещё там в нескольких такая фигня есть (там они пакетами называются). Поэтому ты можешь путаться, что такое модуль. Но в контексте программирования понятие модуля означает только один модуль - независимую единицу, имеющую “прочность” и “связность”.

И вот когда у тебя модуль отвечает точно на вопрос “Что делает этот модуль?”, то этот модуль называется функционально прочным, потому что у него только одна функция, а не несколько.


tags: module



Отредактировано py.user.next (Июль 29, 2018 02:55:40)

Офлайн

#5 Июль 29, 2018 06:11:58

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10022
Репутация: +  857  -
Профиль   Отправить e-mail  

как корректно раскидать классы по модулям?

Что касается полиморфизма. В питоне нет перегрузки методов, а вот в Java в C++ есть перегрузка методов. Поэтому там можно делать полиморфные методы. Что это значит - по сигнатуре вызова метода определяется то, какой вариант тела метода будет выполняться. То есть метод один, а поведений у него много разных. Часто можно увидеть разные конструкторы у одного класса, они все выполняются по-разному - это полиморфный конструктор. Имя у него одно, а аргменты различаются, и вот по аргументам в вызове происходит выбор варианта.

Есть понятие переопределения метода и перегрузки метода. Переопределение метода - это замена метода, а перегрузка метода - это добавление вариантов метода. Часто путают эти понятия, хотя это разные вещи.

На питоне сложно приводить примеры вот этих классических вещей, потому что они подразумеваются, у них нет своего синтакиса. Поэтому если хочешь понять ООП, то бери Java или C++, так как там всё явно и ты полиморфизм тот же делаешь с помощью явных синтаксических конструкций. А в питоне ты потом про них просто думаешь и всё, а синтаксис никакой писать не надо, потому что он как бы подразумевается. А некоторых вещей в питоне и нет вообще (типа перегрузки методов; используют *args, хотя это просто костыль неинформативный). В общем, в других языках с ООП всё просто яснее, полнее и лучше запоминается. Вот интерфейсов в питоне синтаксически нет, вот как их объяснишь на примере питона? Только рассказывать, как он там подразумевается.



Отредактировано py.user.next (Июль 29, 2018 06:22:38)

Офлайн

#6 Июль 29, 2018 18:30:09

Levitanus
Зарегистрирован: 2018-05-01
Сообщения: 46
Репутация: +  0  -
Профиль   Отправить e-mail  

как корректно раскидать классы по модулям?

спасибо большое, что помогаете!
Вообще я согласен, что образование в любом виде должно быть постепенным. Но желания прям постигать дзен, к сожалению, нет. Есть желание решить конкретную задачу для конкретного случая на конкретном ЯП… По отношению к ситуейшну звучит плохо, но на полноценное “образование” в каком бы то ни было виде, времени просто нет. Есть другая предметная область, требующая нормального погружения.
Но вот в рамках этих конкретных задач уж разбираться - совсем другое дело
Тем более, если вдруг повезет на такие здоровские развернутые ответы.
С этим, вроде, стало понятнее, попробую сегодня покрутить.

Офлайн

#7 Июль 29, 2018 22:04:17

Levitanus
Зарегистрирован: 2018-05-01
Сообщения: 46
Репутация: +  0  -
Профиль   Отправить e-mail  

как корректно раскидать классы по модулям?

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

Офлайн

#8 Июль 29, 2018 23:54:52

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10022
Репутация: +  857  -
Профиль   Отправить e-mail  

как корректно раскидать классы по модулям?

Для понимания интерфейсов возьми Java или Go, там они встроенные и для них специальная синтаксическая конструкция есть.
Здесь можно простые примеры и задания по интерфейсам найти
https://tour.golang.org/

А в питоне интерфейсов нет, ты их как бы делаешь самостоятельно, представляя их только в голове.



Отредактировано py.user.next (Июль 29, 2018 23:56:05)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version