什么是做在Python自动属性分配的最佳方式,是不是一个好主意? [英] What is the best way to do automatic attribute assignment in Python, and is it a good idea?

查看:103
本文介绍了什么是做在Python自动属性分配的最佳方式,是不是一个好主意?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

而不是写code每次都是这样的,我定义一个类:

 类Foo(对象):
     高清__init __(自我,A,B,C,D,E,F,G):
        self.a =一
        self.b = B
        self.c = C
        self.d = D
        self.e = E
        self.f = F
        self.g = G

我可以使用这个食谱自动属性分配

 类Foo(对象):
     @autoassign
     高清__init __(自我,A,B,C,D,E,F,G):
        通过

两个问题


  1. 是否有与此相关的快捷方式或缺点陷阱?

  2. 有没有更好的方式来实现类似的方便?


解决方案

有关于autoassign code这个bug我一些东西(主要是风格,而是一个更严重的问题):


  1. autoassign 不分配
    ARGS属性:

     类Foo(对象):
        @autoassign
        高清__init __(自我,A,B,C =假,*参数):
            通过
    A =美孚('IBM','/ tmp目录,诚然,100,101)
    打印(a.args)
    #AttributeError的:'富'对象有没有属性'ARGS


  2. autoassign 就像一个装饰。
    autoassign(* argnames)调用
    函数返回一个装饰。
    为了实现这一法宝, autoassign
    需要测试它的第一类型
    论据。如果给我选择,我
    preFER功能不考
    类型它的参数。


  3. 似乎有相当
    code量致力于建立
    ,lambda表达式中lambda表达式,
    的IFilter和条件很多。

     如果kwargs:
        排除,F =集(kwargs ['排除']),无
        筛=拉姆达L:itertools.ifilter(拉姆达NV:NV [0]不排除升)
    ELIF LEN(地名)== 1和inspect.isfunction(名称[0]):
        F =名称[0]
        筛的λ= 1:1
    其他:
        名称,F =设置(名称),无
        筛=拉姆达L:itertools.ifilter(拉姆达NV:NV [0]名,升)

    我觉得可能是一个更简单的方法。 (看到
    如下图)。


  4. _为中
    itertools.starmap(assigned.setdefault,
    默认值):通过
    。我不认为
    地图星图,是为了呼吁
    功能,其唯一目的是自己
    副作用。它可能已被
    与世俗写更清楚:

     重点,在defaults.iteritems)值(:
        assigned.setdefault(键,值)


下面是具有相同的功能autoassign替代简单的实现(例如,可以执行包括和排除),并且解决了上述各点:

 进口检查
进口functools
高清autoargs(*包括,** kwargs):
    高清_autoargs(FUNC):
        ATTRS,可变参数,varkw,默认= inspect.getargspec(FUNC)
        高清筛(attr)使用:
            如果kwargs和ATTR在kwargs ['排除']:返回False
            如果不包括或ATTR中包括:返回True
            其他:返回False
        @ functools.wraps(FUNC)
        高清封装(个体经营,* ARGS,** kwargs):
            #手柄默认值
            为ATTR,VAL拉链(逆转(ATTRS),反转(默认)):
                如果筛(attr)使用:SETATTR(自我,ATTR,VAL)
            #拉手位置参数
            positional_attrs = ATTRS [1:]
            为ATTR,VAL拉链(positional_attrs,参数):
                如果筛(attr)使用:SETATTR(自我,ATTR,VAL)
            #拉手可变参数
            如果可变参数:
                remaining_args = ARGS [LEN(positional_attrs):]
                如果筛(可变参数):SETATTR(自我,可变参数,remaining_args)
            #拉手varkw
            如果kwargs:
                对ATTR,VAL在kwargs.iteritems():
                    如果筛(attr)使用:SETATTR(自我,ATTR,VAL)
            返回FUNC(个体经营,* ARGS,** kwargs)
        包装回报
    返回_autoargs

这里是我用来检查其行为的单元测试:

 导入单元测试
进口utils_method为UM类测试(unittest.TestCase的):
    高清test_autoargs(个体经营):
        A类(对象):
            @ um.autoargs()
            高清__init __(自我,富,路径,调试= FALSE):
                通过
        A = A('大黄','馅饼',调试=真)
        self.assertTrue(a.foo =='大黄')
        self.assertTrue(a.path =='馅饼')
        self.assertTrue(a.debug ==真)        B类(对象):
            @ um.autoargs()
            高清__init __(自我,富,路径,调试=假,*参数):
                通过
        A = B('大黄','馅饼',真的,100,101)
        self.assertTrue(a.foo =='大黄')
        self.assertTrue(a.path =='馅饼')
        self.assertTrue(a.debug ==真)
        self.assertTrue(a.args ==(100,101))        C类(对象):
            @ um.autoargs()
            高清__init __(自我,富,路径,调试=假,* ARGS,** KW):
                通过
        A = C('大黄','馅饼',真的,100,101,详细= TRUE)
        self.assertTrue(a.foo =='大黄')
        self.assertTrue(a.path =='馅饼')
        self.assertTrue(a.debug ==真)
        self.assertTrue(a.verbose ==真)
        self.assertTrue(a.args ==(100,101))    高清test_autoargs_names(个体经营):
        C类(对象):
            @ um.autoargs('巴','巴兹','详细')
            高清__init __(自我,富,酒吧,巴兹,详细= FALSE):
                通过
        一个C = C('大黄','馅饼',1)
        self.assertTrue(a.bar =='馅饼')
        self.assertTrue(a.baz == 1)
        self.assertTrue(a.verbose ==假)
        self.assertRaises(AttributeError的,GETATTR,一,'富')    高清test_autoargs_exclude(个体经营):
        C类(对象):
            @ um.autoargs(不含=('巴','巴兹','详细'))
            高清__init __(自我,富,酒吧,巴兹,详细= FALSE):
                通过
        一个C = C('大黄','馅饼',1)
        self.assertTrue(a.foo =='大黄')
        self.assertRaises(AttributeError的,GETATTR,一个酒吧)
如果__name__ =='__main__':
    unittest.main(ARGV = unittest.sys.argv + ['--verbose'])

PS。使用 autoassign autoargs 是IPython的code完成兼容。

Instead of writing code like this every time I define a class:

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

I could use this recipe for automatic attribute assignment.

class Foo(object):
     @autoassign
     def __init__(self, a, b, c, d, e, f, g):
        pass

Two questions:

  1. Are there drawbacks or pitfalls associated with this shortcut?
  2. Is there a better way to achieve similar convenience?

解决方案

There are some things about the autoassign code that bug me (mostly stylistic, but one more serious problem):

  1. autoassign does not assign an 'args' attribute:

    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 acts like a decorator. But autoassign(*argnames) calls a function which returns a decorator. To achieve this magic, autoassign needs to test the type of its first argument. If given a choice, I prefer functions not test the type of its arguments.

  3. There seems to be a considerable amount of code devoted to setting up sieve, lambdas within lambdas, ifilters, and lots of conditions.

    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)
    

    I think there might be a simpler way. (See below).

  4. for _ in itertools.starmap(assigned.setdefault, defaults): pass. I don't think map or starmap was meant to call functions, whose only purpose is their side effects. It could have been written more clearly with the mundane:

    for key,value in defaults.iteritems():
        assigned.setdefault(key,value)
    

Here is an alternative simpler implementation which has the same functionality as autoassign (e.g. can do includes and excludes), and which addresses the above points:

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

And here is the unit test I used to check its behavior:

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')


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

PS. Using autoassign or autoargs is compatible with IPython code completion.

这篇关于什么是做在Python自动属性分配的最佳方式,是不是一个好主意?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆