Pergunta

Parece que os métodos muitas vezes __init__ são semelhantes a este:

def __init__(self, ivar1, ivar2, ivar3):
    self.ivar1 = ivar1
    self.ivar2 = ivar2
    self.ivar3 = ivar3

Existe alguma maneira de transformar os argumentos em uma lista (sem recorrer a *args ou **kwargs) e, em seguida, usando setattr para definir as variáveis ??de instância, com o nome do parâmetro e o argumento passado? E talvez cortar a lista, por exemplo, você precisaria de pelo menos fatia para [1:] porque você não quer self.self.

(na verdade, eu acho que teria de ser um dicionário para conter o nome e valor)

como este:

def __init__(self, ivar1, ivar2, ivar3, optional=False):
    for k, v in makedict(self.__class__.__init__.__args__): # made up __args__
        setattr(self, k, v)

Obrigado!

Respondendo a resposta do desconhecido, Encontrei este trabalho:

Class A(object):
    def __init__(self, length, width, x):
        self.__dict__.update(dict([(k, v) for k, v in locals().iteritems() if k != 'self']))

ou

Class A(object):
    def __init__(self, length, width, x):
        self.__dict__.update(locals())
        del self.__dict__['self']

Não muito ruim ..

Foi útil?

Solução

Aqui está. Sim, este é um corte mal feio. Sim, o objeto precisa uma variável __dict__. Mas hey, um forro pequeno puro one!

def __init__(self):
    self.__dict__.update(locals())

O construtor pode tomar qualquer tipo de argumentos.

class test(object):
    def __init__(self, a, b, foo, bar=5)...

a = test(1,2,3)
dir(a)

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'foo', 'bar', 'self']

Ele também irá incluir auto, mas você pode facilmente apagar isso ou fazer a sua própria função de atualização que ignora self.

Outras dicas

Você pode usar inspect.getargspec e encapsular-lo como um decorador. A pesquisa de argumentos opcionais e palavra-chave é um pouco complicado, mas isso deve fazê-lo:

def inits_args(func):
    """Initializes object attributes by the initializer signature"""
    argspec = inspect.getargspec(func)
    argnames = argspec.args[1:]
    defaults = dict(zip(argnames[-len(argspec.defaults):], argspec.defaults))
    @functools.wraps(func)
    def __init__(self, *args, **kwargs):
        args_it = iter(args)
        for key in argnames:
            if key in kwargs:
                value = kwargs[key]
            else:
                try:
                    value = args_it.next()
                except StopIteration:
                    value = defaults[key]
            setattr(self, key, value)
        func(self, *args, **kwargs)
    return __init__

Você pode então usá-lo como este:

class Foo(object):
    @inits_args
    def __init__(self, spam, eggs=4, ham=5):
        print "Foo(%r, %r, %r)" % (self.spam, self.eggs, self.ham)

Não há nenhuma boa maneira de obter os argumentos como uma lista se eles são especificados individualmente na assinatura da função. Provavelmente, você pode fazer alguma coisa com inspecionar ou quadro hacks, mas que será mais feio do que simplesmente ortografia-lo como você fez.

Tente inspect.getargspec :

In [31]: inspect.getargspec(C.__init__)

Out[31]: ArgSpec(args=['self', 'ivar1', 'ivar2', 'ivar3', 'optional'],

                 varargs=None, keywords=None, defaults=(False,))

Veja se o novo namedtuple (novo em Python 2.6) a partir das coleções de módulos de trabalho podem para você.

Você pode fazer isso usando a introspecção dos argumentos, mas o código vai ser mais longo do que o código tentar substituir. Especialmente se você estiver lidando com kw, que você pode ter que fazer.

Este código curto funciona na maioria dos casos (melhorou de desconhecidos exemplo):

>>> class Foo:
...   def __init__(self, labamba, **kw):
...       params = locals().copy()
...       del params['self']
...       if 'kw' in params:
...           params.update(params['kw'])
...           del params['kw']
...       self.__dict__.update(params)

Mas é uma gambiarra, tornando o código menos legível por nenhuma razão em particular, exceto a preguiça, por isso não fazê-lo. E também, quantas vezes você realmente tem classes que têm parâmetros mais de 5-6 inicialização?

Eu gosto que forma a mais, não muito longo e ambos copy-pasteable e sub-classable:

class DynamicInitClass(object):
      __init_defargs=('x',)
      def __init__(self,*args,**attrs):
        for idx,val in enumerate(args): attrs[self.__init_defargs[idx]]=val
        for key,val in attrs.iteritems(): setattr(self,key,val)

Como sobre a derivação de uma classe especial? Eu acho que é mais explícita e mais flexível desta forma:

class InitMe:
    def __init__(self, data):
        if 'self' in data:
             data = data.copy()
             del data['self']
        self.__dict__.update(data)


class MyClassA(InitMe):
    def __init__(self, ivar1, ivar2, ivar3 = 'default value'):
        super().__init__(locals())


class MyClassB(InitMe):
    def __init__(self, foo):
        super().__init__({'xxx': foo, 'yyy': foo, 'zzz': None})
# or    super().__init__(dict(xxx=foo, yyy=foo, zzz=None))

class MyClassC(InitMe):
    def __init__(self, foo, **keywords):
        super().__init__(keywords)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top