将参数分配到属性的Pythonic方法?

时间:2013-03-10 02:33:10

标签: python oop class methods

示例代码如下:

def assign(self, input=None, output=None, param=None, p1=None, p2=None):
    if input:
        self.input = input
    if output:
        self.output = output
    if param:
        self.param = param
    if p1:
        self.p1 = p1
    if p2:
        self.p2 = p2

虽然这看起来非常清楚,但如果此功能有10个参数,则会受到影响。有没有人对这种更方便的方法有想法?

4 个答案:

答案 0 :(得分:7)

你可以这样做:

def assign(self,**kwargs):
    for k,v in kwargs.items():
        if v:
           setattr(self,k,v)

这很简单,适合很多情况。如果您想维护一组您可以接受的关键字,并为其余关键字引发TypeError:

#python2.7 and newer
def assign(self,allowed_kwargs={'foo','bar','baz'},**kwargs):
    if kwargs.keysview() - allowed_kwargs:
        raise TypeError('useful message here...')
    for k in allowed_kwargs:
        setattr(self,k,kwargs[k])

这也是可以检查的,因为用户将看到允许的kwargs集。

答案 1 :(得分:1)

明确优于隐式

def assign(self, input=None, output=None, param=None, p1=None, p2=None):

有很多优点
def assign(self, **kwargs)
  • 自我记录
  • 如果传递了无效参数,则会引发有用的TypeError assign
  • 可以使用位置和关键字参数调用
  • assign

值得赞扬的是,OP发布的代码完全是明确的,尽管if - 语句是单调的。要减少单调, 你可以使用这样的东西:

class Foo(object):
    def assign(self, input=None, output=None, param=None, p1=None, p2=None):
        for name in 'input output param p1 p2'.split():
            val = vars()[name]
            if val is not None:
                setattr(self, name, val)

foo = Foo()
foo.assign(p1=123, p2='abc')

答案 2 :(得分:0)

python的一大优点是它的交互式解释器。当你编写这样的代码时:

>>> def assign(self, input=None, output=None, param=None, p1=None, p2=None):
...     pass
... 

很容易弄清楚你应该如何使用这个功能:

>>> help(assign)
Python Library Documentation: function assign in module __main__

assign(self, input=None, output=None, param=None, p1=None, p2=None)

相比之下:

>>> def assign2(self, **kwargs):
...     pass
... 

给出:

>>> help(assign2)
Python Library Documentation: function assign2 in module __main__

assign2(self, **kwargs)

我希望你理解为什么第一种形式是可取的。但是你仍然希望避免两次写入(在参数和正文中)。

第一个问题是为什么你在编写这种性质的代码?我发现这是一个非常常见的情况,我想要一个具有一堆属性的类,它将随身携带,但这些属性的集合基本上是固定的。在最常见的情况下,这些属性永远不会在对象的生命周期中发生变化;在这种情况下,python有一个内置的帮助器!

>>> import collections
>>> Assignment = collections.namedtuple('Assignment', 'input output param p1 p2')
>>> assign = Assignment(None, None, None, None, None)._replace
>>> assign(p1=10)
Assignment(input=None, output=None, param=None, p1=10, p2=None)
>>> help(Assignment)
Python Library Documentation: class Assignment in module __main__

class Assignment(__builtin__.tuple)
 |  Assignment(input, output, param, p1, p2)
 |  
... SNIP

namedtuple是常规类,您可以从它们继承它们以给它们特殊的行为。不幸的是,它们是不可改变的,如果你碰巧需要它,你将需要另一种技术;但你应该首先达到命名元组。否则,我们可以利用其他一些魔法;我们可以得到所有的局部变量,它们在函数开始时只包含参数:

>>> class Assignable(object):
...     def assign(self, input=None, output=None, param=None, p1=None, p2=None):
...         _kwargs = vars()
...         _kwargs.pop('self')
...         vars(self).update((attr, value) for attr, value in _kwargs.items() if value is not None)
... 
>>> a = Assignable()
>>> vars(a)
{}
>>> a.assign(p1=6)
>>> vars(a)
{'p1': 6}
>>> a.p1
6

并且help()文字仍然非常有用!

>>> help(a.assign)
Python Library Documentation: method assign in module __main__

assign(self, input=None, output=None, param=None, p1=None, p2=None) method of __main__.Assignable instance

答案 3 :(得分:0)

使用mgilson和unutbu的解决方案,写入所有类型的呼叫的可能性都将丢失。

在我的以下解决方案中,这种可能性得以保留:

class Buu(object):
    def assign(self,
               input=None, output=None,
               param=None,
               p1=None, p2=None,
               **kw):
        for k,v in locals().iteritems():
            if v not in (self,None,kw):
                if v == (None,):
                    setattr(self,k,None)
                else:
                    setattr(self,k,v)
        for k,v in kw.iteritems():
            setattr(self,k,v)

buu = Buu()

buu.assign(None,(None,), p1=[])
print "buu's attributes:",vars(buu)
print

buu.assign(1,p2 = 555, xx = 'extra')
print "buu's attributes:",vars(buu)

结果

buu's attributes: {'output': None, 'p1': []}

buu's attributes: {'p2': 555, 'output': None, 'xx': 'extra', 'p1': [], 'input': 1}

顺便说一句,当编码器将None作为参数的默认参数时,这是因为他预见到不会有必要将None作为重要参数传递给在执行期间真的会有影响力 因此,我认为将None作为真正重要的论点传递是一个错误的问题 但是,在上面的代码中,通过使用约定来避免此问题,以防必须创建值为None的新属性,参数应为(None,)

如果有人想通过(None,) ??? 说真的?
哎呀!