2013-03-10 3 views
5

Выборочные коды, как это:Pythonic способ присвоить параметр атрибуту?

def assign(self, input=None, output=None, param=None, p1=None, p2=None): 
    if input: 
     self.input = input 
    if output: 
     self.output = output 
    if param: 
     self.param = param 
    if p1: 
     self.p1 = p1 
    if p2: 
     self.p2 = p2 

Хотя это выглядит очень ясно, он страдает, если вы 10 параметров для этой функции. У кого-нибудь есть идеи относительно более удобного способа для этого?

+0

В качестве примечания, существует потенциальная проблема с вашим кодом: что делать, если вы хотите фактически присвоить значение «Нет»? Если это имеет значение, вы можете обойти это, создав оценочное значение (например, 'sentinel = object()', затем 'input = sentinel, output = sentinel, ...' в параметрах, затем проверьте «если вход не является достоянием» вместо 'if input'). – abarnert

+0

@abarnert Я вижу. Изменившись на '** kwargs', мне теперь не нужно заботиться о часовом. –

+0

Нет проблем. Как я уже сказал, это всего лишь «потенциальная проблема». Если нет допустимых значений false-y ('None',' 0', '' '', '[]' и т. Д.), Которые вы когда-либо захотите сохранить в своих атрибутах, это совершенно нормально. – abarnert

ответ

7

вы можете сделать что-то вроде:

def assign(self,**kwargs): 
    for k,v in kwargs.items(): 
     if v: 
      setattr(self,k,v) 

Это довольно простой и подходит для многих ситуаций. Если вы хотите сохранить набор ключевых слов, которые вы будете принимать и поднимаете TypeError для отдыха:

#python2.7 and newer 
def assign(self,allowed_kwargs={'foo','bar','baz'},**kwargs): 
    if kwargs.keysview() - allowed_kwargs: 
     raise TypeError('useful message here...') 
    for k in allowed_kwargs: 
     setattr(self,k,kwargs[k]) 

Это несколько инспектировать-состояние, а так как пользователь будет видеть набор разрешенных kwargs.

+0

Это именно то, что мне нужно, спасибо mgilson! –

+1

Будьте осторожны с 'if v:'. Это не будет устанавливать атрибут для любого значения, которое не считается «True» в python. Поэтому 'obj.assign (a = 0, b = '', c = [])' не будет присваивать ни одному из этих атрибутов 'obj'. Я думаю, 'если v не является None:' было бы безопаснее. –

+0

@WarrenWeckesser - Это разумный момент. Я ответил так, чтобы отразить исходный код OP, но приятно заметить это здесь. – mgilson

1

Явный лучше, чем неявные

def assign(self, input=None, output=None, param=None, p1=None, p2=None): 

имеет много преимуществ по сравнению с

def assign(self, **kwargs) 
  • Он самодокументирующийся
  • Полезным TypeError возбуждаются, если недопустимый параметр передается до assign.
  • assign можно назвать с позиционными, а также именованными аргументами

К чести кода ОП Размещенный полностью явные, хотя if -statements однообразны. Чтобы сократить монотонность, вы могли бы использовать что-то вроде этого:

class Foo(object): 
    def assign(self, input=None, output=None, param=None, p1=None, p2=None): 
     for name in 'input output param p1 p2'.split(): 
      val = vars()[name] 
      if val is not None: 
       setattr(self, name, val) 

foo = Foo() 
foo.assign(p1=123, p2='abc') 
+0

Если вы хотите воспроизвести его функцию 'assign', вы должны поднять' TypeError' для любого 'key not in kwargs' вместо того, чтобы просто пропустить его. Кроме того, я думаю, что он более общий (например, работает с '__slots__') и, вероятно, более явным, для вызова' setattr' вместо 'self .__ dict __.__ update'. – abarnert

+0

Декораторы игнорируются при непосредственном касании __dict__. Поэтому, если кто-то должен был использовать '@ property' и' @ x.setter' для чего-то, это нарушит это. вызов 'setattr' исправляет это (например, abarnert.) – BlackVegetable

+0

@BlackVegetable: Да, я просто пытался привести один пример того, когда нужно «setattr». Существуют также пользовательские дескрипторы, '__setattr__', суперклассы с некоторыми из этих же проблем, суперклассы, реализованные в C/RPython/Java/.NET с аналогичными проблемами, ... – abarnert

0

Одна из больших вещей о Python является его интерактивный интерпретатор. При написании кода, как это:

>>> def assign(self, input=None, output=None, param=None, p1=None, p2=None): 
...  pass 
... 

Ее довольно легко понять, как вы должны использовать функцию:

>>> help(assign) 
Python Library Documentation: function assign in module __main__ 

assign(self, input=None, output=None, param=None, p1=None, p2=None) 

сравнения:

>>> def assign2(self, **kwargs): 
...  pass 
... 

дает:

>>> help(assign2) 
Python Library Documentation: function assign2 in module __main__ 

assign2(self, **kwargs) 

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

Первый вопрос: Почему Вы пишете код этого типа? Я считаю очень распространенным явлением, что мне нужен класс с кучей атрибутов, который он будет носить, но набор этих атрибутов по существу фиксирован. В самом общем случае эти атрибуты никогда не меняются в течение всего жизненного цикла объекта; в этом случае у python есть встроенный помощник для этого!

>>> import collections 
>>> Assignment = collections.namedtuple('Assignment', 'input output param p1 p2') 
>>> assign = Assignment(None, None, None, None, None)._replace 
>>> assign(p1=10) 
Assignment(input=None, output=None, param=None, p1=10, p2=None) 
>>> help(Assignment) 
Python Library Documentation: class Assignment in module __main__ 

class Assignment(__builtin__.tuple) 
| Assignment(input, output, param, p1, p2) 
| 
... SNIP 

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

>>> class Assignable(object): 
...  def assign(self, input=None, output=None, param=None, p1=None, p2=None): 
...   _kwargs = vars() 
...   _kwargs.pop('self') 
...   vars(self).update((attr, value) for attr, value in _kwargs.items() if value is not None) 
... 
>>> a = Assignable() 
>>> vars(a) 
{} 
>>> a.assign(p1=6) 
>>> vars(a) 
{'p1': 6} 
>>> a.p1 
6 

и help() текста все еще очень полезно!

>>> help(a.assign) 
Python Library Documentation: method assign in module __main__ 

assign(self, input=None, output=None, param=None, p1=None, p2=None) method of __main__.Assignable instance 
+0

Добавить параметры docstring '' '' 'со значением по умолчанию None: input, output, param, p1, p2' '' '' и '' help (assign) '' будут отображать две строки '' assign (self, * * kwargs) '' и '' '' 'со значением по умолчанию None: input, output, param, p1, p2' '' '' – eyquem

+0

Я внимательно изучил ваше предложение использовать namedtuple. Я не думаю, что это так интересно, что кажется на первый взгляд: ** 1) ** именованный экземпляр кортежа имеет атрибуты _fields, _make, _asdict, кроме тех, которые, по мнению кодера, являются единственными атрибутами для его желаемого экземпляра, они это лишняя информация, записанная в экземпляре. ** 2) ** Единственный полезный атрибут '' _replace'' имеет поведение, которое может быть недостатком: он возвращает объект с другим идентификатором. В некоторых случаях это может быть нежелательным. Этот момент покрывается тем, что вы говорите, вызывая, что названные кортежи неизменяемы. – eyquem

+0

** 3) ** Пункт 2 касался изменения значений существующих атрибутов. Это связано с добавлением новых атрибутов: новый атрибут не может быть добавлен после создания именованного кортежа, имя '' __dict__' именованного кортежа не может быть изменено. Это вызывает интерес названных кортежей: _ «Именованные экземпляры кортежей не имеют словарей для каждого экземпляра, поэтому они легки и не требуют больше памяти, чем обычные кортежи.» _, Но это делает их не очень подходящими для других задач, чем то, что они предназначены для. Мое мнение. – eyquem

0

С решениями mgilson и unutbu теряется возможность записи всех типов вызовов.

В моем следующем растворе, сохраняется такая возможность:

class Buu(object): 
    def assign(self, 
       input=None, output=None, 
       param=None, 
       p1=None, p2=None, 
       **kw): 
     for k,v in locals().iteritems(): 
      if v not in (self,None,kw): 
       if v == (None,): 
        setattr(self,k,None) 
       else: 
        setattr(self,k,v) 
     for k,v in kw.iteritems(): 
      setattr(self,k,v) 

buu = Buu() 

buu.assign(None,(None,), p1=[]) 
print "buu's attributes:",vars(buu) 
print 

buu.assign(1,p2 = 555, xx = 'extra') 
print "buu's attributes:",vars(buu) 

результат

buu's attributes: {'output': None, 'p1': []} 

buu's attributes: {'p2': 555, 'output': None, 'xx': 'extra', 'p1': [], 'input': 1} 

Кстати, когда кодер помещает None как аргумент по умолчанию для параметра, это потому, что он предусматривает, что не будет случая, когда необходимо будет передать None в качестве существенных аргументов, которые действительно повлияли бы во время извлечения.
Следовательно, я считаю, что точка передачи None в качестве реального значимого аргумента является ложной проблемой.
Однако в вышеприведенном коде эта проблема оборачивается с помощью соглашения, что в случае, если необходимо создать новый атрибут со значением None, аргумент должен быть (None,).

Если кто-то хочет пройти (None,) ???
Серьезно?
О, черт!

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