Это правильная практика разработки программного обеспечения для построения класса? - PullRequest
0 голосов
/ 12 июля 2019

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

###scrip1.py###
import SampleClass.method1 as method1

output = method1(input_var)

###script2.py###
class SampleClass(object):

    def __init__(self):
        self.var1 = 'var1'
        self.var2 = 'var2'

    @staticmethod
    def method1(input_var):
        # Syntactic Sugar method that outside uses
        sample_class = SampleClass()
        result = sample_class._method2(input_var)
        return result

    def _method2(self, input_var):
        # Main method executes the various steps.
        self.var4 = self._method3(input_var)
        return self._method4(self.var4)

    def _method3(self):
        pass

    def _method4(self):
        pass

1 Ответ

2 голосов
/ 13 июля 2019

Отвечая как на ваш вопрос, так и на ваш комментарий, да, можно написать такой код, но я не вижу смысла в этом:

class A:
    def __new__(cls, value):
        return cls.meth1(value)

    def meth1(value):
        return value + 1 

result = A(100)
print(result)

# output:
101

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

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

result = some_func(value) 

выглядит именно так, как люди ожидают, читая его, вызов функции.

Так что нет, это не очень хорошая практика, если вы не придумали для нее хороший вариант использования (сейчас я не могу вспомнить ни одного)

Также имеет отношение к этому вопросу документация здесь , чтобы понять поведение __new__ и __init__.

Относительно вашего другого комментария под моим ответом:

определение __init__ в классе для установки начального состояния (значений атрибутов) (уже) созданного экземпляра происходит постоянно. Но __new__ имеет другую цель настройки создания объекта . Объект экземпляра еще не существует при запуске __new__ (это функция конструктора). __new__ редко требуется в Python, если вам не нужны такие вещи, как singleton , скажем, класс A, который всегда возвращает один и тот же экземпляр объекта (из A) при вызове с A(). Обычные пользовательские классы обычно возвращают новый объект при создании экземпляра. Вы можете проверить это с помощью встроенной функции id(). Другой вариант использования - когда вы создаете свою собственную версию (путем создания подклассов) неизменяемого типа. Поскольку оно является неизменным, значение уже было установлено, и нет способа изменить значение внутри __init__ или более поздней версии. Отсюда необходимость действовать до этого, добавив код внутри __new__. Использование __new__ без возврата объекта того же типа класса (это необычный случай) имеет дополнительную проблему не __init__.

Если вы просто группируете множество методов внутри класса, но в каждом экземпляре по-прежнему нет состояния для хранения / управления (вы заметите это также из-за отсутствия использования self в теле методов), не используйте Классы и организации этих методов теперь превратились в самоотверженные функции в модуле или пакете для импорта. Потому что, похоже, вы группируете только для организации связанного кода.

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

Это не имеет ничего общего с __new__ использованием.

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

 my_font = SomeFont().italic().bold()

Наконец, если вам не нравится result = A().method(value), рассмотрите псевдоним:

func = A().method
...
result = func(value)

Обратите внимание, что вы остались без ссылки на экземпляр A () в вашем коде. Если вам нужна ссылка, разделите ее дальше:

a = A()
func = a.method
...
result = func(value)

Если ссылка на A () не нужна, то вам, вероятно, тоже не нужен экземпляр, и класс просто группирует методы. Вы можете просто написать

func = A.method
result = func(value)

где бескорыстные методы должны быть украшены @staticmethod, потому что здесь нет ни одного экземпляра Также обратите внимание, как статические методы можно превратить в простые функции вне классов.

Edit:

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

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

Из-за этого имеет смысл, что каждый метод использует self для доступа к состоянию.Также имеет смысл, что вы можете создавать экземпляры разных объектов, содержащих разные конфигурации, поэтому я не вижу здесь смысла для @staticmethod или @classmethod.

Начальная конфигурация экземпляра выполняется в __init__ как обычно.

# file: multistepinc.py

    def __init__(self, offset):
        self.offset = offset

    def result(self, value):
        return self._step1(value)

    def _step1(self, x):
        x = self._step2(x)
        return self.offset + 1 + x

    def _step2(self, x):
        x = self._step3(x)
        return self.offset + 2 + x

    def _step3(self, x):
        return self.offset + 3 + x

def get_multi_step_inc(offset):
    return MultiStepInc(offset).result

--------

# file: multistepinc_example.py

from multistepinc import get_multi_step_inc

# get the result method of a configured
# MultiStepInc instance
# with offset = 10.

# Much like an object factory, but you
# mentioned to prefer to have the result
# method of the instance
# instead of the instance itself.
inc10 = get_multi_step_inc(10)

# invoke the inc10 method
result = inc10(1)
print(result)

# creating another instance with offset=2
inc2 = get_multi_step_inc(2)

result = inc2(1)
print(result)

# if you need to manipulate the object
# instance
# you have to (on file top)

from multistepinc import MultiStepInc

# and then
inc_obj = MultiStepInc(5)

# ...
# ... do something with your obj, then
result = inc_obj.result(1)
print(result)

Выходы:

37
13
22
...