Python: Как легко скопировать параметры функции в поля объекта? - PullRequest
4 голосов
/ 26 мая 2010

Часто у меня есть функции-члены, которые копируют параметры в поля объекта. Например:

class NouveauRiches(object):
  def __init__(self, car, mansion, jet, bling):
    self.car = car
    self.mansion = mansion
    self.jet = jet
    self.bling = bling

Существует ли языковая конструкция Python, которая сделает приведенный выше код менее утомительным? Можно использовать * args:

def __init__(self, *args):
  self.car, self.mansion, self.jet, self.bling = args

+: менее утомительно

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

-: не вызывает TypeError при вызове с неправильным количеством параметров (но вызывает ValueError)

Есть еще идеи? (Независимо от вашего предложения, убедитесь, что код, вызывающий функцию, остается простым)

Ответы [ 7 ]

2 голосов
/ 26 мая 2010

Вы можете сделать это с помощью вспомогательного метода, примерно так:

import inspect

def setargs(func):
    f = inspect.currentframe(1)
    argspec = inspect.getargspec(func)
    for arg in argspec.args:
        setattr(f.f_locals["self"], arg, f.f_locals[arg])

Использование:

class Foo(object):

    def __init__(self, bar, baz=4711):
        setargs(self.__init__)

        print self.bar # Now defined
        print self.baz # Now defined

Это не красиво, и, вероятно, его следует использовать только при создании прототипа. Пожалуйста, используйте явное назначение, если вы планируете, чтобы другие читали его.

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

2 голосов
/ 26 мая 2010

Я бы пошел на это, также вы могли бы переопределить уже определенные свойства.

class D:
  def __init__(self, **kwargs):
    self.__dict__.update(kwargs)

Но лично я бы просто пошел долгий путь.Подумайте о тех:

- Explicit is better than implicit.
- Flat is better than nested.
(The Zen of Python)
1 голос
/ 26 мая 2010

Я не уверен, что это такая хорошая идея, но она может быть реализована:

import inspect
class NouveauRiches(object):
    def __init__(self, car, mansion, jet, bling):
        arguments = inspect.getargvalues(frame)[0]
        values = inspect.getargvalues(frame)[3];
        for name in arguments:
            self.__dict__[name] = values[name]

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

1 голос
/ 26 мая 2010

Попробуйте что-то вроде

d = dict(locals())
del d['self']
self.__dict__.update(d)

Конечно, он возвращает все локальные переменные, а не только аргументы функции.

0 голосов
/ 31 мая 2016

Я фанат следующих

import inspect

def args_to_attrs(otherself):
    frame = inspect.currentframe(1)
    argvalues = inspect.getargvalues(frame)

    for arg in argvalues.args:
        if arg == 'self':
            continue
        value = argvalues.locals[arg]
        setattr(otherself, arg, value)

class MyClass:
    def __init__(self, arga="baf", argb="lek", argc=None):
        args_to_attrs(self)

Аргументы для __init__ имеют явные имена, поэтому ясно, какие атрибуты устанавливаются. Кроме того, он немного упрощен в отношении принятого в настоящее время ответа.

0 голосов
/ 27 мая 2010

Я иногда делаю это для классов, которые действуют «как куча», то есть они имеют кучу настраиваемых атрибутов:

class SuperClass(object):
    def __init__(self, **kw):
        for name, value in kw.iteritems():
            if not hasattr(self, name):
                raise TypeError('Unexpected argument: %s' % name)
            setattr(self, name, value)

class SubClass(SuperClass):
    instance_var = None # default value

class SubClass2(SubClass):
    other_instance_var = True

    @property
    def something_dynamic(self):
        return self._internal_var

    @something_dynamic.setter # new Python 2.6 feature of properties
    def something_dynamic(self, value):
        assert value is None or isinstance(value, str)
        self._internal_var = value

Тогда вы можете позвонить SubClass2(instance_var=[], other_instance_var=False), и он будет работать без определения __init__ ни в одном из них. Вы также можете использовать любую собственность. Хотя это позволяет вам перезаписывать методы, которые вы, вероятно, не захотите (так как они возвращают True для hasattr(), как переменная экземпляра).

Если вы добавите property или другой дескриптор, он будет работать нормально. Вы можете использовать это для проверки типов; в отличие от проверки типов в __init__, она будет применяться каждый раз, когда значение обновляется. Обратите внимание, что вы не можете использовать любые позиционные аргументы для них, если вы не переопределите __init__, поэтому иногда то, что будет естественным позиционным аргументом, не будет работать. formencode.declarative охватывает эту и другие проблемы, возможно, с большой тщательностью, я бы не советовал вам пытаться (оглядываясь назад, я не думаю, что это того стоит).

Обратите внимание, что любой рецепт, который использует self.__dict__, не будет учитывать свойства и дескрипторы, и если вы используете их вместе, вы получите странные и неожиданные результаты. Я только рекомендую использовать setattr() для установки атрибутов, никогда self.__dict__.

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

0 голосов
/ 26 мая 2010

Вы можете попробовать что-то вроде этого:

class C(object):
    def __init__(self, **kwargs):
        for k in kwargs:
            d = {k: kwargs[k]}
            self.__dict__.update(d)

Или используя setattr, вы можете сделать:

class D(object):
    def __init__(self, **kwargs):
        for k in kwargs:
            setattr(self, k, kwargs[k])

И то, и другое можно назвать так:

myclass = C(test=1, test2=2)

Таким образом, вы должны использовать ** kwargs, а не * args.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...