Является ли self .__ dict __. Update (** kwargs) хорошим или плохим стилем? - PullRequest
40 голосов
/ 16 марта 2012

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

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

Круг наследуется от фигуры, поэтому мне нужно использовать именованные аргументы для __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 хорошо документирован)?


Спасибо всем за ваши вдумчивые ответы. Хак self.__dict__.update(**kwargs) был полезен для меня, когда я экспериментировал с организацией своего кода, но я позабочусь о том, чтобы заменить его на правильную передачу аргументов в явном виде и наглядную проверку ошибок в рабочем коде.

Ответы [ 4 ]

25 голосов
/ 16 марта 2012
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 хорошо документирован)?

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

17 голосов
/ 16 марта 2012

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

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

10 голосов
/ 16 марта 2012

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

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

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

3 голосов
/ 16 марта 2012

Если вы хотите, чтобы это было более очевидно, вы можете 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__.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...