Существуют различные способы создания атрибутов объекта вне метода init на основе начальных атрибутов. - PullRequest
0 голосов
/ 11 января 2020

Я пытаюсь понять OOP (конкретно, в Python 3). Вот базовый c шаблон класса:

class Lines:
    """
    Arguments: list of coordinates
    """

    def __init__(self, points):
        self.x1 = points[0]
        self.y1 = points[1]
        self.x2 = points[2]
        self.y2 = points[3]

Я передаю начальные и конечные (x, y) координаты в списке классу. Однако я также хочу добавить атрибуты length , slope и y-intercept к объектам этого класса Lines ( Примечание : я не хочу их как методы). Вот несколько способов, которые я нашел, чтобы сделать это -

Добавить в сам метод init

class Lines:

    def __init__(self, points):
        self.x1 = points[0]
        self.y1 = points[1]
        self.x2 = points[2]
        self.y2 = points[3]

        ##Create length, slope, and y_intercept attributes

        self.length = round(((self.x2-self.x1)**2 + (self.y2-self.y1)**2) ** 0.5, 2) 
        self.slope = round((self.y2 - self.y1)/(self.x2-self.x1),1)
        self.y_intercept = self.y1 - self.slope*self.x1

Создать методы для них и использовать декоратор @property

class Lines:

    def __init__(self, points):
        self.x1 = points[0]
        self.y1 = points[1]
        self.x2 = points[2]
        self.y2 = points[3]

    @property    
    def length(self):
        return round(((self.x2-self.x1)**2 + (self.y2-self.y1)**2) ** 0.5, 2)

    @property        
    def slope(self):
        return round((self.y2 - self.y1)/(self.x2-self.x1),1)

    @property
    def y_intercept(self):
        return self.y1 - self.slope*self.x1

Мои проблемы:

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

Я могу использовать декоратор свойств, а затем получить доступ к наклонам объектов и т. Д. c, например,

line1 = Lines([0,0,2,4])
line1.slope

Однако, когда я печатаю line1.__dict__, они не отображаются в атрибутах доступный.

Мой вопрос

Что я действительно хочу знать, так это то, есть ли другие (более часто используемые, Pythoni c) способы установки атрибутов для объектов (например: наклон) на основе начальные атрибуты (например: x1, y1). Я думал, что это будет очень распространенная проблема (то есть, имея в качестве входных данных набор атрибутов basi c и затем устанавливая другие более продвинутые атрибуты, основанные на них, для того же объекта), но я не нашел здесь много такого , Я уверен, что здесь не хватает простого, но элегантного решения.

Заранее спасибо. Я надеюсь, что я был ясен с моим вопросом.

1 Ответ

1 голос
/ 16 января 2020

Вы можете сделать это несколькими способами, возможно, они не являются «общими», но они все еще Pythonic.

Во-первых, __dict__

Рассмотрим этот простой код :

class Lines:
   pass

line1 = Lines()

Теперь я хочу изменить атрибут xy, я имею в виду установить его как 10. Как только Python является динамическим c и все в Python является объектом, я могу это сделать, посмотрите:

class Lines:
    pass

line1 = Lines()

line1.xy = 10
print(f"line1.xy = {line1.xy}") #Output: line1.xy = 10

Что? Как я могу установить атрибут, который не существует ?! Это просто, __dict__. В нем хранятся все атрибуты, установленные в экземпляре.

class Lines:
    pass

line1 = Lines()

line1.xy = 10
print(f"xy = {line1.xy}")  #Output: xy = 10

print(f"__dict__ = {line1.__dict__}")  #Output: __dict__ = {'xy': 10}

Итак, если __dict__ хранит атрибуты, установленные в экземпляре, каково состояние __dict__ до того, как я установил какой-либо атрибут ?! Как вы можете сделать вывод, он начинается пустым.

Таким образом, вы должны помнить, что __dict__ не показывает доступные атрибуты, но атрибуты, которые были установлены, и не имеет значения, существуют ли эти атрибуты или не в main class

Однако, когда я печатаю line1.__dict__, они не отображаются в доступных атрибутах.

Фактически все атрибуты в __dict__ доступны через него, но если вы попытаетесь получить любой другой атрибут, которого нет в __dict__, но определенный в классе, вы можете получить его. КАК? Взгляните:

class Lines:
    x1 = 3
    x2 = 7

line1 = Lines()

print(f"__dict__ = {line1.__dict__}")  #Output: __dict__ = {}

print(f"x1 = {line1.x1}") #Output: x1 = 3
print(f"x2 = {line1.x2}") #Output: x2 = 7

print(f"__dict__ = {line1.__dict__}") #Output: __dict__ = {}

line1.x1 = 9 #Setting x1, so it will be stored at __dict__
print(f"__dict__ = {line1.__dict__}") #Output: __dict__ = {'x1': 9}

print(f"x1 = {line1.x1}") #Output: x1 = 9
print(f"x2 = {line1.x2}") #Output: x2 = 7

print(f"test = {line1.test}") #Output: AttributeError

Что здесь делает python ?! Python сначала идет к __dict__ и пытается найти атрибуты x1 и x2, поэтому, если он их там не находит, то он переходит к main class, если он их находит, он их возвращает для вас, если нет Python Поднять AttributeError.

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

example = line1.__dict__['x1']
print(f"__dict__['x1'] = {example}") #Output: __dict__['x1'] = 9

Ваши проблемы и вопросы

Дискурс о коде pythoni c действительно Интересно, но мы должны спросить себя, являются ли эти коды python или нет, что я имею в виду, если какой-то код python и нет "improvisation", так что это pythoni c, даже если этот код не такой например, методы getattr(), setattr() и delattr(), иногда их использование более практично, чем "dotted notation", потому что они более надежны для понимания, особенно когда вы глубоко погружаетесь в то, что делаете и вы отлаживаете. (По крайней мере, я так думаю)

Так что все идет вокруг того, что вы делаете и что вы хотите сделать. Представьте, что у вас высококачественный код и вы используете не очень распространенные функции, не так ли, Pythoni c ?! Для ясности представьте, что вы пишете счетчик, который увеличивается каждый раз, когда выполняется транзакция, поэтому вместо этого вы используете обычный способ count += 1, вы go выше и используете itertools.count(), без сомнения, у вас есть высококачественный код , даже если люди обычно не используют этот модуль, вы можете узнать больше об этом здесь: itertools - Функции, создающие итераторы для эффективного цикла .

Итак, давайте go к коду: как вы видите, это еще один способ решения этой проблемы.

class Lines: 

    def __init__(self, points):
        self.x1 = points[0]
        self.y1 = points[1]
        self.x2 = points[2]
        self.y2 = points[3]

        self.length = self._length()
        self.slope = self._slope()
        self.y_intercept = self._y_intercept()

    def _length(self):
        return round(((self.x2-self.x1)**2 + (self.y2-self.y1)**2) ** 0.5, 2) 

    def _slope(self):
        return round((self.y2 - self.y1)/(self.x2-self.x1),1)

    def _y_intercept(self):
        return self.y1 - self.slope*self.x1

line1 = Lines([0,0,2,4])
print(line1.length)
print(line1.slope)
print(line1.y_intercept)

Ваше решение с использованием Read-Only property действительно интересно, на самом деле оно даже позволяет вы также работаете с кешем (если мы напишем, конечно же!), поэтому вам не нужны атрибуты cal c you length, slope и y_intercept каждый раз, когда вы вызываете эти переменные, вы можете cal c их значения только при изменении значения атрибута points.

Надеюсь, вам поможет, пока!

Don't use "improvisation", use Python.

...