FMc*_*FMc 31 python attributes decorator
而不是每次我定义一个类时都编写这样的代码:
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
Run Code Online (Sandbox Code Playgroud)
我可以使用此配方进行自动属性分配.
class Foo(object):
@autoassign
def __init__(self, a, b, c, d, e, f, g):
pass
Run Code Online (Sandbox Code Playgroud)
两个问题:
unu*_*tbu 26
关于自动分配代码的一些事情让我感到困扰(主要是风格,但更严重的问题):
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'
Run Code Online (Sandbox Code Playgroud)autoassign像装饰家一样.但是autoassign(*argnames)调用一个返回装饰器的函数.要实现这个魔力,autoassign
需要测试其第一个参数的类型.如果给出了选择,我更喜欢函数不测试其参数的类型.
似乎有相当多的代码专门用于设置
sievelambda,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)
Run Code Online (Sandbox Code Playgroud)
我认为可能有一种更简单的方法.(见下文).
for _ in
itertools.starmap(assigned.setdefault,
defaults): pass.我不认为
map或starmap意图调用函数,其唯一目的是它们的副作用.它可以用世俗的写得更清楚:
for key,value in defaults.iteritems():
assigned.setdefault(key,value)
Run Code Online (Sandbox Code Playgroud)这是一个替代的更简单的实现,它具有与自动分配相同的功能(例如,可以包含和排除),并解决上述问题:
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
Run Code Online (Sandbox Code Playgroud)
这是我用来检查其行为的单元测试:
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'])
Run Code Online (Sandbox Code Playgroud)
PS.使用autoassign或autoargs兼容IPython代码完成.
Jun*_*ius 10
从Python 3.7+开始,您可以使用数据类,它可以实现您想要的更多功能.
它允许您为类定义字段,这些字段是自动分配的属性.
看起来像这样:
@dataclass
class Foo:
a: str
b: int
c: str
...
Run Code Online (Sandbox Code Playgroud)
该__init__方法将在您的类中自动创建,并将实例创建的参数分配给这些属性(并验证参数).
注意,这里类型提示是必需的,这就是为什么我已经使用int并str在本例中.如果您不知道字段的类型,可以使用模块中的Anytyping.
有没有更好的方法来实现类似的便利?
我不知道它是否一定更好,但你可以这样做:
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]
>>>
Run Code Online (Sandbox Code Playgroud)
礼貌Peter Norvig的Python:不经常回答的问题.