2012-03-15 2 views
33

В Python, скажем, у меня есть класс, Circle, который наследуется от Shape. Форма нуждается в координатах x и y, и, кроме того, Circle нуждается в радиусе. Я хочу, чтобы иметь возможность инициализировать круг делать что-то подобное,Является ли я .__ dict __. Обновление (** kwargs) хорошего или плохого стиля?

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 хорошо документирован)?


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

+0

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

+0

@ JoranBeasley - Да, но если вы это сделали, вы по существу нарушаете правила, и это ваша собственная ошибка. –

+2

Вам не хватает 'self'. –

ответ

21
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 хорошо документирован)?

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

+0

есть много совершенно разумных раз использовать ** независимо от того, когда вы перегружаете другие классы. –

+1

@JoranBeasley - Нажмите его еще раз, чтобы отменить его. –

+0

Где находится 'self'? –

2

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

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

+0

Откуда вы знаете, что «.check» реализовано в 'Shape'. Вы учитель OP? –

+1

@StevenRumbalski - Я был бы самым молодым учителем в области компьютерных наук, если бы был. Я имел в виду, что OP может реализовать 'Shape.check', а затем использовать его во всех подклассах. Я уточню это. –

16

Я бы сказал, что первый метод определенно предпочтительнее, потому что explicit is better than implicit.

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

8

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

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

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

+0

Я бы скорее увидел 'self .__ dict __. Update (kwargs)', чем этот (возможно, менее эффективный и) более длинный путь для достижения того же результата. –

+5

Это будет иметь преимущество при работе с дескрипторами. Работа непосредственно с '__dict__' - плохая идея по этой причине. –

+3

Не могли бы вы помочь мне понять, почему это лучше работает с дескрипторами? –

Смежные вопросы