Python: Итерация через аргументы конструктора

Я часто нахожу, что создаю конструкторы классов следующим образом:

class foo:
    def __init__(self, arg1, arg2, arg3):
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3

Это может стать болью, если число аргументов (и атрибутов класса) становится высоким. Я ищу самый pythonic способ прокрутить список аргументов конструктора и соответственно присвоить атрибуты. Я работаю с Python 2.7, поэтому в идеале я ищу помощь с этой версией.

Ответ 1

Самый питонический путь - это то, что вы уже написали. Если вы с удовольствием требуете именованные аргументы, вы можете сделать это:

class foo:
    def __init__(self, **kwargs):
        vars(self).update(kwargs)

Ответ 2

Предоставленные ответы полагаются на аргументы *vargs и **kargs, что может быть совсем не удобно, если вы хотите ограничить определенный набор аргументов конкретными именами: вам нужно будет все проверить вручную.

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

import inspect
import functools

def store_args(method):
    """Stores provided method args as instance attributes."""
    argspec = inspect.getargspec(method)
    defaults = dict(zip( argspec.args[-len(argspec.defaults):], argspec.defaults ))
    arg_names = argspec.args[1:]
    @functools.wraps(method)
    def wrapper(*positional_args, **keyword_args):
        self = positional_args[0]
        # Get default arg values
        args = defaults.copy()
        # Add provided arg values
        list(map( args.update, ( zip(arg_names, positional_args[1:]), keyword_args.items() ) ))
        # Store values in instance as attributes
        self.__dict__.update(args)
        return method(*positional_args, **keyword_args)

    return wrapper

Затем вы можете использовать его следующим образом:

class A:
    @store_args
    def __init__(self, a, b, c=3, d=4, e=5):
        pass

a = A(1,2)
print(a.a, a.b, a.c, a.d, a.e)

Результат будет 1 2 3 4 5 на Python3.x или (1, 2, 3, 4, 5) на Python2.x

Ответ 3

class foo:
    def __init__(self, **kwargs):
        for arg_name, arg_value in kwargs.items():
            setattr(self, arg_name, arg_value)

Для этого требуются имена аргументов:

obj = foo(arg1 = 1, arg2 = 2)

Ответ 4

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

class Foo(object):
    def __init__(self, *args, **kwargs):
        for arg in args:
            print arg
        for kwarg in kwargs:
            print kwarg

* помещает позиционные аргументы в кортеж и ** аргументы ключевого слова в словарь:

foo = Foo(1, 2, 3, a=4, b=5, c=6) // args = (1, 2, 3), kwargs = {'a' : 4, ...}

Ответ 5

Как насчет этого?

class foo:
    def __init__(self, arg1, arg2, arg3):
        for _prop in dir():
            setattr(self, _prop, locals()[_prop])

Это использует встроенную функцию dir для python для итерации по всем локальным переменным. Он имеет незначительный побочный эффект создания посторонней самооценки, но вы можете отфильтровать его, если хотите. Кроме того, если вы должны были объявить какие-либо другие локали перед вызовом dir(), они также будут добавлены как созданные атрибуты объекта.

Ответ 6

Как отмечали другие, в большинстве случаев вы должны, вероятно, придерживаться своего оригинального метода "pythonic".

Однако, если вы действительно хотите пройти целых девять ярдов, вот какой-то код, который аккуратно обрабатывает args, ключевое слово args, если это необходимо, и избегает повторения шаблонов:

def convert_all_args_to_attribs(self, class_locals):
    class_locals.pop('self')
    if 'kwargs' in class_locals:
        vars(self).update(class_locals.pop('kwargs'))
    vars(self).update(class_locals)

class FooCls:
    def __init__(self, foo, bar):
        convert_all_args_to_attribs(self, locals())

class FooClsWithKeywords:
    def __init__(self, foo, bar, **kwargs):
        convert_all_args_to_attribs(self, locals())

f1 = FooCls(1,2)
f2 = FooClsWithKeywords(3,4, cheese='stilton')


print vars(f1) #{'foo': 1, 'bar': 2}
print vars(f2) #{'cheese': 'stilton', 'foo': 3, 'bar': 4}

Ответ 7

Как насчет повторения имен явных переменных?

т.е.

class foo:
    def __init__(self, arg1, arg2, arg3):
        for arg_name in 'arg1,arg2,arg3'.split(','):
            setattr(self, arg_name, locals()[arg_name])

f = foo(5,'six', 7)

Результат с помощью

print vars(f)

{'arg1': 5, 'arg2': 'six', 'arg3': 7}

Мое предложение похоже на @peepsalot, за исключением его более явного и не использует dir(), который согласно документации

его подробное поведение может меняться через выпуски

Ответ 8

* args - последовательность, поэтому вы можете обращаться к элементам с помощью индексации:

def __init__(self, *args):
        if args:       
            self.arg1 = args[0]    
            self.arg2 = args[1]    
            self.arg3 = args[2]    
            ...  

или вы можете просмотреть все из них

for arg in args:
   #do assignments