Является ли я.__ dict __. Update (** kwargs) хорошим или плохим стилем?

В Python, скажем, у меня есть некоторый класс Circle, который наследуется от Shape. Форма нуждается в координатах x и y, и, кроме того, Circle нуждается в радиусе. Я хочу, чтобы иметь возможность инициализировать Circle, делая что-то вроде

c = Circle(x=1., y=5., r=3.)

Circle наследует от формы, поэтому мне нужно использовать именованные аргументы для __init__, потому что разные классы требуют разных конструкторов. Я мог бы вручную установить x, y и r.

class Shape(object):
    def __init__(self, **kwargs):
        self.x = kwargs['x']
        self.y = kwargs['y']

class Circle(Shape):
    def __init__(self, **kwargs):
        super(Circle, self).__init__(**kwargs)
        self.r = kwargs['r']

или я мог бы автоматически установить атрибуты моего круга, используя self.__dict__.update(kwargs)

class Shape(object):
    def __init__(self, **kwargs):
        self.__dict__.update(**kwargs)

class Circle(Shape):
    def __init__(self, **kwargs):
        super(Circle, self).__init__(**kwargs)

Преимущество этого заключается в том, что меньше кода, и мне не нужно поддерживать шаблон, например self.foo = kwargs['foo']. Недостатком является то, что не очевидно, какие аргументы необходимы для Circle. Это считается чит или это хороший стиль (до тех пор, пока интерфейс для Circle хорошо документирован)?


Спасибо, всем, за ваши продуманные ответы. Мне понравилось, когда я экспериментировал с организацией моего кода, но я убеждаюсь, что я заменил его корректным передачей аргументов и проверил четкую проверку ошибок в производственном коде.

Ответ 1

class Shape(object):
    def __init__(self, x=None, y=None):
        self.x = x
        self.y = y

class Circle(Shape):
    def __init__(self, r=None, **kwargs):
        super(Circle, self).__init__(**kwargs)
        self.r = r

И это он. Не используйте **kwargs, когда они вам действительно не нужны.

Это считается чит или это хороший стиль (пока интерфейс для Circle хорошо документирован)?

Когда у вас есть выбор между написанием простого, понятного кода и кода головной боли + приятных docstrings, у вас на самом деле нет выбора, вы просто заходите и пишете простой, самодокументированный код:)

Ответ 2

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

Подумайте, что произойдет, если вы сделаете опечатку при инициализации круга, что-то вроде Circle(x=1., y=5., rr=3.). Вы хотите увидеть эту ошибку немедленно, чего не было бы с __dict__.update(kwargs).

Ответ 3

Если вы хотите назначить автоматически, я предлагаю следующий подход:

def __init__(self, **kwargs):
    for key, value in kwargs.iteritems():
        setattr(self, key, value)

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

Ответ 4

Если вы хотите, чтобы это было более очевидно, вы могли бы Circle.__init__ выполнить некоторые проверки здравомыслия по аргументам. Предположительно, вы должны проверить, чтобы все аргументы были там, и, возможно, вызывать ошибки для бессмысленных аргументов.

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

class Circle(Shape):
    def __init__(self, **kwargs):
        self.check(kwargs, 'x', 'y', 'r')
        super(Circle, self).__init__(**kwargs)

.check будет реализован в Shape и, по сути, просто проверяет, что все аргументы находятся в kwargs, и, возможно, никаких дополнительных (извините, нет кода для этого - вы можете понять это на своем своя). Вы даже можете перегрузить субклассы, чтобы проверить дополнительные аргументы, которые вы можете обрабатывать иначе, чем другие аргументы (т.е. Дать им значение по умолчанию, которое иначе не было бы назначено в Shape.__init__.

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