Если вы не инициализировали ссылочный тип и хотите установить или прочитать одно из его свойств, он будет генерировать исключение 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 не может произойти. Хотя вам нужно поддерживать оповещение при работе со ссылочными типами!
Только ссылочные типы, как следует из названия, могут содержать ссылки или буквально буквально ничто (или «нуль»). Если типы значений всегда содержат значение.
Типы ссылок (эти должны быть проверены):
Типы значений (вы можете просто игнорировать эти):
Есть некоторые вещи о том, что код автоассинга меня испугает (в основном стилистическая, но еще одна серьезная проблема):
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'
autoassign
действует как декоратор. Но autoassign(*argnames)
вызывает функцию, которая возвращает декоратор. Чтобы достичь этой магии, autoassign
должен проверить тип своего первого аргумента. Если у вас есть выбор, я предпочитаю, чтобы функции не проверяли тип своих аргументов. 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)
Я думаю, что может быть более простой способ. (См. Ниже). 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.
Это простая реализация 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'}
Из Python 3.7+ вы можете использовать Data Class , который достигает того, чего вы хотите, и многое другое.
Он позволяет вам определить поля для вашего класса, которые автоматически назначаются атрибутам.
Это выглядело бы так:
@dataclass
class Foo:
a: str
b: int
c: str
...
Метод __init__
будет автоматически создан в вашем классе, и он назначит аргументы создания экземпляра этим атрибутам ( и проверить аргументы).
Обратите внимание, что здесь требуется тип намека, поэтому я использовал int
и str
в примере. Если вы не знаете тип своего поля, вы можете использовать Any из модуля typing
.
Если у вас много переменных, вы можете передать один единственный файл конфигурации или объект.
Есть ли лучший способ добиться аналогичного удобства?
blockquote>Я не знаю, если это обязательно лучше, но вы можете это сделать:
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 Питера Норвига : Редко ответы на вопросы .
Один недостаток: многие IDE синтаксически анализируют __init__.py
, чтобы обнаружить атрибуты объекта. Если вы хотите, чтобы автоматическое завершение кода в вашей среде IDE было более функциональным, вам может быть лучше написано это старомодно.
Подобно вышеизложенному, хотя и не то же самое ... следующее очень короткое, имеет дело с 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())
class MyClass(object):
def __init__(self, **kwargs):
for key, value in kwargs.iteritems():
setattr(self, key, value)
Вы просто не можете использовать * args, но можете сохранить в каком-то списке экземпляров (например, self.args, не знаете)
В этот пакет вы можете найти
@autoargs
, вдохновленный answer-3653049 @autoprops
, чтобы преобразовать поля, сгенерированные @autoargs
в @property
, для использования в сочетании с библиотекой валидации, такой как принудительно или pycontracts , Обратите внимание, что это подтверждено для python 3.5 +