Python: перебор аргументов конструктора - PullRequest
12 голосов
/ 20 июля 2011

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

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

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

Ответы [ 8 ]

21 голосов
/ 20 июля 2011

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

class foo:
    def __init__(self, **kwargs):
        vars(self).update(kwargs)
8 голосов
/ 26 апреля 2012

Предоставленные ответы основаны на аргументах *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

1 голос
/ 21 мая 2013

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

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

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

1 голос
/ 20 июля 2011

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

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, ...}
1 голос
/ 20 июля 2011
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)
0 голосов
/ 19 февраля 2017

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

* 1003 Т.е. *

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(), который согласно документации

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

0 голосов
/ 10 октября 2012

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

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

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}
0 голосов
/ 20 июля 2011

* 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
...