2010-10-07 1 views
19

Сай, у меня есть следующий класс в PythonАвтоматическая настройка переменных-членов класса в Python

class Foo(object): 
    a = None 
    b = None 
    c = None 
    def __init__(self, a = None, b = None, c = None): 
     self.a = a 
     self.b = b 
     self.c = c 

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

+2

Я также обнаружил, что подклассификация namedtuple - http://docs.python.org/dev/library/collections.html#collections.namedtuple - неплохая идея. –

ответ

25

Пожалуйста, обратите внимание, что

class Foo(object): 
    a = None 

задает пару ключ-значение в Словаре Foo «s:

Foo.__dict__['a']=None 

в то время как

def __init__(self, a = None, b = None, c = None): 
    self.a = a 

задает пару ключ-значение в Foo экземпляр объекта объекта:

foo=Foo() 
foo.__dict__['a']=a 

Поэтому установка членов класса в верхней части вашего определения напрямую не связана с настройкой атрибутов экземпляра в нижней половине вашего определения (внутри __init__.

Также хорошо знать, что __init__ является инициализатором Python. __new__ - класс конструктор.


Если вы ищете способ, чтобы автоматически добавить некоторые атрибуты экземпляра на основе аргументов __init__ «s, вы могли бы использовать это:

import inspect 
import functools 

def autoargs(*include,**kwargs): 
    def _autoargs(func): 
     attrs,varargs,varkw,defaults=inspect.getargspec(func) 
     def sieve(attr): 
      if kwargs and attr in kwargs['exclude']: return False 
      if not include or attr in include: return True 
      else: return False    
     @functools.wraps(func) 
     def wrapper(self,*args,**kwargs): 
      # handle default values 
      for attr,val in zip(reversed(attrs),reversed(defaults)): 
       if sieve(attr): setattr(self, attr, val) 
      # handle positional arguments 
      positional_attrs=attrs[1:]    
      for attr,val in zip(positional_attrs,args): 
       if sieve(attr): setattr(self, attr, val) 
      # handle varargs 
      if varargs: 
       remaining_args=args[len(positional_attrs):] 
       if sieve(varargs): setattr(self, varargs, remaining_args)     
      # handle varkw 
      if kwargs: 
       for attr,val in kwargs.iteritems(): 
        if sieve(attr): setattr(self,attr,val)    
      return func(self,*args,**kwargs) 
     return wrapper 
    return _autoargs 

Так что, когда вы говорите

class Foo(object): 
    @autoargs() 
    def __init__(self,x,path,debug=False,*args,**kw): 
     pass 
foo=Foo('bar','/tmp',True, 100, 101,verbose=True) 

вы автоматически получаете эти атрибуты экземпляра:

print(foo.x) 
# bar 
print(foo.path) 
# /tmp 
print(foo.debug) 
# True 
print(foo.args) 
# (100, 101) 
print(foo.verbose) 
# True 

PS. Хотя я написал это (для удовольствия), я не рекомендую использовать autoargs для серьезной работы. Быть явным просто, ясно и непогрешимо. Я не могу сказать то же самое для autoargs.

PPS. Это только я, или много кнопок, разбитых на Stackoverflow? Окно редактора потерял все свои иконки ... :( Очистка кэш браузера исправили проблему.

+0

Я забыл добавить - спасибо за очистку моих терминов на языке python. –

+0

@Jeeyoung: проблем нет; Рад, что смог помочь. – unutbu

+0

Разве это не должно быть в стандартной библиотеке? Я не вижу смысла устанавливать на каждом __init __... –

10

Существует elegant способов для этого.

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

Существует также неочищенный путь. Он будет работать, но НЕ рекомендуется. Смотрите и решайте.

>>> class Foo(object): 
    def __init__(self, **attrs): 
     self.__dict__.update(**attrs) 
    def __getattr__(self, attr): 
     return self.__dict__.get(attr, None) 


>>> f = Foo(a = 1, b = 2, c = 3) 
>>> f.a, f.b 
(1, 2) 
>>> f = Foo(bar = 'baz') 
>>> f.bar 
'baz' 
>>> f.a 
>>> 

Конструктор аргументов ключевого слова позволяет уйти без явного определения каких-либо аргументов. Предупреждение: это противоречит принципу «явный лучше, чем неявный».

Вы должны переопределить __getattr__ ТОЛЬКО, если вы хотите вернуть значение по умолчанию для атрибута, который отсутствует, вместо получения AttributeError.

+0

отлично! самый элегантный способ сделать это. – karantan