Легкий способ назначения всех параметров атрибутам объекта? [Дубликат]

Если вы не инициализировали ссылочный тип и хотите установить или прочитать одно из его свойств, он будет генерировать исключение NullReferenceException.

Пример:

Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.

Вы можно просто избежать этого, проверив, является ли переменная не нулевой:

Person p = null;
if (p!=null)
{
    p.Name = "Harry"; // Not going to run to this point
}

Чтобы полностью понять, почему выбрано исключение NullReferenceException, важно знать разницу между типами значений и ссылочные типы .

Итак, если вы имеете дело со типами значений, NullReferenceExceptions не может произойти. Хотя вам нужно поддерживать оповещение при работе со ссылочными типами!

Только ссылочные типы, как следует из названия, могут содержать ссылки или буквально буквально ничто (или «нуль»). Если типы значений всегда содержат значение.

Типы ссылок (эти должны быть проверены):

  • динамический
  • объект
  • string

Типы значений (вы можете просто игнорировать эти):

  • Числовые типы
  • Интегральные типы
  • Типы с плавающей запятой
  • decimal
  • bool
  • Пользовательские структуры

27
задан FMc 6 September 2010 в 17:13
поделиться

9 ответов

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

  1. autoassign не присваивает атрибут «args»:
    class Foo(object):
        @autoassign
        def __init__(self,a,b,c=False,*args):
            pass
    a=Foo('IBM','/tmp',True, 100, 101)
    print(a.args)
    # AttributeError: 'Foo' object has no attribute 'args'
    
  2. autoassign действует как декоратор. Но autoassign(*argnames) вызывает функцию, которая возвращает декоратор. Чтобы достичь этой магии, autoassign должен проверить тип своего первого аргумента. Если у вас есть выбор, я предпочитаю, чтобы функции не проверяли тип своих аргументов.
  3. Кажется, что существует значительный объем кода, посвященного настройке sieve, lambdas внутри lambdas, ifilters и множеству условий ,
    if kwargs:
        exclude, f = set(kwargs['exclude']), None
        sieve = lambda l:itertools.ifilter(lambda nv: nv[0] not in exclude, l)
    elif len(names) == 1 and inspect.isfunction(names[0]):
        f = names[0]
        sieve = lambda l:l
    else:
        names, f = set(names), None
        sieve = lambda l: itertools.ifilter(lambda nv: nv[0] in names, l)
    
    Я думаю, что может быть более простой способ. (См. Ниже).
  4. for _ in itertools.starmap(assigned.setdefault, defaults): pass. Я не думаю, что map или starmap предназначались для вызова функций, единственной целью которых являются их побочные эффекты. Это могло быть написано более четко с помощью мирского:
    for key,value in defaults.iteritems():
        assigned.setdefault(key,value)
    

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

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
            if defaults:
                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.items():
                    if sieve(attr):
                        setattr(self, attr, val)
            return func(self, *args, **kwargs)
        return wrapper
    return _autoargs

И вот блок-тест, который я использовал для проверки его поведения:

import sys
import unittest
import utils_method as um

class Test(unittest.TestCase):
    def test_autoargs(self):
        class A(object):
            @um.autoargs()
            def __init__(self,foo,path,debug=False):
                pass
        a=A('rhubarb','pie',debug=True)
        self.assertTrue(a.foo=='rhubarb')
        self.assertTrue(a.path=='pie')
        self.assertTrue(a.debug==True)

        class B(object):
            @um.autoargs()
            def __init__(self,foo,path,debug=False,*args):
                pass
        a=B('rhubarb','pie',True, 100, 101)
        self.assertTrue(a.foo=='rhubarb')
        self.assertTrue(a.path=='pie')
        self.assertTrue(a.debug==True)
        self.assertTrue(a.args==(100,101))        

        class C(object):
            @um.autoargs()
            def __init__(self,foo,path,debug=False,*args,**kw):
                pass
        a=C('rhubarb','pie',True, 100, 101,verbose=True)
        self.assertTrue(a.foo=='rhubarb')
        self.assertTrue(a.path=='pie')
        self.assertTrue(a.debug==True)
        self.assertTrue(a.verbose==True)        
        self.assertTrue(a.args==(100,101))        

    def test_autoargs_names(self):
        class C(object):
            @um.autoargs('bar','baz','verbose')
            def __init__(self,foo,bar,baz,verbose=False):
                pass
        a=C('rhubarb','pie',1)
        self.assertTrue(a.bar=='pie')
        self.assertTrue(a.baz==1)
        self.assertTrue(a.verbose==False)
        self.assertRaises(AttributeError,getattr,a,'foo')

    def test_autoargs_exclude(self):
        class C(object):
            @um.autoargs(exclude=('bar','baz','verbose'))
            def __init__(self,foo,bar,baz,verbose=False):
                pass
        a=C('rhubarb','pie',1)
        self.assertTrue(a.foo=='rhubarb')
        self.assertRaises(AttributeError,getattr,a,'bar')

    def test_defaults_none(self):
        class A(object):
            @um.autoargs()
            def __init__(self,foo,path,debug):
                pass
        a=A('rhubarb','pie',debug=True)
        self.assertTrue(a.foo=='rhubarb')
        self.assertTrue(a.path=='pie')
        self.assertTrue(a.debug==True)


if __name__ == '__main__':
    unittest.main(argv = sys.argv + ['--verbose'])

PS. Использование autoassign или autoargs совместимо с завершением кода IPython.

24
ответ дан unutbu 26 August 2018 в 09:42
поделиться

Это простая реализация judy2k :

from inspect import signature

def auto_args(f):
    sig = signature(f)  # Get a signature object for the target:
    def replacement(self, *args, **kwargs):
        # Parse the provided arguments using the target's signature:
        bound_args = sig.bind(self, *args, **kwargs)
        # Save away the arguments on `self`:
        for k, v in bound_args.arguments.items():
            if k != 'self':
                setattr(self, k, v)
        # Call the actual constructor for anything else:
        f(self, *args, **kwargs)
    return replacement


class MyClass:
    @auto_args
    def __init__(self, a, b, c=None):
        pass

m = MyClass('A', 'B', 'C')
print(m.__dict__)
# {'a': 'A', 'b': 'B', 'c': 'C'}
2
ответ дан Apalala 26 August 2018 в 09:42
поделиться

Из Python 3.7+ вы можете использовать Data Class , который достигает того, чего вы хотите, и многое другое.

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

Это выглядело бы так:

@dataclass
class Foo:
    a: str
    b: int
    c: str
    ...

Метод __init__ будет автоматически создан в вашем классе, и он назначит аргументы создания экземпляра этим атрибутам ( и проверить аргументы).

Обратите внимание, что здесь требуется тип намека, поэтому я использовал int и str в примере. Если вы не знаете тип своего поля, вы можете использовать Any из модуля typing .

5
ответ дан Jundiaius 26 August 2018 в 09:42
поделиться

Если у вас много переменных, вы можете передать один единственный файл конфигурации или объект.

2
ответ дан leoluk 26 August 2018 в 09:42
поделиться

Есть ли лучший способ добиться аналогичного удобства?

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

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


>>> foo = Foo(a = 1, b = 'bar', c = [1, 2])
>>> foo.a
1
>>> foo.b
'bar'
>>> foo.c
[1, 2]
>>> 

Предоставлено Python Питера Норвига : Редко ответы на вопросы .

7
ответ дан Manoj Govindan 26 August 2018 в 09:42
поделиться

Один недостаток: многие IDE синтаксически анализируют __init__.py, чтобы обнаружить атрибуты объекта. Если вы хотите, чтобы автоматическое завершение кода в вашей среде IDE было более функциональным, вам может быть лучше написано это старомодно.

6
ответ дан Max 26 August 2018 в 09:42
поделиться

Подобно вышеизложенному, хотя и не то же самое ... следующее очень короткое, имеет дело с args и kwargs:

def autoassign(lcls):
    for key in lcls.keys():
        if key!="self":
            lcls["self"].__dict__[key]=lcls[key]

Используйте следующее:

class Test(object):
    def __init__(self, a, b, *args, **kwargs):
        autoassign(locals())
2
ответ дан onno 26 August 2018 в 09:42
поделиться
class MyClass(object):
    def __init__(self, **kwargs):
        for key, value in kwargs.iteritems():
            setattr(self, key, value)

Вы просто не можете использовать * args, но можете сохранить в каком-то списке экземпляров (например, self.args, не знаете)

0
ответ дан Rafael Sierra 26 August 2018 в 09:42
поделиться

В этот пакет вы можете найти

Обратите внимание, что это подтверждено для python 3.5 +

2
ответ дан smarie 26 August 2018 в 09:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: