在Python中执行自动属性分配的最佳方法是什么,这是个好主意吗?
每次定义一个类时,无需像这样编写代码:
class Foo(object):
def __init__(self, a, b, c, d, e, f, g):
self.a = a
self.b = b
self.c = c
self.d = d
self.e = e
self.f = f
self.g = g
我可以使用此配方进行自动属性分配。
class Foo(object):
@autoassign
def __init__(self, a, b, c, d, e, f, g):
pass
两个问题 :
- 此快捷方式是否有缺点或陷阱?
- 有没有更好的方法来实现类似的便利?
-
关于自动分配代码的某些问题会困扰我(主要是样式问题,但还有一个更严重的问题):
-
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
,lambda中的lambda,ifilter和许多条件。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)
这是一个替代性的更简单的实现,它具有与自动分配相同的功能(例如可以进行包含和排除),并且可以解决上述问题:
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代码完成兼容。 -